diff --git a/controllers/RecipesApiController.php b/controllers/RecipesApiController.php new file mode 100644 index 00000000..fd2cce5a --- /dev/null +++ b/controllers/RecipesApiController.php @@ -0,0 +1,22 @@ +RecipesService = new RecipesService(); + } + + protected $RecipesService; + + public function AddNotFulfilledProductsToShoppingList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) + { + $this->RecipesService->AddNotFulfilledProductsToShoppingList($args['recipeId']); + return $this->VoidApiActionResponse($response); + } +} diff --git a/migrations/0025.sql b/migrations/0025.sql index e128d4f9..b5e27513 100644 --- a/migrations/0025.sql +++ b/migrations/0025.sql @@ -19,20 +19,32 @@ AS SELECT r.id AS recipe_id, rp.id AS recipe_pos_id, - rp.product_id, + rp.product_id AS product_id, rp.amount AS recipe_amount, sc.amount AS stock_amount, - CASE WHEN sc.amount >= rp.amount THEN 1 ELSE 0 END AS need_fullfiled + CASE WHEN IFNULL(sc.amount, 0) >= rp.amount THEN 1 ELSE 0 END AS need_fulfilled, + CASE WHEN IFNULL(sc.amount, 0) - rp.amount < 0 THEN ABS(sc.amount - rp.amount) ELSE 0 END AS missing_amount, + IFNULL(sl.amount, 0) AS amount_on_shopping_list, + CASE WHEN IFNULL(sc.amount, 0) + IFNULL(sl.amount, 0) >= rp.amount THEN 1 ELSE 0 END AS need_fulfilled_with_shopping_list FROM recipes r -LEFT JOIN recipes_pos rp +JOIN recipes_pos rp ON r.id = rp.recipe_id +LEFT JOIN ( + SELECT product_id, SUM(amount + amount_autoadded) AS amount + FROM shopping_list + GROUP BY product_id) sl + ON rp.product_id = sl.product_id LEFT JOIN stock_current sc ON rp.product_id = sc.product_id; CREATE VIEW recipes_fulfillment_sum AS SELECT - rf.recipe_id, - MIN(rf.need_fullfiled) AS need_fullfiled -FROM recipes_fulfillment rf -GROUP BY rf.recipe_id; + r.id AS recipe_id, + IFNULL(MIN(rf.need_fulfilled), 1) AS need_fulfilled, + IFNULL(MIN(rf.need_fulfilled_with_shopping_list), 1) AS need_fulfilled_with_shopping_list, + (SELECT COUNT(*) FROM recipes_fulfillment WHERE recipe_id = rf.recipe_id AND need_fulfilled = 0 AND recipe_pos_id IS NOT NULL) AS missing_products_count +FROM recipes r +LEFT JOIN recipes_fulfillment rf + ON rf.recipe_id = r.id +GROUP BY r.id; diff --git a/package.json b/package.json index dc0b3b0c..8693082e 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "datatables.net-responsive-bs4": "^2.2.3", "datatables.net-colreorder": "^1.5.1", "datatables.net-colreorder-bs4": "^1.5.1", + "datatables.net-select": "^1.2.7", + "datatables.net-select-bs4": "^1.2.7", "jquery": "^3.3.1", "jquery-serializejson": "^2.8.1", "jquery-ui-dist": "^1.12.1", diff --git a/public/viewjs/recipeform.js b/public/viewjs/recipeform.js index 53d82ab1..433e0fa0 100644 --- a/public/viewjs/recipeform.js +++ b/public/viewjs/recipeform.js @@ -96,3 +96,27 @@ $(document).on('click', '.recipe-pos-delete-button', function(e) } }); }); + +$(document).on('click', '.recipe-pos-order-missing-button', function(e) +{ + var productName = $(e.currentTarget).attr('data-product-name'); + var productId = $(e.currentTarget).attr('data-product-id'); + var productAmount = $(e.currentTarget).attr('data-product-amount'); + var recipeName = $(e.currentTarget).attr('data-recipe-name'); + + var jsonData = {}; + jsonData.product_id = productId; + jsonData.amount = productAmount; + jsonData.note = L('Added for recipe #1', recipeName); + + Grocy.Api.Post('add-object/shopping_list', jsonData, + function(result) + { + window.location.href = U('/recipe/' + Grocy.EditObjectId); + }, + function(xhr) + { + console.error(xhr); + } + ); +}); diff --git a/public/viewjs/recipeposform.js b/public/viewjs/recipeposform.js index 7f38c51b..6fa2d7ac 100644 --- a/public/viewjs/recipeposform.js +++ b/public/viewjs/recipeposform.js @@ -46,6 +46,7 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e) { $('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name); $('#amount').focus(); + Grocy.FrontendHelpers.ValidateForm('recipe-pos-form'); }, function(xhr) { diff --git a/public/viewjs/recipes.js b/public/viewjs/recipes.js index 54689654..c96eae2f 100644 --- a/public/viewjs/recipes.js +++ b/public/viewjs/recipes.js @@ -7,7 +7,8 @@ 'language': JSON.parse(L('datatables_localization')), 'scrollY': false, 'colReorder': true, - 'stateSave': true + 'stateSave': true, + 'select': 'single' }); $("#search").on("keyup", function() @@ -56,3 +57,39 @@ $(document).on('click', '.recipe-delete-button', function(e) } }); }); + +$(document).on('click', '.recipe-order-missing-button', 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 put all missing ingredients for recipe "#1" on the shopping list?', 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/add-not-fulfilled-products-to-shopping-list/' + objectId, + function(result) + { + window.location.href = U('/recipes'); + }, + function(xhr) + { + console.error(xhr); + } + ); + } + } + }); +}); diff --git a/routes.php b/routes.php index aba8932a..2cfc997d 100644 --- a/routes.php +++ b/routes.php @@ -77,6 +77,8 @@ $app->group('/api', function() $this->get('/stock/add-missing-products-to-shoppinglist', 'Grocy\Controllers\StockApiController:AddMissingProductsToShoppingList'); $this->get('/stock/external-barcode-lookup/{barcode}', 'Grocy\Controllers\StockApiController:ExternalBarcodeLookup'); + $this->get('/recipes/add-not-fulfilled-products-to-shopping-list/{recipeId}', 'Grocy\Controllers\RecipesApiController:AddNotFulfilledProductsToShoppingList'); + $this->get('/habits/track-habit-execution/{habitId}', 'Grocy\Controllers\HabitsApiController:TrackHabitExecution'); $this->get('/habits/get-habit-details/{habitId}', 'Grocy\Controllers\HabitsApiController:HabitDetails'); diff --git a/services/BaseService.php b/services/BaseService.php index 82d12abb..43c0af4e 100644 --- a/services/BaseService.php +++ b/services/BaseService.php @@ -3,14 +3,19 @@ namespace Grocy\Services; use \Grocy\Services\DatabaseService; +use \Grocy\Services\LocalizationService; class BaseService { public function __construct() { $this->DatabaseService = new DatabaseService(); $this->Database = $this->DatabaseService->GetDbConnection(); + + $localizationService = new LocalizationService(CULTURE); + $this->LocalizationService = $localizationService; } protected $DatabaseService; protected $Database; + protected $LocalizationService; } diff --git a/services/LocalizationService.php b/services/LocalizationService.php index 56e81d27..5dbe1a48 100644 --- a/services/LocalizationService.php +++ b/services/LocalizationService.php @@ -2,14 +2,12 @@ namespace Grocy\Services; -class LocalizationService extends BaseService +class LocalizationService { const DEFAULT_CULTURE = 'en'; public function __construct(string $culture) { - parent::__construct(); - $this->Culture = $culture; $this->StringsDefaultCulture = $this->LoadLocalizationFile(self::DEFAULT_CULTURE); diff --git a/services/RecipesService.php b/services/RecipesService.php index 02824635..bbad144c 100644 --- a/services/RecipesService.php +++ b/services/RecipesService.php @@ -15,4 +15,27 @@ class RecipesService extends BaseService $sql = 'SELECT * from recipes_fulfillment_sum'; return $this->DatabaseService->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); } + + public function AddNotFulfilledProductsToShoppingList($recipeId) + { + $recipe = $this->Database->recipes($recipeId); + + $recipePositions = $this->GetRecipesFulfillment(); + foreach ($recipePositions as $recipePosition) + { + if($recipePosition->recipe_id == $recipeId) + { + $toOrderAmount = $recipePosition->missing_amount - $recipePosition->amount_on_shopping_list; + if($toOrderAmount > 0) + { + $shoppinglistRow = $this->Database->shopping_list()->createRow(array( + 'product_id' => $recipePosition->product_id, + 'amount' => $toOrderAmount, + 'note' => $this->LocalizationService->Localize('Added for recipe #1', $recipe->name) + )); + $shoppinglistRow->save(); + } + } + } + } } diff --git a/views/layout/default.blade.php b/views/layout/default.blade.php index 6fe888f0..171f83fe 100644 --- a/views/layout/default.blade.php +++ b/views/layout/default.blade.php @@ -20,6 +20,7 @@ + @@ -90,7 +91,7 @@