Reuse existing shopping list items when adding products from the stock overview page (closes #375)

This commit is contained in:
Bernd Bestel 2019-09-24 18:27:50 +02:00
parent b0c7958891
commit 5351828e79
No known key found for this signature in database
GPG Key ID: 71BD34C0D4891300
8 changed files with 63 additions and 13 deletions

View File

@ -2,6 +2,7 @@
- It's now possible to display a recipe directly from the meal plan (new "eye button") (thanks @kriddles) - It's now possible to display a recipe directly from the meal plan (new "eye button") (thanks @kriddles)
- Improved the responsiveness of the meal plan and calendar page by automatically switching to a day calendar view on smaller screens (thanks for the idea @kriddles) - Improved the responsiveness of the meal plan and calendar page by automatically switching to a day calendar view on smaller screens (thanks for the idea @kriddles)
- The calendar now also contains all planned recipes from the meal plan on the corresponding day - The calendar now also contains all planned recipes from the meal plan on the corresponding day
- When adding a product to the shopping list from the new context/more menu from the stock overview page and if the product is already on the shopping list, the amount of that entry will be updated acccordingly instead of adding a new (double) shopping list item
- The API Endpoint `GET /files/{group}/{fileName}` now also returns a `Cache-Control` header (defaults fixed to 30 days) to further increase page load times - The API Endpoint `GET /files/{group}/{fileName}` now also returns a `Cache-Control` header (defaults fixed to 30 days) to further increase page load times
- Fixed that the API endpoint `/stock/shoppinglist/add-product` failed when a product should be added which was not already on the shopping list (thanks @Forceu) - Fixed that the API endpoint `/stock/shoppinglist/add-product` failed when a product should be added which was not already on the shopping list (thanks @Forceu)
- Some style/CSS detail-refinements - Some style/CSS detail-refinements

View File

@ -341,7 +341,7 @@ class StockApiController extends BaseApiController
} }
public function AddItemToShoppingList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) public function AddProductToShoppingList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{ {
try try
{ {
@ -350,6 +350,7 @@ class StockApiController extends BaseApiController
$listId = 1; $listId = 1;
$amount = 1; $amount = 1;
$productId = null; $productId = null;
$note = null;
if (array_key_exists('list_id', $requestBody) && !empty($requestBody['list_id']) && is_numeric($requestBody['list_id'])) if (array_key_exists('list_id', $requestBody) && !empty($requestBody['list_id']) && is_numeric($requestBody['list_id']))
{ {
$listId = intval($requestBody['list_id']); $listId = intval($requestBody['list_id']);
@ -362,13 +363,17 @@ class StockApiController extends BaseApiController
{ {
$productId = intval($requestBody['product_id']); $productId = intval($requestBody['product_id']);
} }
if (array_key_exists('note', $requestBody) && !empty($requestBody['note']))
{
$note = $requestBody['note'];
}
if ($productId == null) if ($productId == null)
{ {
throw new \Exception("No product id was supplied"); throw new \Exception("No product id was supplied");
} }
$this->StockService->AddProductToShoppingList($productId, $amount, $listId); $this->StockService->AddProductToShoppingList($productId, $amount, $note, $listId);
return $this->EmptyApiResponse($response); return $this->EmptyApiResponse($response);
} }
catch (\Exception $ex) catch (\Exception $ex)
@ -377,7 +382,7 @@ class StockApiController extends BaseApiController
} }
} }
public function RemoveItemFromShoppingList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) public function RemoveProductFromShoppingList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{ {
try try
{ {

View File

@ -1935,7 +1935,8 @@
}, },
"/stock/shoppinglist/add-product": { "/stock/shoppinglist/add-product": {
"post": { "post": {
"summary": "Adds the given product to the given shopping list", "summary": "Adds the given amount of the given product to the given shopping list",
"description": "If the product is already on the shopping list, the given amount will increase the amount of the already existing item, otherwise a new item will be added",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -1957,12 +1958,17 @@
"product_amount": { "product_amount": {
"type": "integer", "type": "integer",
"description": "The amount of product units to add, when omitted, the default amount of 1 is used" "description": "The amount of product units to add, when omitted, the default amount of 1 is used"
},
"note": {
"type": "string",
"description": "The note of the shopping list item"
} }
}, },
"example": { "example": {
"product_id": 3, "product_id": 3,
"list_id": 2, "list_id": 2,
"product_amount": 5 "product_amount": 5,
"note": "This is the note of the shopping list item..."
} }
} }
} }
@ -1987,7 +1993,8 @@
}, },
"/stock/shoppinglist/remove-product": { "/stock/shoppinglist/remove-product": {
"post": { "post": {
"summary": "Removes the given product from the given shopping list, if it is on it", "summary": "Removes the given amount of the given product from the given shopping list, if it is on it",
"description": "If the resulting amount is <= 0, the item will be completely removed from the given list, otherwise the given amount will reduce the amount of the existing item",
"tags": [ "tags": [
"Stock" "Stock"
], ],

View File

@ -5,6 +5,41 @@
var jsonData = $('#shoppinglist-form').serializeJSON(); var jsonData = $('#shoppinglist-form').serializeJSON();
Grocy.FrontendHelpers.BeginUiBusy("shoppinglist-form"); Grocy.FrontendHelpers.BeginUiBusy("shoppinglist-form");
if (GetUriParam("updateexistingproduct") !== undefined)
{
jsonData.product_amount = jsonData.amount;
delete jsonData.amount;
Grocy.Api.Post('stock/shoppinglist/add-product', jsonData,
function(result)
{
if (GetUriParam("embedded") !== undefined)
{
Grocy.Api.Get('stock/products/' + jsonData.product_id,
function (productDetails)
{
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", jsonData.product_amount + " " + __n(jsonData.product_amount, productDetails.quantity_unit_purchase.name, productDetails.quantity_unit_purchase.name_plural), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
},
function (xhr)
{
console.error(xhr);
}
);
}
else
{
window.location.href = U('/shoppinglist?list=' + $("#shopping_list_id").val().toString());
}
},
function(xhr)
{
Grocy.FrontendHelpers.EndUiBusy("shoppinglist-form");
console.error(xhr);
}
);
}
if (Grocy.EditMode === 'create') if (Grocy.EditMode === 'create')
{ {
Grocy.Api.Post('objects/shopping_list', jsonData, Grocy.Api.Post('objects/shopping_list', jsonData,

View File

@ -288,7 +288,7 @@ $(document).on("click", ".product-add-to-shopping-list-button", function(e)
var productId = $(e.currentTarget).attr("data-product-id"); var productId = $(e.currentTarget).attr("data-product-id");
bootbox.dialog({ bootbox.dialog({
message: '<iframe height="650px" class="embed-responsive" src="' + U("/shoppinglistitem/new?embedded&product=") + productId.toString() + '"></iframe>', message: '<iframe height="650px" class="embed-responsive" src="' + U("/shoppinglistitem/new?embedded&updateexistingproduct&product=") + productId.toString() + '"></iframe>',
size: 'large', size: 'large',
backdrop: true, backdrop: true,
closeButton: false, closeButton: false,

View File

@ -181,8 +181,8 @@ $app->group('/api', function()
{ {
$this->post('/stock/shoppinglist/add-missing-products', '\Grocy\Controllers\StockApiController:AddMissingProductsToShoppingList'); $this->post('/stock/shoppinglist/add-missing-products', '\Grocy\Controllers\StockApiController:AddMissingProductsToShoppingList');
$this->post('/stock/shoppinglist/clear', '\Grocy\Controllers\StockApiController:ClearShoppingList'); $this->post('/stock/shoppinglist/clear', '\Grocy\Controllers\StockApiController:ClearShoppingList');
$this->post('/stock/shoppinglist/add-product', '\Grocy\Controllers\StockApiController:AddItemToShoppingList'); $this->post('/stock/shoppinglist/add-product', '\Grocy\Controllers\StockApiController:AddProductToShoppingList');
$this->post('/stock/shoppinglist/remove-product', '\Grocy\Controllers\StockApiController:RemoveItemFromShoppingList'); $this->post('/stock/shoppinglist/remove-product', '\Grocy\Controllers\StockApiController:RemoveProductFromShoppingList');
} }
// Recipes // Recipes

View File

@ -569,7 +569,7 @@ class StockService extends BaseService
} }
} }
public function AddProductToShoppingList($productId, $amount = 1, $listId = 1) public function AddProductToShoppingList($productId, $amount = 1, $note = null, $listId = 1)
{ {
if (!$this->ShoppingListExists($listId)) if (!$this->ShoppingListExists($listId))
{ {
@ -586,7 +586,8 @@ class StockService extends BaseService
{ {
$alreadyExistingEntry->update(array( $alreadyExistingEntry->update(array(
'amount' => ($alreadyExistingEntry->amount + $amount), 'amount' => ($alreadyExistingEntry->amount + $amount),
'shopping_list_id' => $listId 'shopping_list_id' => $listId,
'note' => $note
)); ));
} }
else // Insert else // Insert
@ -594,7 +595,8 @@ class StockService extends BaseService
$shoppinglistRow = $this->Database->shopping_list()->createRow(array( $shoppinglistRow = $this->Database->shopping_list()->createRow(array(
'product_id' => $productId, 'product_id' => $productId,
'amount' => $amount, 'amount' => $amount,
'shopping_list_id' => $listId 'shopping_list_id' => $listId,
'note' => $note
)); ));
$shoppinglistRow->save(); $shoppinglistRow->save();
} }

View File

@ -22,7 +22,7 @@
<form id="shoppinglist-form" novalidate> <form id="shoppinglist-form" novalidate>
<div class="form-group"> <div class="form-group">
<label for="product_group_id">{{ $__t('Shopping list') }}</label> <label for="shopping_list_id">{{ $__t('Shopping list') }}</label>
<select class="form-control" id="shopping_list_id" name="shopping_list_id"> <select class="form-control" id="shopping_list_id" name="shopping_list_id">
@foreach($shoppingLists as $shoppingList) @foreach($shoppingLists as $shoppingList)
<option @if($mode == 'edit' && $shoppingList->id == $listItem->shopping_list_id) selected="selected" @endif value="{{ $shoppingList->id }}">{{ $shoppingList->name }}</option> <option @if($mode == 'edit' && $shoppingList->id == $listItem->shopping_list_id) selected="selected" @endif value="{{ $shoppingList->id }}">{{ $shoppingList->name }}</option>