mirror of
https://github.com/grocy/grocy.git
synced 2026-04-05 20:36:15 +02:00
Merge branch 'master' into valueInformation
This commit is contained in:
commit
8350a66901
|
|
@ -30,6 +30,7 @@
|
|||
- Added a "Clear filter"-button on the stock overview page to quickly reset applied filters
|
||||
- It's now tracked who made a stock change (currently logged in user, visible on the stock journal page) (thanks @fipwmaqzufheoxq92ebc)
|
||||
- Product edit page improvements ("Save & continue" button, deleting and adding a product picuture is now possible in one go) (thanks @Ma27)
|
||||
- For products with tare weight handling enabled, it's now optionally possible to consume a fixed/exact amount (just like for "normal" products) in case you don't want to weigh the whole container this time (new checkbox on the consume page) (thanks @fipwmaqzufheoxq92ebc)
|
||||
- Fixed that it was not possible to leave the "Barcode(s)" on the product edit page by `TAB`
|
||||
- Fixed that when adding products through a product picker workflow and when the created products contains special characters, the product was not preselected on the previous page (thanks @Forceu)
|
||||
- Fixed that when editing a product the default store was not visible / always empty regardless if the product had one set (thanks @kriddles)
|
||||
|
|
@ -47,6 +48,7 @@
|
|||
- Decimal amounts are now allowed (for any product, rounded by two decimal places)
|
||||
- "Add products that are below defined min. stock amount" always rounded up the missing amount to an integral number, this now allows decimal numbers
|
||||
- Added a button to add all currently in-stock but expired products to the shopping list (thanks @m-byte)
|
||||
- Improved that when `FEATURE_FLAG_STOCK` is disabled, all product/stock related inputs and buttons are now hidden on the shopping list page (thanks @fipwmaqzufheoxq92ebc)
|
||||
|
||||
### Recipe improvements/fixes
|
||||
- It's now possible to print recipes (button next to the recipe title) (thanks @zsarnett)
|
||||
|
|
@ -58,12 +60,16 @@
|
|||
- Fixed that when editing a recipe ingredient the checkbox "Disable stock fulfillment checking for this ingredient" was not initaliased with the saved value
|
||||
- Fixed that when using the "+"-symbol (which appears on small screens only to expand columns which don't fit the screen) the page reloaded
|
||||
- Fixed that the status filter ("Enough in stock", etc.) on the recipes page did not filter recipes on the gallery tab (thanks @fipwmaqzufheoxq92ebc)
|
||||
- Fixed that consuming a recipe ingredient with tare weight handling enabled consumed a wrong amount (thanks @fipwmaqzufheoxq92ebc)
|
||||
|
||||
### Chores improvements/fixes
|
||||
- Changed that not assigned chores on the chores overview page display now just a dash instead of an ellipsis in the "Assigned to" column to make this more clear (thanks @Germs2004)
|
||||
- Fixed (again) that weekly chores, where the next execution should be in the same week, were scheduled (not) always (but sometimes) for the next week only (thanks @shadow7412)
|
||||
- Fixed that the assignment type "In alphabetic order" did not work correctly (the last person in the list was always assigned next once reached) (thanks @fipwmaqzufheoxq92ebc)
|
||||
|
||||
### Equipment improvements
|
||||
- The equipment page now will be never automatically reloaded, even when `Auto reload on external changes` is on and a change was detected (because you most probably have that page open longer to read the manual) (thanks @fipwmaqzufheoxq92ebc)
|
||||
|
||||
### Calendar improvements/fixes
|
||||
- Events are now links to the corresponding page (thanks @zsarnett)
|
||||
- Fixed a PHP warning when using the "Share/Integrate calendar (iCal)" button (thanks @tsia)
|
||||
|
|
@ -109,6 +115,7 @@
|
|||
- Fixed that the endpoint `/objects/{entity}/{objectId}` always returned successfully, even when the given object not exists (now returns `404` when the object is not found) (thanks @fipwmaqzufheoxq92ebc)
|
||||
- Fixed that the endpoint `/stock/volatile` didn't include products which expire today (thanks @fipwmaqzufheoxq92ebc)
|
||||
- Fixed that the endpoint `/objects/{entity}` did not include Userfields for Userentities (so the effective endpoint `/objects/userobjects`)
|
||||
- Fixed that the endpoint `/stock/consume` returned the response code `200` and an empty response body when `stock_entry_id` was set (consuming a specific stock entry) but invalid (now returns the response code `400`) (thanks @fipwmaqzufheoxq92ebc)
|
||||
- Endpoint `/calendar/ical`: Fixed that "Track date only"-chores were always set to happen at 12am (are treated as all-day events now)
|
||||
- Fixed (again) that CORS was broken
|
||||
|
||||
|
|
@ -129,6 +136,7 @@
|
|||
- Replaced (again, added before in v2.7.0, then reverted in v2.7.1 due to some problems) [QuaggaJS](https://github.com/serratus/quaggaJS) (seems to be unmaintained) by [Quagga2](https://github.com/ericblade/quagga2)
|
||||
- More `config.php` settings (see the section `Component configuration for Quagga2`) to tweak Quagga2 (this is the component used for device camera for barcode scanning) (thanks @andrelam)
|
||||
- Some localization string fixes (thanks @duckfullstop)
|
||||
- Fixed that XSS / HTML injection was possible through some user input fields (low severity / not really a problem as this could not be abused unauthenticated)
|
||||
- New translations: (thanks all the translators)
|
||||
- Greek (demo available at https://el.demo.grocy.info)
|
||||
- Korean (demo available at https://ko.demo.grocy.info)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@
|
|||
"gettext/gettext": "^4.8",
|
||||
"eluceo/ical": "^0.16.0",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"gumlet/php-image-resize": "^1.9"
|
||||
"gumlet/php-image-resize": "^1.9",
|
||||
"ezyang/htmlpurifier": "^4.13"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
|||
52
composer.lock
generated
52
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "70c5b65f78f4eb43dac8df8dc144e56c",
|
||||
"content-hash": "651fcabf083befffe196b08c8f17506b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "doctrine/inflector",
|
||||
|
|
@ -195,6 +195,56 @@
|
|||
],
|
||||
"time": "2019-12-30T22:54:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
"version": "v4.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ezyang/htmlpurifier.git",
|
||||
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
|
||||
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"HTMLPurifier": "library/"
|
||||
},
|
||||
"files": [
|
||||
"library/HTMLPurifier.composer.php"
|
||||
],
|
||||
"exclude-from-classmap": [
|
||||
"/library/HTMLPurifier/Language/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Edward Z. Yang",
|
||||
"email": "admin@htmlpurifier.org",
|
||||
"homepage": "http://ezyang.com"
|
||||
}
|
||||
],
|
||||
"description": "Standards compliant HTML filter written in PHP",
|
||||
"homepage": "http://htmlpurifier.org/",
|
||||
"keywords": [
|
||||
"html"
|
||||
],
|
||||
"time": "2020-06-29T00:56:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fig/http-message-util",
|
||||
"version": "1.1.4",
|
||||
|
|
|
|||
|
|
@ -115,4 +115,22 @@ class BaseApiController extends BaseController
|
|||
|
||||
return $this->OpenApiSpec;
|
||||
}
|
||||
|
||||
private static $htmlPurifierInstance = null;
|
||||
|
||||
protected function GetParsedAndFilteredRequestBody($request)
|
||||
{
|
||||
if (self::$htmlPurifierInstance == null)
|
||||
{
|
||||
self::$htmlPurifierInstance = new \HTMLPurifier(\HTMLPurifier_Config::createDefault());
|
||||
}
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
foreach ($requestBody as $key => &$value)
|
||||
{
|
||||
$value = self::$htmlPurifierInstance->purify($value);
|
||||
}
|
||||
|
||||
return $requestBody;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class BatteriesApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_BATTERIES_TRACK_CHARGE_CYCLE);
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class ChoresApiController extends BaseApiController
|
|||
{
|
||||
try
|
||||
{
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
$choreId = null;
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ class ChoresApiController extends BaseApiController
|
|||
|
||||
public function TrackChoreExecution(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class GenericEntityApiController extends BaseApiController
|
|||
User::checkPermission($request, User::PERMISSION_ADMIN);
|
||||
}
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -78,7 +78,8 @@ class GenericEntityApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_ADMIN);
|
||||
}
|
||||
$requestBody = $request->getParsedBody();
|
||||
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -202,7 +203,7 @@ class GenericEntityApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_MASTER_DATA_EDIT);
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class LoginController extends BaseController
|
|||
|
||||
public function ProcessLogin(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
$postParams = $request->getParsedBody();
|
||||
$postParams = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
if (isset($postParams['username']) && isset($postParams['password']))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class RecipesApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_SHOPPINGLIST_ITEMS_ADD);
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
$excludedProductIds = null;
|
||||
|
||||
if ($requestBody !== null && array_key_exists('excludedProductIds', $requestBody))
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class StockApiController extends BaseApiController
|
|||
|
||||
try
|
||||
{
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
$listId = 1;
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ class StockApiController extends BaseApiController
|
|||
|
||||
try
|
||||
{
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
$listId = 1;
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ class StockApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_STOCK_PURCHASE);
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -143,7 +143,7 @@ class StockApiController extends BaseApiController
|
|||
|
||||
try
|
||||
{
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
$listId = 1;
|
||||
$amount = 1;
|
||||
|
|
@ -190,7 +190,7 @@ class StockApiController extends BaseApiController
|
|||
|
||||
try
|
||||
{
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
$listId = 1;
|
||||
|
||||
|
|
@ -212,7 +212,7 @@ class StockApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_STOCK_CONSUME);
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
$result = null;
|
||||
|
||||
|
|
@ -263,7 +263,15 @@ class StockApiController extends BaseApiController
|
|||
$recipeId = $requestBody['recipe_id'];
|
||||
}
|
||||
|
||||
$bookingId = $this->getStockService()->ConsumeProduct($args['productId'], $requestBody['amount'], $spoiled, $transactionType, $specificStockEntryId, $recipeId, $locationId);
|
||||
$consumeExact = false;
|
||||
|
||||
if (array_key_exists('exact_amount', $requestBody))
|
||||
{
|
||||
$consumeExact = $requestBody['exact_amount'];
|
||||
}
|
||||
$transactionId = null;
|
||||
|
||||
$bookingId = $this->getStockService()->ConsumeProduct($args['productId'], $requestBody['amount'], $spoiled, $transactionType, $specificStockEntryId, $recipeId, $locationId, $transactionId, false, $consumeExact);
|
||||
return $this->ApiResponse($response, $this->getDatabase()->stock_log($bookingId));
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
|
|
@ -315,7 +323,7 @@ class StockApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_STOCK_EDIT);
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -391,7 +399,7 @@ class StockApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_STOCK_INVENTORY);
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -459,7 +467,7 @@ class StockApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_STOCK_OPEN);
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -574,7 +582,7 @@ class StockApiController extends BaseApiController
|
|||
|
||||
try
|
||||
{
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
$listId = 1;
|
||||
$amount = 1;
|
||||
|
|
@ -656,7 +664,7 @@ class StockApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_STOCK_TRANSFER);
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class SystemApiController extends BaseApiController
|
|||
{
|
||||
try
|
||||
{
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
$this->getLocalizationService()->CheckAndAddMissingTranslationToPot($requestBody['text']);
|
||||
return $this->EmptyApiResponse($response);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class TasksApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_TASKS_MARK_COMPLETED);
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class UsersApiController extends BaseApiController
|
|||
try
|
||||
{
|
||||
User::checkPermission($request, User::PERMISSION_ADMIN);
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
$this->getDatabase()->user_permissions()->createRow([
|
||||
'user_id' => $args['userId'],
|
||||
|
|
@ -32,7 +32,7 @@ class UsersApiController extends BaseApiController
|
|||
public function CreateUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
User::checkPermission($request, User::PERMISSION_USERS_CREATE);
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -75,7 +75,7 @@ class UsersApiController extends BaseApiController
|
|||
User::checkPermission($request, User::PERMISSION_USERS_EDIT);
|
||||
}
|
||||
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -152,7 +152,7 @@ class UsersApiController extends BaseApiController
|
|||
try
|
||||
{
|
||||
User::checkPermission($request, User::PERMISSION_ADMIN);
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
$db = $this->getDatabase();
|
||||
$db->user_permissions()
|
||||
->where('user_id', $args['userId'])
|
||||
|
|
@ -186,7 +186,7 @@ class UsersApiController extends BaseApiController
|
|||
{
|
||||
try
|
||||
{
|
||||
$requestBody = $request->getParsedBody();
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
$value = $this->getUsersService()->SetUserSetting(GROCY_USER_ID, $args['settingKey'], $requestBody['value']);
|
||||
return $this->EmptyApiResponse($response);
|
||||
|
|
|
|||
|
|
@ -195,8 +195,3 @@ function getQRCodeForAPIKey(apikey_type, apikey_key)
|
|||
}
|
||||
return getQRCodeForContent(content);
|
||||
}
|
||||
|
||||
function SanitizeHtml(input)
|
||||
{
|
||||
return $("<div/>").text(input).html();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,31 +15,34 @@
|
|||
// Check if the database has changed once a minute
|
||||
// If a change is detected, reload the current page, but only if already idling for at least 50 seconds,
|
||||
// when there is no unsaved form data and when the user enabled auto reloading
|
||||
setInterval(function()
|
||||
if (Grocy.DbChangedHandlingEnabledForPage)
|
||||
{
|
||||
Grocy.Api.Get('system/db-changed-time',
|
||||
function(result)
|
||||
{
|
||||
var newDbChangedTime = moment(result.changed_time);
|
||||
if (newDbChangedTime.isAfter(Grocy.DatabaseChangedTime))
|
||||
setInterval(function()
|
||||
{
|
||||
Grocy.Api.Get('system/db-changed-time',
|
||||
function(result)
|
||||
{
|
||||
if (Grocy.IdleTime >= 50)
|
||||
var newDbChangedTime = moment(result.changed_time);
|
||||
if (newDbChangedTime.isAfter(Grocy.DatabaseChangedTime))
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.auto_reload_on_db_change) && $("form.is-dirty").length === 0 && !$("body").hasClass("fullscreen-card"))
|
||||
if (Grocy.IdleTime >= 50)
|
||||
{
|
||||
window.location.reload();
|
||||
if (BoolVal(Grocy.UserSettings.auto_reload_on_db_change) && $("form.is-dirty").length === 0 && !$("body").hasClass("fullscreen-card"))
|
||||
{
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Grocy.DatabaseChangedTime = newDbChangedTime;
|
||||
Grocy.DatabaseChangedTime = newDbChangedTime;
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}, 60000);
|
||||
);
|
||||
}, 60000);
|
||||
}
|
||||
|
||||
Grocy.IdleTime = 0;
|
||||
Grocy.ResetIdleTime = function()
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ $("#search").on("keyup", Delay(function()
|
|||
|
||||
$(document).on('click', '.battery-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-battery-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-battery-name');
|
||||
var objectId = $(e.currentTarget).attr('data-battery-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ $("#search").on("keyup", Delay(function()
|
|||
|
||||
$(document).on('click', '.chore-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-chore-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-chore-name');
|
||||
var objectId = $(e.currentTarget).attr('data-chore-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ $('#product_id_text_input').on('blur', function(e)
|
|||
|
||||
Grocy.Components.ProductPicker.PopupOpen = true;
|
||||
bootbox.dialog({
|
||||
message: __t('"%s" could not be resolved to a product, how do you want to proceed?', SanitizeHtml(input)),
|
||||
message: __t('"%s" could not be resolved to a product, how do you want to proceed?', input),
|
||||
title: __t('Create or assign product'),
|
||||
onEscape: function()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
var jsonData = {};
|
||||
jsonData.amount = jsonForm.amount;
|
||||
jsonData.exact_amount = (jsonForm.exact_amount == "on");
|
||||
jsonData.spoiled = $('#spoiled').is(':checked');
|
||||
|
||||
if ($("#use_specific_stock_entry").is(":checked"))
|
||||
|
|
@ -70,7 +71,7 @@
|
|||
$("#use_specific_stock_entry").click();
|
||||
}
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
if (productDetails.product.enable_tare_weight_handling == 1 && !jsonData.exact_amount)
|
||||
{
|
||||
var successMessage = __t('Removed %1$s of %2$s from stock', Math.abs(jsonForm.amount - (parseFloat(productDetails.product.tare_weight) + parseFloat(productDetails.stock_amount))) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockTransaction(\'' + bookingResponse.transaction_id + '\')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>';
|
||||
}
|
||||
|
|
@ -177,11 +178,11 @@ $('#save-mark-as-open-button').on('click', function(e)
|
|||
}
|
||||
);
|
||||
});
|
||||
|
||||
var sumValue = 0;
|
||||
$("#location_id").on('change', function(e)
|
||||
{
|
||||
var locationId = $(e.target).val();
|
||||
var sumValue = 0;
|
||||
sumValue = 0;
|
||||
var stockId = null;
|
||||
|
||||
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
||||
|
|
@ -228,37 +229,8 @@ $("#location_id").on('change', function(e)
|
|||
Grocy.Api.Get('stock/products/' + Grocy.Components.ProductPicker.GetValue(),
|
||||
function(productDetails)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
$("#amount").attr("min", productDetails.product.tare_weight);
|
||||
$('#amount').attr('max', sumValue + parseFloat(productDetails.product.tare_weight));
|
||||
$("#amount").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', parseFloat(productDetails.product.tare_weight).toLocaleString(), (parseFloat(productDetails.stock_amount) + parseFloat(productDetails.product.tare_weight)).toLocaleString()));
|
||||
$("#tare-weight-handling-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#tare-weight-handling-info").addClass("d-none");
|
||||
|
||||
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 must be between %1$s and %2$s', 0.01.toLocaleString(), parseFloat(productDetails.stock_amount).toLocaleString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#amount").attr("min", "1");
|
||||
$("#amount").attr("step", "1");
|
||||
$("#amount").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', "1", parseFloat(productDetails.stock_amount).toLocaleString()));
|
||||
}
|
||||
|
||||
$('#amount').attr('max', sumValue);
|
||||
|
||||
if (sumValue == 0)
|
||||
{
|
||||
$("#amount").parent().find(".invalid-feedback").text(__t('There are no units available at this location'));
|
||||
}
|
||||
}
|
||||
current_productDetails = productDetails;
|
||||
RefreshForm();
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
|
|
@ -447,7 +419,7 @@ $("#specific_stock_entry").on("change", function(e)
|
|||
{
|
||||
if ($(e.target).val() == "")
|
||||
{
|
||||
var sumValue = 0;
|
||||
sumValue = 0;
|
||||
Grocy.Api.Get("stock/products/" + Grocy.Components.ProductPicker.GetValue() + '/entries',
|
||||
function(stockEntries)
|
||||
{
|
||||
|
|
@ -572,3 +544,49 @@ $("#scan-mode-button").on("click", function(e)
|
|||
$("#scan-mode-status").text(__t("off"));
|
||||
}
|
||||
});
|
||||
|
||||
$('#consume-exact-amount').on('change', RefreshForm);
|
||||
var current_productDetails;
|
||||
function RefreshForm()
|
||||
{
|
||||
var productDetails = current_productDetails;
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
$("#consume-exact-amount").parent().removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#consume-exact-amount").parent().addClass("d-none");
|
||||
}
|
||||
if (productDetails.product.enable_tare_weight_handling == 1 && !$('#consume-exact-amount').is(':checked'))
|
||||
{
|
||||
$("#amount").attr("min", productDetails.product.tare_weight);
|
||||
$('#amount').attr('max', sumValue + parseFloat(productDetails.product.tare_weight));
|
||||
$("#amount").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', parseFloat(productDetails.product.tare_weight).toLocaleString(), (parseFloat(productDetails.stock_amount) + parseFloat(productDetails.product.tare_weight)).toLocaleString()));
|
||||
$("#tare-weight-handling-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#tare-weight-handling-info").addClass("d-none");
|
||||
|
||||
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 must be between %1$s and %2$s', 0.01.toLocaleString(), parseFloat(productDetails.stock_amount).toLocaleString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#amount").attr("min", "1");
|
||||
$("#amount").attr("step", "1");
|
||||
$("#amount").parent().find(".invalid-feedback").text(__t('The amount must be between %1$s and %2$s', "1", parseFloat(productDetails.stock_amount).toLocaleString()));
|
||||
}
|
||||
|
||||
$('#amount').attr('max', sumValue);
|
||||
|
||||
if (sumValue == 0)
|
||||
{
|
||||
$("#amount").parent().find(".invalid-feedback").text(__t('There are no units available at this location'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ $("#search").on("keyup", Delay(function()
|
|||
|
||||
$(document).on('click', '.equipment-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-equipment-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-equipment-name');
|
||||
var objectId = $(e.currentTarget).attr('data-equipment-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ $("#search").on("keyup", Delay(function()
|
|||
|
||||
$(document).on('click', '.location-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-location-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-location-name');
|
||||
var objectId = $(e.currentTarget).attr('data-location-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -563,7 +563,7 @@ $(document).on('click', '.recipe-order-missing-button', function(e)
|
|||
// to prevent that the tooltip stays until clicked anywhere else
|
||||
document.activeElement.blur();
|
||||
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-recipe-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-recipe-name');
|
||||
var objectId = $(e.currentTarget).attr('data-recipe-id');
|
||||
var button = $(this);
|
||||
var servings = $(e.currentTarget).attr('data-mealplan-servings');
|
||||
|
|
@ -667,7 +667,7 @@ $(document).on('click', '.recipe-consume-button', function(e)
|
|||
// to prevent that the tooltip stays until clicked anywhere else
|
||||
document.activeElement.blur();
|
||||
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-recipe-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-recipe-name');
|
||||
var objectId = $(e.currentTarget).attr('data-recipe-id');
|
||||
var servings = $(e.currentTarget).attr('data-mealplan-servings');
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ $("#search").on("keyup", Delay(function()
|
|||
|
||||
$(document).on('click', '.product-group-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-group-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-group-name');
|
||||
var objectId = $(e.currentTarget).attr('data-group-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ if (typeof GetUriParam("product-group") !== "undefined")
|
|||
|
||||
$(document).on('click', '.product-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-product-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-product-name');
|
||||
var objectId = $(e.currentTarget).attr('data-product-id');
|
||||
|
||||
Grocy.Api.Get('stock/products/' + objectId,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ $("#search").on("keyup", Delay(function()
|
|||
|
||||
$(document).on('click', '.quantityunit-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-quantityunit-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-quantityunit-name');
|
||||
var objectId = $(e.currentTarget).attr('data-quantityunit-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
function saveRecipePicture(result, location)
|
||||
function saveRecipePicture(result, location, jsonData)
|
||||
{
|
||||
$recipeId = Grocy.EditObjectId || result.created_object_id;
|
||||
Grocy.Components.UserfieldsForm.Save(() =>
|
||||
|
|
@ -43,7 +43,7 @@ $('.save-recipe').on('click', function(e)
|
|||
{
|
||||
console.log(jsonData);
|
||||
Grocy.Api.Post('objects/recipes', jsonData,
|
||||
(result) => saveRecipePicture(result, location));
|
||||
(result) => saveRecipePicture(result, location, jsonData));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ $('.save-recipe').on('click', function(e)
|
|||
}
|
||||
|
||||
Grocy.Api.Put('objects/recipes/' + Grocy.EditObjectId, jsonData,
|
||||
(result) => saveRecipePicture(result, location),
|
||||
(result) => saveRecipePicture(result, location, jsonData),
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.EndUiBusy("recipe-form");
|
||||
|
|
@ -126,7 +126,7 @@ $('#recipe-form input').keydown(function(event)
|
|||
|
||||
$(document).on('click', '.recipe-pos-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-recipe-pos-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-recipe-pos-name');
|
||||
var objectId = $(e.currentTarget).attr('data-recipe-pos-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
@ -163,7 +163,7 @@ $(document).on('click', '.recipe-pos-delete-button', function(e)
|
|||
|
||||
$(document).on('click', '.recipe-include-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-recipe-include-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-recipe-include-name');
|
||||
var objectId = $(e.currentTarget).attr('data-recipe-include-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
@ -200,7 +200,7 @@ $(document).on('click', '.recipe-include-delete-button', function(e)
|
|||
|
||||
$(document).on('click', '.recipe-pos-show-note-button', function(e)
|
||||
{
|
||||
var note = SanitizeHtml($(e.currentTarget).attr('data-recipe-pos-note'));
|
||||
var note = $(e.currentTarget).attr('data-recipe-pos-note');
|
||||
|
||||
bootbox.alert(note);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ $(".recipe-delete").on('click', function(e)
|
|||
{
|
||||
e.preventDefault();
|
||||
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-recipe-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-recipe-name');
|
||||
var objectId = $(e.currentTarget).attr('data-recipe-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
@ -135,7 +135,7 @@ $(".recipe-delete").on('click', function(e)
|
|||
|
||||
$(document).on('click', '.recipe-shopping-list', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-recipe-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-recipe-name');
|
||||
var objectId = $(e.currentTarget).attr('data-recipe-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
@ -181,7 +181,7 @@ $(document).on('click', '.recipe-shopping-list', function(e)
|
|||
|
||||
$(".recipe-consume").on('click', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-recipe-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-recipe-name');
|
||||
var objectId = $(e.currentTarget).attr('data-recipe-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ $(".status-filter-message").on("click", function()
|
|||
|
||||
$("#delete-selected-shopping-list").on("click", function()
|
||||
{
|
||||
var objectName = SanitizeHtml($("#selected-shopping-list option:selected").text());
|
||||
var objectName = $("#selected-shopping-list option:selected").text();
|
||||
var objectId = $("#selected-shopping-list").val();
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
@ -172,7 +172,7 @@ $(document).on('click', '#add-expired-products', function(e)
|
|||
$(document).on('click', '#clear-shopping-list', function(e)
|
||||
{
|
||||
bootbox.confirm({
|
||||
message: __t('Are you sure to empty shopping list "%s"?', SanitizeHtml($("#selected-shopping-list option:selected").text())),
|
||||
message: __t('Are you sure to empty shopping list "%s"?', $("#selected-shopping-list option:selected").text()),
|
||||
closeButton: false,
|
||||
buttons: {
|
||||
confirm: {
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ if (Grocy.EditMode === "edit")
|
|||
|
||||
$('#amount').on('focus', function(e)
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetValue().length === 0)
|
||||
if (Grocy.Components.ProductPicker.GetValue().length === 0 && Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ $("#search").on("keyup", Delay(function()
|
|||
|
||||
$(document).on('click', '.shoppinglocation-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-shoppinglocation-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-shoppinglocation-name');
|
||||
var objectId = $(e.currentTarget).attr('data-shoppinglocation-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ $("#search").on("keyup", Delay(function()
|
|||
|
||||
$(document).on('click', '.task-category-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-category-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-category-name');
|
||||
var objectId = $(e.currentTarget).attr('data-category-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ $(document).on('click', '.delete-task-button', function(e)
|
|||
{
|
||||
e.preventDefault();
|
||||
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-task-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-task-name');
|
||||
var objectId = $(e.currentTarget).attr('data-task-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ $("#search").on("keyup", Delay(function()
|
|||
|
||||
$(document).on('click', '.userentity-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-userentity-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-userentity-name');
|
||||
var objectId = $(e.currentTarget).attr('data-userentity-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ $("#entity-filter").on("change", function()
|
|||
|
||||
$(document).on('click', '.userfield-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-userfield-name'));
|
||||
var objectName = $(e.currentTarget).attr('data-userfield-name');
|
||||
var objectId = $(e.currentTarget).attr('data-userfield-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ $("#search").on("keyup", Delay(function()
|
|||
|
||||
$(document).on('click', '.user-delete-button', function(e)
|
||||
{
|
||||
var objectName = SanitizeHtml($(e.currentTarget).attr('data-user-username'));
|
||||
var objectName = $(e.currentTarget).attr('data-user-username');
|
||||
var objectId = $(e.currentTarget).attr('data-user-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class RecipesService extends BaseService
|
|||
{
|
||||
if ($recipePosition->only_check_single_unit_in_stock == 0)
|
||||
{
|
||||
$this->getStockService()->ConsumeProduct($recipePosition->product_id, $recipePosition->recipe_amount, false, StockService::TRANSACTION_TYPE_CONSUME, 'default', $recipeId, null, $transactionId, true);
|
||||
$this->getStockService()->ConsumeProduct($recipePosition->product_id, $recipePosition->recipe_amount, false, StockService::TRANSACTION_TYPE_CONSUME, 'default', $recipeId, null, $transactionId, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ class StockService extends BaseService
|
|||
$this->getDatabase()->shopping_list()->where('shopping_list_id = :1', $listId)->delete();
|
||||
}
|
||||
|
||||
public function ConsumeProduct(int $productId, float $amount, bool $spoiled, $transactionType, $specificStockEntryId = 'default', $recipeId = null, $locationId = null, &$transactionId = null, $allowSubproductSubstitution = false)
|
||||
public function ConsumeProduct(int $productId, float $amount, bool $spoiled, $transactionType, $specificStockEntryId = 'default', $recipeId = null, $locationId = null, &$transactionId = null, $allowSubproductSubstitution = false, $consumeExactAmount = false)
|
||||
{
|
||||
if (!$this->ProductExists($productId))
|
||||
{
|
||||
|
|
@ -230,6 +230,10 @@ class StockService extends BaseService
|
|||
|
||||
if ($productDetails->product->enable_tare_weight_handling == 1)
|
||||
{
|
||||
if($consumeExactAmount)
|
||||
{
|
||||
$amount = floatval($productDetails->stock_amount) + floatval($productDetails->product->tare_weight) - $amount;
|
||||
}
|
||||
if ($amount < floatval($productDetails->product->tare_weight))
|
||||
{
|
||||
throw new \Exception('The amount cannot be lower than the defined tare weight');
|
||||
|
|
@ -249,6 +253,11 @@ class StockService extends BaseService
|
|||
$potentialStockEntries = $this->GetProductStockEntriesForLocation($productId, $locationId, false, $allowSubproductSubstitution);
|
||||
}
|
||||
|
||||
if ($specificStockEntryId !== 'default')
|
||||
{
|
||||
$potentialStockEntries = FindAllObjectsInArrayByPropertyValue($potentialStockEntries, 'stock_id', $specificStockEntryId);
|
||||
}
|
||||
|
||||
$productStockAmount = SumArrayValue($potentialStockEntries, 'amount');
|
||||
|
||||
if ($amount > $productStockAmount)
|
||||
|
|
@ -256,11 +265,6 @@ class StockService extends BaseService
|
|||
throw new \Exception('Amount to be consumed cannot be > current stock amount (if supplied, at the desired location)');
|
||||
}
|
||||
|
||||
if ($specificStockEntryId !== 'default')
|
||||
{
|
||||
$potentialStockEntries = FindAllObjectsInArrayByPropertyValue($potentialStockEntries, 'stock_id', $specificStockEntryId);
|
||||
}
|
||||
|
||||
if ($transactionId === null)
|
||||
{
|
||||
$transactionId = uniqid();
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@
|
|||
@php if(empty($prefillById)) { $prefillById = ''; } @endphp
|
||||
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
|
||||
@php if(empty($hint)) { $hint = ''; } @endphp
|
||||
@php if(empty($nextInputSelector)) { $nextInputSelector = ''; } @endphp
|
||||
|
||||
<div class="form-group"
|
||||
@if(isset($nextInputSelector))data-next-input-selector="{{ $nextInputSelector }}" @endif
|
||||
data-next-input-selector="{{ $nextInputSelector }}"
|
||||
data-prefill-by-name="{{ $prefillByName }}"
|
||||
data-prefill-by-id="{{ $prefillById }}">
|
||||
<label for="location_id">{{ $__t('Location') }} <span @if(!empty($hintId))id="{{ $hintId }}" @endif
|
||||
|
|
|
|||
|
|
@ -43,7 +43,10 @@
|
|||
'nextInputSelector' => '#amount',
|
||||
'disallowAddProductWorkflows' => true
|
||||
))
|
||||
|
||||
<label for="consume-exact-amount" class="d-none">
|
||||
<input type="checkbox" id="consume-exact-amount" name="exact_amount">
|
||||
{{ $__t('Consume exact amount') }}
|
||||
</label>
|
||||
@include('components.numberpicker', array(
|
||||
'id' => 'amount',
|
||||
'label' => 'Amount',
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
@section('title', $__t('Equipment'))
|
||||
@section('activeNav', 'equipment')
|
||||
@section('viewJsName', 'equipment')
|
||||
@section('DbChangedHandlingEnabledForPage', 'false')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@
|
|||
@if(file_exists(GROCY_DATAPATH . '/custom_css.html'))
|
||||
@php include GROCY_DATAPATH . '/custom_css.html' @endphp
|
||||
@endif
|
||||
|
||||
@section('DbChangedHandlingEnabledForPage', 'true')
|
||||
<script>
|
||||
var Grocy = { };
|
||||
Grocy.Components = { };
|
||||
|
|
@ -96,6 +96,7 @@
|
|||
Grocy.CalendarShowWeekNumbers = {{ BoolToString(GROCY_CALENDAR_SHOW_WEEK_OF_YEAR) }};
|
||||
Grocy.GettextPo = {!! $GettextPo !!};
|
||||
Grocy.FeatureFlags = {!! json_encode($featureFlags) !!};
|
||||
Grocy.DbChangedHandlingEnabledForPage = @yield('DbChangedHandlingEnabledForPage');
|
||||
|
||||
@if (GROCY_AUTHENTICATED)
|
||||
Grocy.UserSettings = {!! json_encode($userSettings) !!};
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
<span class="input-group-text"><i class="fas fa-filter"></i></span>
|
||||
</div>
|
||||
<select class="form-control"
|
||||
id="location-filter">
|
||||
id="product-group-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($productGroups as $productGroup)
|
||||
<option value="{{ $productGroup->id }}">{{ $productGroup->name }}</option>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@
|
|||
</div>
|
||||
<hr>
|
||||
<p data-status-filter="belowminstockamount"
|
||||
class="normal-message status-filter-message responsive-button">{{ $__n(count($missingProducts), '%s product is below defined min. stock amount', '%s products are below defined min. stock amount') }}</p>
|
||||
class="normal-message status-filter-message responsive-button @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif">{{ $__n(count($missingProducts), '%s product is below defined min. stock amount', '%s products are below defined min. stock amount') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -87,17 +87,17 @@
|
|||
{{ $__t('Clear list') }}
|
||||
</a>
|
||||
<a id="add-all-items-to-stock-button"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
href="#">
|
||||
{{ $__t('Add all list items to stock') }}
|
||||
</a>
|
||||
<a id="add-products-below-min-stock-amount"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
href="#">
|
||||
{{ $__t('Add products that are below defined min. stock amount') }}
|
||||
</a>
|
||||
<a id="add-expired-products"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
href="#">
|
||||
{{ $__t('Add expired products') }}
|
||||
</a>
|
||||
|
|
@ -121,7 +121,8 @@
|
|||
<select class="form-control"
|
||||
id="status-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
<option value="belowminstockamount">{{ $__t('Below min. stock amount') }}</option>
|
||||
<option class="@if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
value="belowminstockamount">{{ $__t('Below min. stock amount') }}</option>
|
||||
<option value="xxUNDONExx">{{ $__t('Only undone items') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -188,14 +189,14 @@
|
|||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-primary @if(empty($listItem->product_id)) disabled @else shopping-list-stock-add-workflow-list-item-button @endif"
|
||||
<a class="btn btn-sm btn-primary @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif @if(empty($listItem->product_id)) disabled @else shopping-list-stock-add-workflow-list-item-button @endif"
|
||||
href="{{ $U('/purchase?embedded&flow=shoppinglistitemtostock&product=') }}{{ $listItem->product_id }}&amount={{ $listItem->amount }}&listitemid={{ $listItem->id }}"
|
||||
@if(!empty($listItem->product_id)) data-toggle="tooltip" title="{{ $__t('Add %1$s of %2$s to stock', $listItem->amount . ' ' . $__n($listItem->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->qu_id_purchase)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->qu_id_purchase)->name_plural), FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->name, $listItem->amount) }}" @endif>
|
||||
<i class="fas fa-box"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
@if(!empty($listItem->product_id)) {{ FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->name }}<br>@endif<em>{!! nl2br(e($listItem->note)) !!}</em>
|
||||
@if(!empty($listItem->product_id)) {{ FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->name }}<br>@endif<em>{!! nl2br($listItem->note) !!}</em>
|
||||
</td>
|
||||
<td>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $listItem->amount }}</span> @if(!empty($listItem->product_id)){{ $__n($listItem->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->qu_id_purchase)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->qu_id_purchase)->name_plural) }}@endif
|
||||
|
|
@ -301,7 +302,7 @@
|
|||
@foreach($listItems as $listItem)
|
||||
<tr>
|
||||
<td>
|
||||
@if(!empty($listItem->product_id)) {{ FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->name }}<br>@endif<em>{!! nl2br(e($listItem->note)) !!}</em>
|
||||
@if(!empty($listItem->product_id)) {{ FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->name }}<br>@endif<em>{!! nl2br($listItem->note) !!}</em>
|
||||
</td>
|
||||
<td>
|
||||
{{ $listItem->amount }} @if(!empty($listItem->product_id)){{ $__n($listItem->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->qu_id_purchase)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->qu_id_purchase)->name_plural) }}@endif
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@
|
|||
value="1">
|
||||
@endif
|
||||
|
||||
@php if($mode == 'edit') { $productId = $listItem->product_id; } else { $productId = ''; } @endphp
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
'nextInputSelector' => '#amount',
|
||||
'isRequired' => false,
|
||||
'prefillById' => $productId
|
||||
))
|
||||
|
||||
<div class="@if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif" >
|
||||
@php if($mode == 'edit') { $productId = $listItem->product_id; } else { $productId = ''; } @endphp
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
'nextInputSelector' => '#amount',
|
||||
'isRequired' => false,
|
||||
'prefillById' => $productId
|
||||
))
|
||||
</div>
|
||||
@php if($mode == 'edit') { $value = $listItem->amount; } else { $value = 1; } @endphp
|
||||
@include('components.numberpicker', array(
|
||||
'id' => 'amount',
|
||||
|
|
@ -82,9 +83,10 @@
|
|||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_STOCK)
|
||||
<div class="col-xs-12 col-md-6 col-xl-4 hide-when-embedded">
|
||||
@include('components.productcard')
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -143,9 +143,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"))
|
||||
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">
|
||||
<td class="fit-content border-right">
|
||||
|
|
@ -331,10 +329,7 @@
|
|||
strtotime('-1
|
||||
days'))
|
||||
&&
|
||||
$currentStockEntry->amount > 0) expired @elseif($currentStockEntry->best_before_date < date('Y-m-d
|
||||
23:59:59',
|
||||
strtotime("+$nextXDays
|
||||
days"))
|
||||
$currentStockEntry->amount > 0) expired @elseif($currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime("+$nextXDays days"))
|
||||
&&
|
||||
$currentStockEntry->amount > 0) expiring @endif @if($currentStockEntry->product_missing) belowminstockamount @endif
|
||||
</td>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user