diff --git a/controllers/RecipesController.php b/controllers/RecipesController.php index b9cebc0e..3ff3316d 100644 --- a/controllers/RecipesController.php +++ b/controllers/RecipesController.php @@ -35,19 +35,21 @@ class RecipesController extends BaseController } } - // Scale ingredients amount based on desired servings - foreach ($selectedRecipePositions as $selectedRecipePosition) - { - $selectedRecipePosition->amount = $selectedRecipePosition->amount * ($selectedRecipe->desired_servings / $selectedRecipe->base_servings); - } - $selectedRecipeSubRecipes = $this->Database->recipes()->where('id IN (SELECT includes_recipe_id FROM recipes_nestings_resolved WHERE recipe_id = :1 AND includes_recipe_id != :1)', $selectedRecipe->id)->orderBy('name')->fetchAll(); $selectedRecipeSubRecipesPositions = $this->Database->recipes_pos()->where('recipe_id IN (SELECT includes_recipe_id FROM recipes_nestings_resolved WHERE recipe_id = :1 AND includes_recipe_id != :1)', $selectedRecipe->id)->orderBy('ingredient_group')->fetchAll(); - // Scale ingredients amount based on desired servings + // Scale ingredients amount based on desired servings & calculate total costs + $recipesFulfillment = $this->RecipesService->GetRecipesFulfillment(); + $totalRecipeCosts = 0; + foreach ($selectedRecipePositions as $selectedRecipePosition) + { + $selectedRecipePosition->amount = $selectedRecipePosition->amount * ($selectedRecipe->desired_servings / $selectedRecipe->base_servings); + $totalRecipeCosts += FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $selectedRecipePosition->id)->costs; + } foreach ($selectedRecipeSubRecipesPositions as $selectedSubRecipePosition) { $selectedSubRecipePosition->amount = $selectedSubRecipePosition->amount * ($selectedRecipe->desired_servings / $selectedRecipe->base_servings); + $totalRecipeCosts += FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $selectedSubRecipePosition->id)->costs; } $includedRecipeIdsAbsolute = array(); @@ -59,7 +61,7 @@ class RecipesController extends BaseController return $this->AppContainer->view->render($response, 'recipes', [ 'recipes' => $recipes, - 'recipesFulfillment' => $this->RecipesService->GetRecipesFulfillment(), + 'recipesFulfillment' => $recipesFulfillment, 'recipesSumFulfillment' => $this->RecipesService->GetRecipesSumFulfillment(), 'selectedRecipe' => $selectedRecipe, 'selectedRecipePositions' => $selectedRecipePositions, @@ -67,7 +69,8 @@ class RecipesController extends BaseController 'quantityunits' => $this->Database->quantity_units(), 'selectedRecipeSubRecipes' => $selectedRecipeSubRecipes, 'selectedRecipeSubRecipesPositions' => $selectedRecipeSubRecipesPositions, - 'includedRecipeIdsAbsolute' => $includedRecipeIdsAbsolute + 'includedRecipeIdsAbsolute' => $includedRecipeIdsAbsolute, + 'totalRecipeCosts' => $totalRecipeCosts ]); } diff --git a/migrations/0054.sql b/migrations/0054.sql new file mode 100644 index 00000000..0e574b3d --- /dev/null +++ b/migrations/0054.sql @@ -0,0 +1,79 @@ +CREATE VIEW products_current_price +AS +SELECT + p.id AS product_id, + IFNULL(sl.price, 0) AS last_price +FROM products p +LEFT JOIN ( + SELECT product_id, MAX(id) AS newest_Id + FROM stock_log + WHERE undone = 0 + AND transaction_type = 'purchase' + GROUP BY product_id + ) slg + ON p.id = slg.product_id +LEFT JOIN stock_log sl + ON slg.newest_id = sl.id; + +DROP VIEW recipes_fulfillment; +CREATE VIEW recipes_fulfillment +AS +SELECT + r.id AS recipe_id, + rp.id AS recipe_pos_id, + rp.product_id AS product_id, + rp.amount * (r.desired_servings / r.base_servings) AS recipe_amount, + IFNULL(sc.amount, 0) AS stock_amount, + CASE WHEN IFNULL(sc.amount, 0) >= CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE IFNULL(rp.amount, 0) * (r.desired_servings / r.base_servings) END THEN 1 ELSE 0 END AS need_fulfilled, + CASE WHEN IFNULL(sc.amount, 0) - CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE IFNULL(rp.amount, 0) * (r.desired_servings / r.base_servings) END < 0 THEN ABS(IFNULL(sc.amount, 0) - (CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE IFNULL(rp.amount, 0) * (r.desired_servings / r.base_servings) END)) ELSE 0 END AS missing_amount, + IFNULL(sl.amount, 0) * p.qu_factor_purchase_to_stock AS amount_on_shopping_list, + CASE WHEN IFNULL(sc.amount, 0) + (CASE WHEN r.not_check_shoppinglist = 1 THEN 0 ELSE IFNULL(sl.amount, 0) END * p.qu_factor_purchase_to_stock) >= CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE IFNULL(rp.amount, 0) * (r.desired_servings / r.base_servings) END THEN 1 ELSE 0 END AS need_fulfilled_with_shopping_list, + rp.qu_id, + (CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE rp.amount * (r.desired_servings / r.base_servings) END / p.qu_factor_purchase_to_stock) * pcp.last_price AS costs +FROM recipes r +JOIN recipes_pos rp + ON r.id = rp.recipe_id +JOIN products p + ON rp.product_id = p.id +LEFT JOIN ( + SELECT product_id, SUM(amount) 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 +LEFT JOIN products_current_price pcp + ON rp.product_id = pcp.product_id +WHERE rp.not_check_stock_fulfillment = 0 + +UNION + +-- Just add all recipe positions which should not be checked against stock with fulfilled need + +SELECT + r.id AS recipe_id, + rp.id AS recipe_pos_id, + rp.product_id AS product_id, + rp.amount * (r.desired_servings / r.base_servings) AS recipe_amount, + IFNULL(sc.amount, 0) AS stock_amount, + 1 AS need_fulfilled, + 0 AS missing_amount, + IFNULL(sl.amount, 0) * p.qu_factor_purchase_to_stock AS amount_on_shopping_list, + 1 AS need_fulfilled_with_shopping_list, + rp.qu_id, + (CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE rp.amount * (r.desired_servings / r.base_servings) END / p.qu_factor_purchase_to_stock) * pcp.last_price AS costs +FROM recipes r +JOIN recipes_pos rp + ON r.id = rp.recipe_id +JOIN products p + ON rp.product_id = p.id +LEFT JOIN ( + SELECT product_id, SUM(amount) 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 +LEFT JOIN products_current_price pcp + ON rp.product_id = pcp.product_id +WHERE rp.not_check_stock_fulfillment = 1; diff --git a/public/js/grocy.js b/public/js/grocy.js index 45766bef..0a27099f 100644 --- a/public/js/grocy.js +++ b/public/js/grocy.js @@ -501,3 +501,8 @@ $("#about-dialog-link").on("click", function() closeButton: false }); }); + +$(".local-number-format[data-format='currency']").each(function () +{ + $(this).text(parseFloat($(this).text()).toLocaleString(undefined, { minimumFractionDigits: 2 })); +}); diff --git a/services/StockService.php b/services/StockService.php index da73ad81..923172d3 100644 --- a/services/StockService.php +++ b/services/StockService.php @@ -26,6 +26,12 @@ class StockService extends BaseService return $this->DatabaseService->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); } + public function GetCurrentProductPrices() + { + $sql = 'SELECT * FROM products_current_price'; + return $this->DatabaseService->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); + } + public function GetMissingProducts() { $sql = 'SELECT * FROM stock_missing_products'; diff --git a/views/recipeform.blade.php b/views/recipeform.blade.php index 3adcb786..6aee73fe 100644 --- a/views/recipeform.blade.php +++ b/views/recipeform.blade.php @@ -66,7 +66,7 @@
+ {{ $totalRecipeCosts }} {{ GROCY_CURRENCY }} +
+