diff --git a/controllers/RecipesController.php b/controllers/RecipesController.php
index ed0d8005..9d6e3654 100644
--- a/controllers/RecipesController.php
+++ b/controllers/RecipesController.php
@@ -132,6 +132,7 @@ class RecipesController extends BaseController
public function MealPlan(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{
$recipes = $this->Database->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->fetchAll();
+ $products = $this->Database->products()->orderBy('name');
$events = array();
foreach($this->Database->meal_plan() as $mealPlanEntry)
@@ -141,22 +142,39 @@ class RecipesController extends BaseController
if ($recipe !== null)
{
$title = $recipe->name;
+
+ $events[] = array(
+ 'id' => $mealPlanEntry['id'],
+ 'title' => $title,
+ 'start' => $mealPlanEntry['day'],
+ 'date_format' => 'date',
+ 'recipe' => json_encode($recipe),
+ 'mealPlanEntry' => json_encode($mealPlanEntry),
+ 'type' => $mealPlanEntry['type']
+ );
}
- $events[] = array(
- 'id' => $mealPlanEntry['id'],
- 'title' => $title,
- 'start' => $mealPlanEntry['day'],
- 'date_format' => 'date',
- 'recipe' => json_encode($recipe),
- 'mealPlanEntry' => json_encode($mealPlanEntry),
- 'type' => $mealPlanEntry['type']
- );
+ $product = FindObjectInArrayByPropertyValue($products, 'id', $mealPlanEntry['product_id']);
+ if ($product !== null)
+ {
+ $title = $product->name;
+
+ $events[] = array(
+ 'id' => $mealPlanEntry['id'],
+ 'title' => $title,
+ 'start' => $mealPlanEntry['day'],
+ 'date_format' => 'date',
+ 'product' => json_encode($product),
+ 'mealPlanEntry' => json_encode($mealPlanEntry),
+ 'type' => $mealPlanEntry['type']
+ );
+ }
}
return $this->AppContainer->view->render($response, 'mealplan', [
'fullcalendarEventSources' => $events,
'recipes' => $recipes,
+ 'products' => $products,
'internalRecipes' => $this->Database->recipes()->whereNot('type', RecipesService::RECIPE_TYPE_NORMAL)->fetchAll(),
'recipesResolved' => $this->RecipesService->GetRecipesResolved()
]);
diff --git a/migrations/0096.sql b/migrations/0096.sql
index a3e66a30..3ace6fc0 100644
--- a/migrations/0096.sql
+++ b/migrations/0096.sql
@@ -6,6 +6,7 @@ CREATE TABLE meal_plan (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
day DATE NOT NULL,
type TEXT DEFAULT 'recipe',
+ product_id INTEGER,
recipe_id INTEGER,
recipe_servings INTEGER DEFAULT 1,
note TEXT,
diff --git a/public/viewjs/mealplan.js b/public/viewjs/mealplan.js
index 956f8383..fc73eadd 100644
--- a/public/viewjs/mealplan.js
+++ b/public/viewjs/mealplan.js
@@ -157,6 +157,33 @@ var calendar = $("#calendar").fullCalendar({
}
}
}
+ if (event.type == "product")
+ {
+ var product = JSON.parse(event.product);
+ if (product === null || product === undefined)
+ {
+ return false;
+ }
+
+
+ element.attr("data-product", event.product);
+
+ var productConsumeButtonDisabledClasses = "";
+ element.html('\
+
\
+
' + product.name + ' \
+ ' + __n(mealPlanEntry.recipe_servings, "%s serving", "%s servings") + '
\
+ \
+ \
+ \
+
\
+
');
+
+ if (product.picture_file_name && !product.picture_file_name.isEmpty())
+ {
+ element.html(element.html() + '')
+ }
+ }
else if (event.type == "note")
{
element.html('\
@@ -254,7 +281,18 @@ $('#save-add-recipe-button').on('click', function(e)
return false;
}
- Grocy.Api.Post('objects/meal_plan', $('#add-recipe-form').serializeJSON(),
+ var jsonData = $('#add-recipe-form').serializeJSON();
+
+ if (Grocy.Components.RecipePicker.GetValue() != "")
+ {
+ jsonData.type = "recipe";
+ }
+ else if (Grocy.Components.ProductPicker.GetValue() != "")
+ {
+ jsonData.type = "product";
+ }
+
+ Grocy.Api.Post('objects/meal_plan', jsonData,
function(result)
{
window.location.reload();
@@ -376,6 +414,45 @@ $(document).on('click', '.recipe-order-missing-button', function(e)
});
});
+$(document).on('click', '.product-consume-button', function(e)
+{
+ e.preventDefault();
+
+ // Remove the focus from the current button
+ // to prevent that the tooltip stays until clicked anywhere else
+ document.activeElement.blur();
+
+ Grocy.FrontendHelpers.BeginUiBusy();
+
+ var productId = $(e.currentTarget).attr('data-product-id');
+ var consumeAmount = $(e.currentTarget).attr('data-servings-amount');
+
+ Grocy.Api.Post('stock/products/' + productId + '/consume', { 'amount': consumeAmount, 'spoiled': false },
+ function(bookingResponse)
+ {
+ Grocy.Api.Get('stock/products/' + productId,
+ function(result)
+ {
+ var toastMessage = __t('Removed %1$s of %2$s from stock', consumeAmount.toString() + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural), result.product.name) + '
' + __t("Undo") + '';
+
+ Grocy.FrontendHelpers.EndUiBusy();
+ toastr.success(toastMessage);
+ },
+ function(xhr)
+ {
+ Grocy.FrontendHelpers.EndUiBusy();
+ console.error(xhr);
+ }
+ );
+ },
+ function(xhr)
+ {
+ Grocy.FrontendHelpers.EndUiBusy();
+ console.error(xhr);
+ }
+ );
+});
+
$(document).on('click', '.recipe-consume-button', function(e)
{
// Remove the focus from the current button
@@ -461,3 +538,16 @@ $(window).on("resize", function()
calendar.fullCalendar("changeView", "basicWeek");
}
});
+function UndoStockTransaction(transactionId)
+{
+ Grocy.Api.Post('stock/transactions/' + transactionId.toString() + '/undo', { },
+ function (result)
+ {
+ toastr.success(__t("Transaction successfully undone"));
+ },
+ function (xhr)
+ {
+ console.error(xhr);
+ }
+ );
+};
diff --git a/views/mealplan.blade.php b/views/mealplan.blade.php
index e4759cad..79351d90 100644
--- a/views/mealplan.blade.php
+++ b/views/mealplan.blade.php
@@ -45,7 +45,14 @@
@include('components.recipepicker', array(
'recipes' => $recipes,
- 'isRequired' => true,
+ 'isRequired' => false,
+ 'nextInputSelector' => '#recipe_servings'
+ ))
+
+ @include('components.productpicker', array(
+ 'products' => $products,
+ 'isRequired' => false,
+ 'disallowAllProductWorkflows' => true,
'nextInputSelector' => '#recipe_servings'
))
@@ -58,7 +65,6 @@
))
-