Make purchased date on /stockedit editable / Dirty workaround for 2 datetimepickers on the same page (references #506)

This commit is contained in:
Bernd Bestel 2020-01-23 18:58:05 +01:00
parent 3baffcfe7b
commit 99d4b05a3c
No known key found for this signature in database
GPG Key ID: 71BD34C0D4891300
7 changed files with 385 additions and 15 deletions

View File

@ -147,7 +147,7 @@ class StockApiController extends BaseApiController
$locationId = $requestBody['location_id']; $locationId = $requestBody['location_id'];
} }
$bookingId = $this->StockService->EditStockEntry($args['entryId'], $requestBody['amount'], $bestBeforeDate, $locationId, $price, $requestBody['open']); $bookingId = $this->StockService->EditStockEntry($args['entryId'], $requestBody['amount'], $bestBeforeDate, $locationId, $price, $requestBody['open'], $requestBody['purchased_date']);
return $this->ApiResponse($this->Database->stock_log($bookingId)); return $this->ApiResponse($this->Database->stock_log($bookingId));
} }
catch (\Exception $ex) catch (\Exception $ex)

View File

@ -1138,6 +1138,11 @@
"type": "number", "type": "number",
"format": "integer", "format": "integer",
"description": "If omitted, the default location of the product is used" "description": "If omitted, the default location of the product is used"
},
"purchased_date": {
"type": "string",
"format": "date",
"description": "The date when this stock entry was purchased"
} }
}, },
"example": { "example": {

View File

@ -0,0 +1,307 @@
Grocy.Components.DateTimePicker2 = { };
Grocy.Components.DateTimePicker2.GetInputElement = function()
{
return $('.datetimepicker2').find('input').not(".form-check-input");
}
Grocy.Components.DateTimePicker2.GetValue = function()
{
return Grocy.Components.DateTimePicker2.GetInputElement().val();
}
Grocy.Components.DateTimePicker2.SetValue = function(value)
{
Grocy.Components.DateTimePicker2.GetInputElement().val(value);
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
// "Click" the shortcut checkbox when the desired value is
// not the shortcut value and it is currently set
var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
if (value != shortcutValue && $("#datetimepicker2-shortcut").is(":checked"))
{
$("#datetimepicker2-shortcut").click();
}
Grocy.Components.DateTimePicker2.GetInputElement().keyup();
}
Grocy.Components.DateTimePicker2.Clear = function()
{
$(".datetimepicker2").datetimepicker("destroy");
Grocy.Components.DateTimePicker2.Init();
Grocy.Components.DateTimePicker2.GetInputElement().val("");
// "Click" the shortcut checkbox when the desired value is
// not the shortcut value and it is currently set
value = "";
var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
if (value != shortcutValue && $("#datetimepicker2-shortcut").is(":checked"))
{
$("#datetimepicker2-shortcut").click();
}
$('#datetimepicker-timeago').text('');
}
Grocy.Components.DateTimePicker2.ChangeFormat = function(format)
{
$(".datetimepicker2").datetimepicker("destroy");
Grocy.Components.DateTimePicker2.GetInputElement().data("format", format);
Grocy.Components.DateTimePicker2.Init();
if (format == "YYYY-MM-DD")
{
Grocy.Components.DateTimePicker2.GetInputElement().addClass("date-only-datetimepicker");
}
else
{
Grocy.Components.DateTimePicker2.GetInputElement().removeClass("date-only-datetimepicker");
}
}
var startDate = null;
if (Grocy.Components.DateTimePicker2.GetInputElement().data('init-with-now') === true)
{
startDate = moment().format(Grocy.Components.DateTimePicker2.GetInputElement().data('format'));
}
if (Grocy.Components.DateTimePicker2.GetInputElement().data('init-value').length > 0)
{
startDate = moment(Grocy.Components.DateTimePicker2.GetInputElement().data('init-value')).format(Grocy.Components.DateTimePicker2.GetInputElement().data('format'));
}
var limitDate = moment('2999-12-31 23:59:59');
if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-end-to-now') === true)
{
limitDate = moment();
}
Grocy.Components.DateTimePicker2.Init = function()
{
$('.datetimepicker').datetimepicker(
{
format: Grocy.Components.DateTimePicker2.GetInputElement().data('format'),
buttons: {
showToday: true,
showClose: true
},
calendarWeeks: Grocy.CalendarShowWeekNumbers,
maxDate: limitDate,
locale: moment.locale(),
defaultDate: startDate,
useCurrent: false,
icons: {
time: 'far fa-clock',
date: 'far fa-calendar',
up: 'fas fa-arrow-up',
down: 'fas fa-arrow-down',
previous: 'fas fa-chevron-left',
next: 'fas fa-chevron-right',
today: 'fas fa-calendar-check',
clear: 'far fa-trash-alt',
close: 'far fa-times-circle'
},
sideBySide: true,
keyBinds: {
up: function(widget) { },
down: function(widget) { },
'control up': function(widget) { },
'control down': function(widget) { },
left: function(widget) { },
right: function(widget) { },
pageUp: function(widget) { },
pageDown: function(widget) { },
enter: function(widget) { },
escape: function(widget) { },
'control space': function(widget) { },
t: function(widget) { },
'delete': function(widget) { }
}
});
}
Grocy.Components.DateTimePicker2.Init();
Grocy.Components.DateTimePicker2.GetInputElement().on('keyup', function(e)
{
$('.datetimepicker').datetimepicker('hide');
var value = Grocy.Components.DateTimePicker2.GetValue();
var now = new Date();
var centuryStart = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '00');
var centuryEnd = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '99');
var format = Grocy.Components.DateTimePicker2.GetInputElement().data('format');
var nextInputElement = $(Grocy.Components.DateTimePicker2.GetInputElement().data('next-input-selector'));
//If input is empty and any arrow key is pressed, set date to today
if (value.length === 0 && (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 37 || e.keyCode === 39))
{
Grocy.Components.DateTimePicker2.SetValue(moment(new Date(), format, true).format(format));
nextInputElement.focus();
}
else if (value === 'x' || value === 'X')
{
Grocy.Components.DateTimePicker2.SetValue(moment('2999-12-31 23:59:59').format(format));
nextInputElement.focus();
}
else if (value.length === 4 && !(Number.parseInt(value) > centuryStart && Number.parseInt(value) < centuryEnd))
{
var date = moment((new Date()).getFullYear().toString() + value);
if (date.isBefore(moment()))
{
date.add(1, "year");
}
Grocy.Components.DateTimePicker2.SetValue(date.format(format));
nextInputElement.focus();
}
else if (value.length === 8 && $.isNumeric(value))
{
Grocy.Components.DateTimePicker2.SetValue(value.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3'));
nextInputElement.focus();
}
else if (value.length === 7 && $.isNumeric(value.substring(0, 6)) && (value.substring(6, 7).toLowerCase() === "e" || value.substring(6, 7).toLowerCase() === "+"))
{
var date = moment(value.substring(0, 4) + "-" + value.substring(4, 6) + "-01").endOf("month");
Grocy.Components.DateTimePicker2.SetValue(date.format(format));
nextInputElement.focus();
}
else
{
var dateObj = moment(value, format, true);
if (dateObj.isValid())
{
if (e.shiftKey)
{
// WITH shift modifier key
if (e.keyCode === 38) // Up
{
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'months').format(format));
}
else if (e.keyCode === 40) // Down
{
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'months').format(format));
}
else if (e.keyCode === 37) // Left
{
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'years').format(format));
}
else if (e.keyCode === 39) // Right
{
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'years').format(format));
}
}
else
{
// WITHOUT shift modifier key
if (e.keyCode === 38) // Up
{
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'days').format(format));
}
else if (e.keyCode === 40) // Down
{
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'days').format(format));
}
else if (e.keyCode === 37) // Left
{
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'weeks').format(format));
}
else if (e.keyCode === 39) // Right
{
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'weeks').format(format));
}
}
}
}
//Custom validation
value = Grocy.Components.DateTimePicker2.GetValue();
dateObj = moment(value, format, true);
var element = Grocy.Components.DateTimePicker2.GetInputElement()[0];
if (!dateObj.isValid())
{
if ($(element).hasAttr("required"))
{
element.setCustomValidity("error");
}
}
else
{
if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-end-to-now') === true && dateObj.isAfter(moment()))
{
element.setCustomValidity("error");
}
else if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-start-to-now') === true && dateObj.isBefore(moment()))
{
element.setCustomValidity("error");
}
else
{
element.setCustomValidity("");
}
var earlierThanLimit = Grocy.Components.DateTimePicker2.GetInputElement().data("earlier-than-limit");
if (!earlierThanLimit.isEmpty())
{
if (moment(value).isBefore(moment(earlierThanLimit)))
{
$("#datetimepicker-earlier-than-info").removeClass("d-none");
}
else
{
$("#datetimepicker-earlier-than-info").addClass("d-none");
}
}
}
// "Click" the shortcut checkbox when the shortcut value was
// entered manually and it is currently not set
var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
if (value == shortcutValue && !$("#datetimepicker2-shortcut").is(":checked"))
{
$("#datetimepicker2-shortcut").click();
}
});
Grocy.Components.DateTimePicker2.GetInputElement().on('input', function(e)
{
$('#datetimepicker-timeago').attr("datetime", Grocy.Components.DateTimePicker2.GetValue());
EmptyElementWhenMatches('#datetimepicker-timeago', __t('timeago_nan'));
RefreshContextualTimeago();
});
$('.datetimepicker').on('update.datetimepicker', function(e)
{
Grocy.Components.DateTimePicker2.GetInputElement().trigger('input');
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress');
});
$('.datetimepicker').on('hide.datetimepicker', function(e)
{
Grocy.Components.DateTimePicker2.GetInputElement().trigger('input');
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress');
});
$("#datetimepicker2-shortcut").on("click", function()
{
if (this.checked)
{
var value = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
Grocy.Components.DateTimePicker2.SetValue(value);
Grocy.Components.DateTimePicker2.GetInputElement().attr("readonly", "");
$(Grocy.Components.DateTimePicker2.GetInputElement().data('next-input-selector')).focus();
}
else
{
Grocy.Components.DateTimePicker2.SetValue("");
Grocy.Components.DateTimePicker2.GetInputElement().removeAttr("readonly");
Grocy.Components.DateTimePicker2.GetInputElement().focus();
}
Grocy.Components.DateTimePicker2.GetInputElement().trigger('input');
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress');
});

View File

@ -13,6 +13,7 @@
var jsonData = { }; var jsonData = { };
jsonData.amount = jsonForm.amount; jsonData.amount = jsonForm.amount;
jsonData.best_before_date = Grocy.Components.DateTimePicker.GetValue(); jsonData.best_before_date = Grocy.Components.DateTimePicker.GetValue();
jsonData.purchased_date = Grocy.Components.DateTimePicker2.GetValue();
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
{ {
jsonData.location_id = Grocy.Components.LocationPicker.GetValue(); jsonData.location_id = Grocy.Components.LocationPicker.GetValue();
@ -69,18 +70,25 @@ $('#stockedit-form input').keydown(function(event)
} }
}); });
if (Grocy.Components.DateTimePicker) Grocy.Components.DateTimePicker.GetInputElement().on('change', function(e)
{ {
Grocy.Components.DateTimePicker.GetInputElement().on('change', function(e)
{
Grocy.FrontendHelpers.ValidateForm('stockedit-form'); Grocy.FrontendHelpers.ValidateForm('stockedit-form');
}); });
Grocy.Components.DateTimePicker.GetInputElement().on('keypress', function(e) Grocy.Components.DateTimePicker.GetInputElement().on('keypress', function(e)
{ {
Grocy.FrontendHelpers.ValidateForm('stockedit-form'); Grocy.FrontendHelpers.ValidateForm('stockedit-form');
}); });
}
Grocy.Components.DateTimePicker2.GetInputElement().on('change', function(e)
{
Grocy.FrontendHelpers.ValidateForm('stockedit-form');
});
Grocy.Components.DateTimePicker2.GetInputElement().on('keypress', function(e)
{
Grocy.FrontendHelpers.ValidateForm('stockedit-form');
});
var stockRowId = GetUriParam('stockRowId'); var stockRowId = GetUriParam('stockRowId');
Grocy.Api.Get("stock/entry/" + stockRowId, Grocy.Api.Get("stock/entry/" + stockRowId,
@ -91,6 +99,7 @@ Grocy.Api.Get("stock/entry/" + stockRowId,
$('#price').val(stockEntry.price); $('#price').val(stockEntry.price);
$("#open").prop('checked', BoolVal(stockEntry.open)); $("#open").prop('checked', BoolVal(stockEntry.open));
Grocy.Components.DateTimePicker.SetValue(stockEntry.best_before_date); Grocy.Components.DateTimePicker.SetValue(stockEntry.best_before_date);
Grocy.Components.DateTimePicker2.SetValue(stockEntry.purchased_date);
Grocy.Api.Get('stock/products/' + stockEntry.product_id, Grocy.Api.Get('stock/products/' + stockEntry.product_id,
function (productDetails) function (productDetails)

View File

@ -563,7 +563,7 @@ class StockService extends BaseService
return $this->Database->lastInsertId(); return $this->Database->lastInsertId();
} }
public function EditStockEntry(int $stockRowId, int $amount, $bestBeforeDate, $locationId, $price, $open) public function EditStockEntry(int $stockRowId, int $amount, $bestBeforeDate, $locationId, $price, $open, $purchasedDate)
{ {
$stockRow = $this->Database->stock()->where('id = :1', $stockRowId)->fetch(); $stockRow = $this->Database->stock()->where('id = :1', $stockRowId)->fetch();
@ -607,7 +607,8 @@ class StockService extends BaseService
'best_before_date' => $bestBeforeDate, 'best_before_date' => $bestBeforeDate,
'location_id' => $locationId, 'location_id' => $locationId,
'opened_date' => $openedDate, 'opened_date' => $openedDate,
'open' => $open 'open' => $open,
'purchased_date' => $purchasedDate
)); ));
$logNewRowForStockUpdate = $this->Database->stock_log()->createRow(array( $logNewRowForStockUpdate = $this->Database->stock_log()->createRow(array(

View File

@ -0,0 +1,48 @@
@push('componentScripts')
<script src="{{ $U('/viewjs/components/datetimepicker2.js', true) }}?v={{ $version }}"></script>
@endpush
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
@php if(!isset($initialValue)) { $initialValue = ''; } @endphp
@php if(empty($earlierThanInfoLimit)) { $earlierThanInfoLimit = ''; } @endphp
@php if(empty($earlierThanInfoText)) { $earlierThanInfoText = ''; } @endphp
@php if(empty($additionalCssClasses)) { $additionalCssClasses = ''; } @endphp
@php if(empty($additionalGroupCssClasses)) { $additionalGroupCssClasses = ''; } @endphp
@php if(empty($invalidFeedback)) { $invalidFeedback = ''; } @endphp
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
@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 {{ $additionalGroupCssClasses }}">
<label for="{{ $id }}">{{ $__t($label) }}
<span class="small text-muted">
@if(!empty($hint)){{ $__t($hint) }}@endif
<time id="datetimepicker2-timeago" class="timeago timeago-contextual"></time>
</span>
</label>
<div class="input-group">
<div class="input-group date datetimepicker2 @if(!empty($additionalGroupCssClasses)){{ $additionalGroupCssClasses }}@endif" id="{{ $id }}" @if(!$noNameAttribute) name="{{ $id }}" @endif data-target-input="nearest">
<input {!! $additionalAttributes !!} type="text" @if($isRequired) @if($isRequired) required @endif @endif class="form-control datetimepicker2-input @if(!empty($additionalCssClasses)){{ $additionalCssClasses }}@endif"
data-target="#{{ $id }}" data-format="{{ $format }}"
data-init-with-now="{{ BoolToString($initWithNow) }}"
data-init-value="{{ $initialValue }}"
data-limit-end-to-now="{{ BoolToString($limitEndToNow) }}"
data-limit-start-to-now="{{ BoolToString($limitStartToNow) }}"
data-next-input-selector="{{ $nextInputSelector }}"
data-earlier-than-limit="{{ $earlierThanInfoLimit }}" />
<div class="input-group-append" data-target="#{{ $id }}" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fas fa-calendar"></i></div>
</div>
<div class="invalid-feedback">{{ $invalidFeedback }}</div>
</div>
<div id="datetimepicker2-earlier-than-info" class="form-text text-info font-italic d-none">{{ $earlierThanInfoText }}</div>
@if(isset($shortcutValue) && isset($shortcutLabel))
<div class="form-check w-100">
<input class="form-check-input" type="checkbox" id="datetimepicker2-shortcut" data-datetimepicker2-shortcut-value="{{ $shortcutValue }}">
<label class="form-check-label" for="datetimepicker2-shortcut">{{ $__t($shortcutLabel) }}</label>
</div>
@endif
</div>
</div>

View File

@ -69,7 +69,7 @@
<input type="hidden" name="price" id="price" value="0"> <input type="hidden" name="price" id="price" value="0">
@endif @endif
@php /*@include('components.datetimepicker', array( @include('components.datetimepicker2', array(
'id' => 'purchase_date', 'id' => 'purchase_date',
'label' => 'Purchased date', 'label' => 'Purchased date',
'format' => 'YYYY-MM-DD', 'format' => 'YYYY-MM-DD',
@ -79,7 +79,7 @@
'invalidFeedback' => $__t('A purchased date is required'), 'invalidFeedback' => $__t('A purchased date is required'),
'nextInputSelector' => '#save-stockedit-button', 'nextInputSelector' => '#save-stockedit-button',
'additionalGroupCssClasses' => 'date-only-datetimepicker' 'additionalGroupCssClasses' => 'date-only-datetimepicker'
))*/ @endphp ))
<div class="checkbox"> <div class="checkbox">
<label for="open"> <label for="open">