Add a "consume all ingredients of this recipe" button (this now closes #32)

This commit is contained in:
Bernd Bestel 2018-08-11 11:48:25 +02:00
parent 9a8c61497b
commit 324487d395
No known key found for this signature in database
GPG Key ID: 71BD34C0D4891300
7 changed files with 125 additions and 5 deletions

View File

@ -19,4 +19,17 @@ class RecipesApiController extends BaseApiController
$this->RecipesService->AddNotFulfilledProductsToShoppingList($args['recipeId']); $this->RecipesService->AddNotFulfilledProductsToShoppingList($args['recipeId']);
return $this->VoidApiActionResponse($response); return $this->VoidApiActionResponse($response);
} }
public function ConsumeRecipe(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{
try
{
$this->RecipesService->ConsumeRecipe($args['recipeId']);
return $this->VoidApiActionResponse($response);
}
catch (\Exception $ex)
{
return $this->VoidApiActionResponse($response, false, 400, $ex->getMessage());
}
}
} }

View File

@ -1010,6 +1010,37 @@
} }
} }
}, },
"/recipes/consume-recipe/{recipeId}": {
"get": {
"description": "Consumes all products of the given recipe",
"tags": [
"Recipes"
],
"parameters": [
{
"in": "path",
"name": "recipeId",
"required": true,
"description": "A valid recipe id",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "A VoidApiActionResponse object",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/VoidApiActionResponse"
}
}
}
}
}
}
},
"/habits/track-habit-execution/{habitId}": { "/habits/track-habit-execution/{habitId}": {
"get": { "get": {
"description": "Tracks an execution of the given habit", "description": "Tracks an execution of the given habit",

View File

@ -209,7 +209,10 @@ return array(
'This cannot be lower than #1' => 'Dies darf nicht kleiner als #1 sein', 'This cannot be lower than #1' => 'Dies darf nicht kleiner als #1 sein',
'-1 means that this product never expires' => '-1 bedeuet, dass dieses Produkt niemals abläuft', '-1 means that this product never expires' => '-1 bedeuet, dass dieses Produkt niemals abläuft',
'Quantity unit' => 'Mengeneinheit', 'Quantity unit' => 'Mengeneinheit',
'Only check if a single unit is in stock (a different quantity can then be used above)' => 'Nur prüfen, ob eine einzelne Einheit vorrätig ist (eine abweichende Mengeneinheit kann dann oben verwendet werden)', 'Only check if a single unit is in stock (a different quantity can then be used above)' => 'Nur prüfen, ob eine einzelne Einheit vorrätig ist (eine abweichende Mengeneinheit kann dann oben verwendet werden)',
'Are you sure to consume all ingredients needed by recipe "#1" (ingredients marked with "check only if a single unit is in stock" will be ignored)?' => 'Sicher, dass alle Zutaten die vom Rezept "#1" benötigt werden aus dem Bestand entfernt werden sollen (Zutaten markiert mit "nur prüfen, ob eine einzelne Einheit vorrätig ist" werden ignoriert)?',
'Removed all ingredients of recipe "#1" from stock' => 'Alle Zutaten, die vom Rezept "#1" benötigt werden, wurdem aus dem Bestand entfernt',
'Consume all ingredients needed by this recipe' => 'Alle Zutaten, die von diesem Rezept benötigt werden, aus dem Bestand enternen',
//Constants //Constants
'manually' => 'Manuell', 'manually' => 'Manuell',

View File

@ -104,6 +104,42 @@ $(document).on('click', '.recipe-order-missing-button', function(e)
}); });
}); });
$("#selectedRecipeConsumeButton").on('click', function(e)
{
var objectName = $(e.currentTarget).attr('data-recipe-name');
var objectId = $(e.currentTarget).attr('data-recipe-id');
bootbox.confirm({
message: L('Are you sure to consume all ingredients needed by recipe "#1" (ingredients marked with "check only if a single unit is in stock" will be ignored)?', objectName),
buttons: {
confirm: {
label: L('Yes'),
className: 'btn-success'
},
cancel: {
label: L('No'),
className: 'btn-danger'
}
},
callback: function(result)
{
if (result === true)
{
Grocy.Api.Get('recipes/consume-recipe/' + objectId,
function(result)
{
toastr.success(L('Removed all ingredients of recipe "#1" from stock', objectName));
},
function(xhr)
{
console.error(xhr);
}
);
}
}
});
});
recipesTables.on('select', function(e, dt, type, indexes) recipesTables.on('select', function(e, dt, type, indexes)
{ {
if (type === 'row') if (type === 'row')

View File

@ -91,6 +91,7 @@ $app->group('/api', function()
// Recipes // Recipes
$this->get('/recipes/add-not-fulfilled-products-to-shopping-list/{recipeId}', '\Grocy\Controllers\RecipesApiController:AddNotFulfilledProductsToShoppingList'); $this->get('/recipes/add-not-fulfilled-products-to-shopping-list/{recipeId}', '\Grocy\Controllers\RecipesApiController:AddNotFulfilledProductsToShoppingList');
$this->get('/recipes/consume-recipe/{recipeId}', '\Grocy\Controllers\RecipesApiController:ConsumeRecipe');
// Habits // Habits
$this->get('/habits/track-habit-execution/{habitId}', '\Grocy\Controllers\HabitsApiController:TrackHabitExecution'); $this->get('/habits/track-habit-execution/{habitId}', '\Grocy\Controllers\HabitsApiController:TrackHabitExecution');

View File

@ -2,8 +2,18 @@
namespace Grocy\Services; namespace Grocy\Services;
use \Grocy\Services\StockService;
class RecipesService extends BaseService class RecipesService extends BaseService
{ {
public function __construct()
{
parent::__construct();
$this->StockService = new StockService();
}
protected $StockService;
public function GetRecipesFulfillment() public function GetRecipesFulfillment()
{ {
$sql = 'SELECT * from recipes_fulfillment'; $sql = 'SELECT * from recipes_fulfillment';
@ -38,4 +48,27 @@ class RecipesService extends BaseService
} }
} }
} }
public function ConsumeRecipe($recipeId)
{
if (!$this->RecipeExists($recipeId))
{
throw new \Exception('Recipe does not exist');
}
$recipePositions = $this->Database->recipes_pos()->where('recipe_id', $recipeId)->fetchAll();
foreach ($recipePositions as $recipePosition)
{
if ($recipePosition->only_check_single_unit_in_stock == 0)
{
$this->StockService->ConsumeProduct($recipePosition->product_id, $recipePosition->amount, false, StockService::TRANSACTION_TYPE_CONSUME);
}
}
}
private function RecipeExists($recipeId)
{
$recipeRow = $this->Database->recipes()->where('id = :1', $recipeId)->fetch();
return $recipeRow !== null;
}
} }

View File

@ -39,9 +39,6 @@
<a class="btn btn-sm btn-danger recipe-delete-button" href="#" data-recipe-id="{{ $recipe->id }}" data-recipe-name="{{ $recipe->name }}"> <a class="btn btn-sm btn-danger recipe-delete-button" href="#" data-recipe-id="{{ $recipe->id }}" data-recipe-name="{{ $recipe->name }}">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</a> </a>
<a class="btn btn-sm btn-primary recipe-order-missing-button @if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1){{ disabled }}@endif" href="#" title="{{ $L('Put missing products on shopping list') }}" data-recipe-id="{{ $recipe->id }}" data-recipe-name="{{ $recipe->name }}">
<i class="fas fa-cart-plus"></i>
</a>
</td> </td>
<td> <td>
{{ $recipe->name }} {{ $recipe->name }}
@ -60,7 +57,13 @@
<div class="col-xs-12 col-md-6"> <div class="col-xs-12 col-md-6">
<div id="selectedRecipeCard" class="card"> <div id="selectedRecipeCard" class="card">
<div class="card-header"> <div class="card-header">
<i class="fas fa-cocktail"></i> {{ $selectedRecipe->name }} <i class="fas fa-cocktail"></i> {{ $selectedRecipe->name }}&nbsp;&nbsp;
<a id="selectedRecipeConsumeButton" class="btn btn-sm btn-outline-success py-0" href="#" title="{{ $L('Consume all ingredients needed by this recipe') }}" data-recipe-id="{{ $selectedRecipe->id }}" data-recipe-name="{{ $selectedRecipe->name }}">
<i class="fas fa-utensils"></i>
</a>
<a class="btn btn-sm btn-outline-primary py-0 recipe-order-missing-button @if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $selectedRecipe->id)->need_fulfilled_with_shopping_list == 1){{ disabled }}@endif" href="#" title="{{ $L('Put missing products on shopping list') }}" data-recipe-id="{{ $selectedRecipe->id }}" data-recipe-name="{{ $selectedRecipe->name }}">
<i class="fas fa-cart-plus"></i>
</a>
<a id="selectedRecipeToggleFullscreenButton" class="btn btn-sm btn-outline-secondary py-0 float-right" href="#" title="{{ $L('Expand to fullscreen') }}"> <a id="selectedRecipeToggleFullscreenButton" class="btn btn-sm btn-outline-secondary py-0 float-right" href="#" title="{{ $L('Expand to fullscreen') }}">
<i class="fas fa-expand-arrows-alt"></i> <i class="fas fa-expand-arrows-alt"></i>
</a> </a>