Distinguish expiry/best before dates (closes #851)

This commit is contained in:
Bernd Bestel
2020-11-15 19:53:44 +01:00
parent 1d50d5dd22
commit b393998601
39 changed files with 348 additions and 192 deletions

View File

@@ -19,7 +19,7 @@
@include('components.numberpicker', array(
'id' => 'batteries_due_soon_days',
'additionalAttributes' => 'data-setting-key="batteries_due_soon_days"',
'label' => 'Batteries due to be charged soon days',
'label' => 'Due soon days',
'min' => 1,
'invalidFeedback' => $__t('This cannot be lower than %s', '1'),
'additionalCssClasses' => 'user-setting-control'

View File

@@ -19,7 +19,7 @@
@include('components.numberpicker', array(
'id' => 'chores_due_soon_days',
'additionalAttributes' => 'data-setting-key="chores_due_soon_days"',
'label' => 'Chores due soon days',
'label' => 'Due soon days',
'min' => 1,
'invalidFeedback' => $__t('This cannot be lower than %s', '1'),
'additionalCssClasses' => 'user-setting-control'

View File

@@ -118,7 +118,7 @@
for="use_specific_stock_entry">{{ $__t('Use a specific stock item') }}
&nbsp;<i class="fas fa-question-circle text-muted"
data-toggle="tooltip"
title="{{ $__t('The first item in this list would be picked by the default rule which is "First expiring first, then first in first out"') }}"></i>
title="{{ $__t('The first item in this list would be picked by the default rule which is "First due first, then first in first out"') }}"></i>
</label>
</div>
<select disabled

View File

@@ -60,17 +60,17 @@
@endphp
@include('components.datetimepicker', array(
'id' => 'best_before_date',
'label' => 'Best before',
'label' => 'Due date',
'hint' => 'This will apply to added products',
'format' => 'YYYY-MM-DD',
'initWithNow' => false,
'limitEndToNow' => false,
'limitStartToNow' => false,
'invalidFeedback' => $__t('A best before date is required'),
'invalidFeedback' => $__t('A due date is required'),
'nextInputSelector' => '#best_before_date',
'additionalGroupCssClasses' => 'date-only-datetimepicker',
'shortcutValue' => '2999-12-31',
'shortcutLabel' => 'Never expires',
'shortcutLabel' => 'Never overdue',
'earlierThanInfoLimit' => date('Y-m-d'),
'earlierThanInfoText' => $__t('The given date is earlier than today, are you sure?'),
'additionalGroupCssClasses' => $additionalGroupCssClasses,

View File

@@ -61,7 +61,7 @@
for="is_freezer">{{ $__t('Is freezer') }}
&nbsp;<i class="fas fa-question-circle text-muted"
data-toggle="tooltip"
title="{{ $__t('When moving products from/to a freezer location, the products best before date is automatically adjusted according to the product settings') }}"></i>
title="{{ $__t('When moving products from/to a freezer location, the products due date is automatically adjusted according to the product settings') }}"></i>
</label>
</div>
</div>

View File

@@ -159,31 +159,72 @@
for="cumulate_min_stock_amount_of_sub_products">{{ $__t('Accumulate sub products min. stock amount') }}
&nbsp;<i class="fas fa-question-circle text-muted"
data-toggle="tooltip"
title="{{ $__t('If enabled, the min. stock amount of sub products will be accumulated into this product, means the sub product will never be "missing", only this product') }}"></i></span>
title="{{ $__t('If enabled, the min. stock amount of sub products will be accumulated into this product, means the sub product will never be "missing", only this product') }}"></i>
</label>
</div>
</div>
@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
<div class="form-group">
<label class="d-block my-0"
for="location_id">{{ $__t('Due date type') }}
<i class="fas fa-question-circle text-muted"
data-toggle="tooltip"
title="{{ $__t('Based on the selected type, the highlighting on the stock overview page will be different') }}"></i>
</label>
<div class="custom-control custom-radio mt-n2">
<input class="custom-control-input"
type="radio"
name="due_type"
id="due-type-bestbefore"
value="1"
@if($mode=='edit'
&&
$product->due_type == 1) checked @else checked @endif>
<label class="custom-control-label"
for="due-type-bestbefore">{{ $__t('Best before date') }}
<i class="fas fa-question-circle text-muted"
data-toggle="tooltip"
title="{{ $__t('Means that the product is maybe still safe to be consumed after its due date is reached') }}"></i>
</label>
</div>
<div class="custom-control custom-radio mt-n2">
<input class="custom-control-input"
type="radio"
name="due_type"
id="due-type-expiration"
value="2"
@if($mode=='edit'
&&
$product->due_type == 2) checked @endif>
<label class="custom-control-label"
for="due-type-expiration">{{ $__t('Expiration date') }}
<i class="fas fa-question-circle text-muted"
data-toggle="tooltip"
title="{{ $__t('Means that the product is not safe to be consumed after its due date is reached') }}"></i>
</label>
</div>
</div>
@php if($mode == 'edit') { $value = $product->default_best_before_days; } else { $value = 0; } @endphp
@include('components.numberpicker', array(
'id' => 'default_best_before_days',
'label' => 'Default best before days',
'label' => 'Default due days',
'min' => -1,
'value' => $value,
'invalidFeedback' => $__t('The amount cannot be lower than %s', '-1'),
'hint' => $__t('For purchases this amount of days will be added to today for the best before date suggestion') . ' (' . $__t('-1 means that this product never expires') . ')'
'hint' => $__t('For purchases this amount of days will be added to today for the due date suggestion') . ' (' . $__t('-1 means that this product wille be never overdue') . ')'
))
@if(GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
@php if($mode == 'edit') { $value = $product->default_best_before_days_after_open; } else { $value = 0; } @endphp
@include('components.numberpicker', array(
'id' => 'default_best_before_days_after_open',
'label' => 'Default best before days after opened',
'label' => 'Default due days after opened',
'min' => 0,
'value' => $value,
'invalidFeedback' => $__t('The amount cannot be lower than %s', '-1'),
'hint' => $__t('When this product was marked as opened, the best before date will be replaced by today + this amount of days (a value of 0 disables this)')
'hint' => $__t('When this product was marked as opened, the expiry date will be replaced by today + this amount of days (a value of 0 disables this)')
))
@else
<input type="hidden"
@@ -196,6 +237,10 @@
name="default_best_before_days"
id="default_best_before_days"
value="1">
<input type="hidden"
name="due_type"
id="due_type"
value="1">
@endif
<div class="form-group">
@@ -344,21 +389,21 @@
@php if($mode == 'edit') { $value = $product->default_best_before_days_after_freezing; } else { $value = 0; } @endphp
@include('components.numberpicker', array(
'id' => 'default_best_before_days_after_freezing',
'label' => 'Default best before days after freezing',
'label' => 'Default due days after freezing',
'min' => -1,
'value' => $value,
'invalidFeedback' => $__t('The amount cannot be lower than %s', '0'),
'hint' => $__t('On moving this product to a freezer location (so when freezing it), the best before date will be replaced by today + this amount of days')
'hint' => $__t('On moving this product to a freezer location (so when freezing it), the expiry date will be replaced by today + this amount of days')
))
@php if($mode == 'edit') { $value = $product->default_best_before_days_after_thawing; } else { $value = 0; } @endphp
@include('components.numberpicker', array(
'id' => 'default_best_before_days_after_thawing',
'label' => 'Default best before days after thawing',
'label' => 'Default due days after thawing',
'min' => -1,
'value' => $value,
'invalidFeedback' => $__t('The amount cannot be lower than %s', '0'),
'hint' => $__t('On moving this product from a freezer location (so when thawing it), the best before date will be replaced by today + this amount of days')
'hint' => $__t('On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days')
))
@else
<input type="hidden"

View File

@@ -81,16 +81,16 @@
@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
@include('components.datetimepicker', array(
'id' => 'best_before_date',
'label' => 'Best before',
'label' => 'Due date',
'format' => 'YYYY-MM-DD',
'initWithNow' => false,
'limitEndToNow' => false,
'limitStartToNow' => false,
'invalidFeedback' => $__t('A best before date is required'),
'invalidFeedback' => $__t('A due date is required'),
'nextInputSelector' => '#price',
'additionalCssClasses' => 'date-only-datetimepicker',
'shortcutValue' => '2999-12-31',
'shortcutLabel' => 'Never expires',
'shortcutLabel' => 'Never overdue',
'earlierThanInfoLimit' => date('Y-m-d'),
'earlierThanInfoText' => $__t('The given date is earlier than today, are you sure?'),
'activateNumberPad' => GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_FIELD_NUMBER_PAD
@@ -111,23 +111,23 @@
'additionalCssClasses' => 'locale-number-input locale-number-currency'
))
<div class="form-check form-check-inline mb-3">
<input class="form-check-input"
<div class="custom-control custom-radio custom-control-inline mt-n2 mb-3">
<input class="custom-control-input"
type="radio"
name="price-type"
id="price-type-unit-price"
value="unit-price"
checked>
<label class="form-check-label"
<label class="custom-control-label"
for="price-type-unit-price">{{ $__t('Unit price') }}</label>
</div>
<div class="form-check form-check-inline mb-3">
<input class="form-check-input"
<div class="custom-control custom-radio custom-control-inline mt-n2 mb-3">
<input class="custom-control-input"
type="radio"
name="price-type"
id="price-type-total-price"
value="total-price">
<label class="form-check-label"
<label class="custom-control-label"
for="price-type-total-price">{{ $__t('Total price') }}</label>
</div>
@include('components.shoppinglocationpicker', array(

View File

@@ -320,7 +320,7 @@
<label>{{ $__t('Costs') }}&nbsp;</label>
<i class="fas fa-question-circle text-muted"
data-toggle="tooltip"
title="{{ $__t('Based on the prices of the default consume rule which is "First expiring first, then first in first out"') }}"></i>
title="{{ $__t('Based on the prices of the default consume rule which is "First due first, then first in first out"') }}"></i>
<h3 class="locale-number locale-number-currency pt-0">{{ $costs }}</h3>
</div>
@endif

View File

@@ -115,10 +115,10 @@
href="#">
{{ $__t('Add products that are below defined min. stock amount') }}
</a>
<a id="add-expired-products"
<a id="add-overdue-products"
class="btn btn-outline-primary btn-sm mb-1 responsive-button @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
href="#">
{{ $__t('Add expired products') }}
{{ $__t('Add overdue products') }}
</a>
</div>
</div>

View File

@@ -38,7 +38,7 @@
data-setting-key="shopping_list_to_stock_workflow_auto_submit_when_prefilled">
<label class="form-check-label custom-control-label"
for="shopping_list_to_stock_workflow_auto_submit_when_prefilled">
{{ $__t('Automatically do the booking using the last price and the amount of the shopping list item, if the product has "Default best before days" set') }}
{{ $__t('Automatically do the booking using the last price and the amount of the shopping list item, if the product has "Default due days" set') }}
</label>
</div>
</div>

View File

@@ -66,7 +66,7 @@
<th>{{ $__t('Product') }}</th>
<th>{{ $__t('Amount') }}</th>
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
<th>{{ $__t('Best before date') }}</th>
<th>{{ $__t('Due date') }}</th>
@endif
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)<th>{{ $__t('Location') }}</th>@endif
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
@@ -83,7 +83,8 @@
<tbody class="d-none">
@foreach($stockEntries as $stockEntry)
<tr id="stock-{{ $stockEntry->id }}-row"
class="@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $stockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $stockEntry->amount > 0) table-danger @elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $stockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('+' . $nextXDays . ' days'))
data-due-type="{{ FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->due_type }}"
class="@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $stockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $stockEntry->amount > 0) @if(FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->due_type == 1) table-secondary @else table-danger @endif @elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $stockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('+' . $nextXDays . ' days'))
&&
$stockEntry->amount > 0) table-warning @endif">
<td class="fit-content border-right">
@@ -218,8 +219,8 @@
</td>
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
<td>
<span id="stock-{{ $stockEntry->id }}-best-before-date">{{ $stockEntry->best_before_date }}</span>
<time id="stock-{{ $stockEntry->id }}-best-before-date-timeago"
<span id="stock-{{ $stockEntry->id }}-due-date">{{ $stockEntry->best_before_date }}</span>
<time id="stock-{{ $stockEntry->id }}-due-date-timeago"
class="timeago timeago-contextual"
datetime="{{ $stockEntry->best_before_date }} 23:59:59"></time>
</td>

View File

@@ -36,16 +36,16 @@
@include('components.datetimepicker', array(
'id' => 'best_before_date',
'initialValue' => $stockEntry->best_before_date,
'label' => 'Best before',
'label' => 'Due date',
'format' => 'YYYY-MM-DD',
'initWithNow' => false,
'limitEndToNow' => false,
'limitStartToNow' => false,
'invalidFeedback' => $__t('A best before date is required'),
'invalidFeedback' => $__t('A due date is required'),
'nextInputSelector' => '#best_before_date',
'additionalGroupCssClasses' => 'date-only-datetimepicker',
'shortcutValue' => '2999-12-31',
'shortcutLabel' => 'Never expires',
'shortcutLabel' => 'Never overdue',
'earlierThanInfoLimit' => date('Y-m-d'),
'earlierThanInfoText' => $__t('The given date is earlier than today, are you sure?'),
'additionalGroupCssClasses' => $additionalGroupCssClasses,

View File

@@ -50,10 +50,13 @@
</div>
<div class="border-top border-bottom my-2 py-1">
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
<div id="info-expiring-products"
<div id="info-duesoon-products"
data-next-x-days="{{ $nextXDays }}"
data-status-filter="expiring"
data-status-filter="duesoon"
class="warning-message status-filter-message responsive-button mr-2"></div>
<div id="info-overdue-products"
data-status-filter="overdue"
class="secondary-message status-filter-message responsive-button mr-2"></div>
<div id="info-expired-products"
data-status-filter="expired"
class="error-message status-filter-message responsive-button mr-2"></div>
@@ -130,8 +133,9 @@
<option class="bg-white"
value="all">{{ $__t('All') }}</option>
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
<option value="expiring">{{ $__t('Expiring soon') }}</option>
<option value="expired">{{ $__t('Already expired') }}</option>
<option value="duesoon">{{ $__t('Due soon') }}</option>
<option value="overdue">{{ $__t('Overdue') }}</option>
<option value="expired">{{ $__t('Expired') }}</option>
@endif
<option value="belowminstockamount">{{ $__t('Below min. stock amount') }}</option>
</select>
@@ -156,7 +160,7 @@
<th>{{ $__t('Product group') }}</th>
<th>{{ $__t('Amount') }}</th>
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Value') }}</th>
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif">{{ $__t('Next best before date') }}</th>
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif">{{ $__t('Next due date') }}</th>
<th class="d-none">Hidden location</th>
<th class="d-none">Hidden status</th>
<th class="d-none">Hidden product group</th>
@@ -172,7 +176,7 @@
<tbody class="d-none">
@foreach($currentStock as $currentStockEntry)
<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 ($currentStockEntry->product_missing) table-info @endif">
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) @if($currentStockEntry->due_type == 1) table-secondary @else table-danger @endif @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 ($currentStockEntry->product_missing) table-info @endif">
<td class="fit-content border-right">
<a class="permission-STOCK_CONSUME btn btn-success btn-sm product-consume-button @if($currentStockEntry->amount < $currentStockEntry->quick_consume_amount || $currentStockEntry->enable_tare_weight_handling == 1) disabled @endif"
href="#"
@@ -340,8 +344,8 @@
class="locale-number locale-number-currency">{{ $currentStockEntry->value }}</span>
</td>
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif">
<span id="product-{{ $currentStockEntry->product_id }}-next-best-before-date">{{ $currentStockEntry->best_before_date }}</span>
<time id="product-{{ $currentStockEntry->product_id }}-next-best-before-date-timeago"
<span id="product-{{ $currentStockEntry->product_id }}-next-due-date">{{ $currentStockEntry->best_before_date }}</span>
<time id="product-{{ $currentStockEntry->product_id }}-next-due-date-timeago"
class="timeago timeago-contextual"
datetime="{{ $currentStockEntry->best_before_date }} 23:59:59"></time>
</td>
@@ -358,7 +362,7 @@
. ' days'
))
&&
$currentStockEntry->amount > 0) expired @elseif($currentStockEntry->best_before_date < date('Y-m-d
$currentStockEntry->amount > 0) @if($currentStockEntry->due_type == 1) overdue @else expired @endif @elseif($currentStockEntry->best_before_date < date('Y-m-d
23:59:59',
strtotime('+'
.
@@ -366,7 +370,7 @@
. ' days'
))
&&
$currentStockEntry->amount > 0) expiring @elseif ($currentStockEntry->product_missing) belowminstockamount @endif"
$currentStockEntry->amount > 0) duesoon @elseif ($currentStockEntry->product_missing) belowminstockamount @endif"
</td>
<td class="d-none">
xx{{ $currentStockEntry->product_group_name }}xx

View File

@@ -59,9 +59,9 @@
<h4 class="mt-2">{{ $__t('Stock overview') }}</h4>
@include('components.numberpicker', array(
'id' => 'stock_expiring_soon_days',
'additionalAttributes' => 'data-setting-key="stock_expiring_soon_days"',
'label' => 'Expiring soon days',
'id' => 'stock_due_soon_days',
'additionalAttributes' => 'data-setting-key="stock_due_soon_days"',
'label' => 'Due soon days',
'min' => 1,
'invalidFeedback' => $__t('This cannot be lower than %s', '1'),
'additionalCssClasses' => 'user-setting-control'
@@ -108,11 +108,11 @@
<div class="custom-control custom-checkbox">
<input type="checkbox"
class="form-check-input custom-control-input user-setting-control"
id="show_warning_on_purchase_when_best_before_date_is_earlier_than_next"
data-setting-key="show_warning_on_purchase_when_best_before_date_is_earlier_than_next">
id="show_warning_on_purchase_when_due_date_is_earlier_than_next"
data-setting-key="show_warning_on_purchase_when_due_date_is_earlier_than_next">
<label class="form-check-label custom-control-label"
for="show_warning_on_purchase_when_best_before_date_is_earlier_than_next">
{{ $__t('Show a warning when the best before date of the purchased product is earlier than the next best before date in stock') }}
for="show_warning_on_purchase_when_due_date_is_earlier_than_next">
{{ $__t('Show a warning when the due date of the purchased product is earlier than the next due date in stock') }}
</label>
</div>
</div>

View File

@@ -19,7 +19,7 @@
@include('components.numberpicker', array(
'id' => 'tasks_due_soon_days',
'additionalAttributes' => 'data-setting-key="tasks_due_soon_days"',
'label' => 'Tasks due soon days',
'label' => 'Due soon days',
'min' => 1,
'invalidFeedback' => $__t('This cannot be lower than %s', '1'),
'additionalCssClasses' => 'user-setting-control'

View File

@@ -73,7 +73,7 @@
for="use_specific_stock_entry">{{ $__t('Use a specific stock item') }}
&nbsp;<i class="fas fa-question-circle text-muted"
data-toggle="tooltip"
title="{{ $__t('The first item in this list would be picked by the default rule which is "First expiring first, then first in first out"') }}"></i>
title="{{ $__t('The first item in this list would be picked by the default rule which is "First due first, then first in first out"') }}"></i>
</label>
</div>
<select disabled