diff --git a/controllers/RecipesController.php b/controllers/RecipesController.php index 75f3919f..27cdc4bd 100644 --- a/controllers/RecipesController.php +++ b/controllers/RecipesController.php @@ -76,8 +76,9 @@ class RecipesController extends BaseController { return $this->AppContainer->view->render($response, 'recipeposform', [ 'mode' => 'create', - 'recipe' => $this->Database->recipes($args['recipeId']), - 'products' => $this->Database->products()->orderBy('name') + 'recipe' => $this->Database->recipes($args['recipeId']), + 'products' => $this->Database->products()->orderBy('name'), + 'quantityUnits' => $this->Database->quantity_units()->orderBy('name') ]); } else @@ -85,8 +86,9 @@ class RecipesController extends BaseController return $this->AppContainer->view->render($response, 'recipeposform', [ 'mode' => 'edit', 'recipe' => $this->Database->recipes($args['recipeId']), - 'recipePos' => $this->Database->recipes_pos($args['recipePosId']), - 'products' => $this->Database->products()->orderBy('name') + 'recipePos' => $this->Database->recipes_pos($args['recipePosId']), + 'products' => $this->Database->products()->orderBy('name'), + 'quantityUnits' => $this->Database->quantity_units()->orderBy('name') ]); } } diff --git a/localization/de.php b/localization/de.php index 96c700ab..1f01765d 100644 --- a/localization/de.php +++ b/localization/de.php @@ -208,6 +208,8 @@ return array( 'Never expires' => 'Läuft nie ab', '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', + '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)', //Constants 'manually' => 'Manuell', @@ -272,5 +274,10 @@ return array( 'Italian' => 'Italienisch', 'Demo in different language' => 'Demo in anderer Sprache', 'This is the note content of the recipe ingredient' => 'Dies ist der Inhalt der Notiz der Zutat', - 'Demo User' => 'Demo Benutzer' + 'Demo User' => 'Demo Benutzer', + 'Gram' => 'Gramm', + 'Grams' => 'Gramm', + 'Flour' => 'Mehl', + 'Pancackes' => 'Pfannkuchen', + 'Sugar' => 'Zucker' ); diff --git a/migrations/0034.sql b/migrations/0034.sql new file mode 100644 index 00000000..fc999ef6 --- /dev/null +++ b/migrations/0034.sql @@ -0,0 +1,41 @@ +ALTER TABLE recipes_pos +ADD qu_id INTEGER; + +UPDATE recipes_pos +SET qu_id = (SELECT qu_id_stock FROM products where id = product_id); + +CREATE TRIGGER recipes_pos_qu_id_default AFTER INSERT ON recipes_pos +BEGIN + UPDATE recipes_pos + SET qu_id = (SELECT qu_id_stock FROM products where id = product_id) + WHERE qu_id IS NULL + AND id = NEW.id; +END; + +ALTER TABLE recipes_pos +ADD only_check_single_unit_in_stock TINYINT NOT NULL DEFAULT 0; + +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 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) 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) 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) END) 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) >= CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 1 ELSE IFNULL(rp.amount, 0) END THEN 1 ELSE 0 END AS need_fulfilled_with_shopping_list, + rp.qu_id +FROM recipes r +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; diff --git a/public/viewjs/recipeposform.js b/public/viewjs/recipeposform.js index 6fa2d7ac..01df2e98 100644 --- a/public/viewjs/recipeposform.js +++ b/public/viewjs/recipeposform.js @@ -2,7 +2,7 @@ { e.preventDefault(); - var jsonData = $('#recipe-pos-form').serializeJSON(); + var jsonData = $('#recipe-pos-form').serializeJSON({ checkboxUncheckedValue: "0" }); jsonData.recipe_id = Grocy.EditObjectParentId; console.log(jsonData); if (Grocy.EditMode === 'create') @@ -44,7 +44,10 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e) Grocy.Api.Get('stock/get-product-details/' + productId, function (productDetails) { - $('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name); + if (!$("#only_check_single_unit_in_stock").is(":checked")) + { + $("#qu_id").val(productDetails.quantity_unit_stock.id); + } $('#amount').focus(); Grocy.FrontendHelpers.ValidateForm('recipe-pos-form'); }, @@ -76,12 +79,12 @@ $('#amount').on('focus', function(e) } }); -$('#recipe-pos-form input').keyup(function (event) +$('#recipe-pos-form input').keyup(function(event) { Grocy.FrontendHelpers.ValidateForm('recipe-pos-form'); }); -$('#recipe-pos-form input').keydown(function (event) +$('#recipe-pos-form input').keydown(function(event) { if (event.keyCode === 13) //Enter { @@ -96,3 +99,16 @@ $('#recipe-pos-form input').keydown(function (event) } } }); + +$("#only_check_single_unit_in_stock").on("click", function() +{ + if (this.checked) + { + $("#qu_id").removeAttr("disabled"); + } + else + { + $("#qu_id").attr("disabled", ""); + Grocy.Components.ProductPicker.GetPicker().trigger("change"); + } +}); diff --git a/services/DemoDataGeneratorService.php b/services/DemoDataGeneratorService.php index f469d272..208c60a0 100644 --- a/services/DemoDataGeneratorService.php +++ b/services/DemoDataGeneratorService.php @@ -21,14 +21,15 @@ class DemoDataGeneratorService extends BaseService INSERT INTO users (username, password) VALUES ('{$localizationService->Localize('Demo User')} 3', 'x'); INSERT INTO users (username, password) VALUES ('{$localizationService->Localize('Demo User')} 4', 'x'); - INSERT INTO locations (name) VALUES ('{$localizationService->Localize('Pantry')}'); --2 - INSERT INTO locations (name) VALUES ('{$localizationService->Localize('Candy cupboard')}'); --3 - INSERT INTO locations (name) VALUES ('{$localizationService->Localize('Tinned food cupboard')}'); --4 + INSERT INTO locations (name) VALUES ('{$localizationService->Localize('Pantry')}'); --3 + INSERT INTO locations (name) VALUES ('{$localizationService->Localize('Candy cupboard')}'); --4 + INSERT INTO locations (name) VALUES ('{$localizationService->Localize('Tinned food cupboard')}'); --5 INSERT INTO quantity_units (name, name_plural) VALUES ('{$localizationService->Localize('Glass')}', '{$localizationService->Localize('Glasses')}'); --4 INSERT INTO quantity_units (name, name_plural) VALUES ('{$localizationService->Localize('Tin')}', '{$localizationService->Localize('Tins')}'); --5 INSERT INTO quantity_units (name, name_plural) VALUES ('{$localizationService->Localize('Can')}', '{$localizationService->Localize('Cans')}'); --6 INSERT INTO quantity_units (name, name_plural) VALUES ('{$localizationService->Localize('Bunch')}', '{$localizationService->Localize('Bunches')}'); --7 + INSERT INTO quantity_units (name, name_plural) VALUES ('{$localizationService->Localize('Gram')}', '{$localizationService->Localize('Grams')}'); --8 DELETE FROM sqlite_sequence WHERE name = 'products'; --Just to keep IDs in order as mentioned here... INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock, min_stock_amount) VALUES ('{$localizationService->Localize('Cookies')}', 3, 3, 3, 1, 8); --1 @@ -51,6 +52,8 @@ class DemoDataGeneratorService extends BaseService INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('{$localizationService->Localize('Salami')}', 2, 3, 3, 1); --18 INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('{$localizationService->Localize('Toast')}', 4, 5, 5, 1); --19 INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('{$localizationService->Localize('Minced meat')}', 2, 3, 3, 1); --20 + INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('{$localizationService->Localize('Flour')}', 2, 3, 3, 1); --21 + INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('{$localizationService->Localize('Sugar')}', 3, 3, 3, 1); --22 INSERT INTO shopping_list (note, amount) VALUES ('{$localizationService->Localize('Some good snacks')}', 1); INSERT INTO shopping_list (product_id, amount) VALUES (20, 1); @@ -59,6 +62,7 @@ class DemoDataGeneratorService extends BaseService INSERT INTO recipes (name, description) VALUES ('{$localizationService->Localize('Pizza')}', '{$loremIpsum}'); --1 INSERT INTO recipes (name, description) VALUES ('{$localizationService->Localize('Spaghetti bolognese')}', '{$loremIpsum}'); --2 INSERT INTO recipes (name, description) VALUES ('{$localizationService->Localize('Sandwiches')}', '{$loremIpsum}'); --3 + INSERT INTO recipes (name, description) VALUES ('{$localizationService->Localize('Pancackes')}', '{$loremIpsum}'); --4 INSERT INTO recipes_pos (recipe_id, product_id, amount) VALUES (1, 16, 1); INSERT INTO recipes_pos (recipe_id, product_id, amount) VALUES (1, 17, 1); @@ -70,6 +74,9 @@ class DemoDataGeneratorService extends BaseService INSERT INTO recipes_pos (recipe_id, product_id, amount) VALUES (2, 20, 1); INSERT INTO recipes_pos (recipe_id, product_id, amount) VALUES (3, 10, 1); INSERT INTO recipes_pos (recipe_id, product_id, amount) VALUES (3, 11, 1); + INSERT INTO recipes_pos (recipe_id, product_id, amount) VALUES (4, 5, 4); + INSERT INTO recipes_pos (recipe_id, product_id, amount, qu_id, only_check_single_unit_in_stock) VALUES (4, 21, 200, 8, 1); + INSERT INTO recipes_pos (recipe_id, product_id, amount, qu_id, only_check_single_unit_in_stock) VALUES (4, 22, 200, 8, 1); INSERT INTO habits (name, period_type, period_days) VALUES ('{$localizationService->Localize('Changed towels in the bathroom')}', 'manually', 5); --1 INSERT INTO habits (name, period_type, period_days) VALUES ('{$localizationService->Localize('Cleaned the kitchen floor')}', 'dynamic-regular', 7); --2 @@ -151,6 +158,10 @@ class DemoDataGeneratorService extends BaseService $stockService->AddProduct(15, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice()); $stockService->AddProduct(15, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice()); $stockService->AddProduct(15, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice()); + $stockService->AddProduct(21, 1, date('Y-m-d', strtotime('+200 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice()); + $stockService->AddProduct(21, 1, date('Y-m-d', strtotime('+200 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice()); + $stockService->AddProduct(22, 1, date('Y-m-d', strtotime('+200 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice()); + $stockService->AddProduct(22, 1, date('Y-m-d', strtotime('+200 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice()); $stockService->AddMissingProductsToShoppingList(); $habitsService = new HabitsService(); diff --git a/views/recipeform.blade.php b/views/recipeform.blade.php index 37b0cce0..07128fda 100644 --- a/views/recipeform.blade.php +++ b/views/recipeform.blade.php @@ -76,7 +76,7 @@ {{ FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id)->name }} - {{ $recipePosition->amount }} {{ Pluralize($recipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id)->qu_id_stock)->name_plural) }} + {{ $recipePosition->amount }} {{ Pluralize($recipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural) }} @if(FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $recipePosition->id)->need_fulfilled == 1) {{ $L('Enough in stock') }} @else {{ $L('Not enough in stock, #1 missing, #2 already on shopping list', FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $recipePosition->id)->missing_amount, FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $recipePosition->id)->amount_on_shopping_list) }} @endif diff --git a/views/recipeposform.blade.php b/views/recipeposform.blade.php index 99045cff..cfe60726 100644 --- a/views/recipeposform.blade.php +++ b/views/recipeposform.blade.php @@ -32,10 +32,34 @@ 'prefillByName' => $prefillByName )) -
- - -
{{ $L('This cannot be negative') }}
+
+
+
+
+ + +
{{ $L('This cannot be negative') }}
+
+
+ + +
{{ $L('A quantity unit is required') }}
+
+
+
+
+
+ + only_check_single_unit_in_stock == 1) checked @endif class="form-check-input" type="checkbox" id="only_check_single_unit_in_stock" name="only_check_single_unit_in_stock" value="1"> + +
+
+
+
diff --git a/views/recipes.blade.php b/views/recipes.blade.php index f2c414a5..195f6fba 100644 --- a/views/recipes.blade.php +++ b/views/recipes.blade.php @@ -71,7 +71,7 @@