mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 17:45:39 +00:00
Implement that recipe ingredients can have arbitrary quantity units (references #32)
This commit is contained in:
parent
3e73a44576
commit
71b9d11ff5
@ -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')
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -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'
|
||||
);
|
||||
|
41
migrations/0034.sql
Normal file
41
migrations/0034.sql
Normal file
@ -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;
|
@ -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");
|
||||
}
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -76,7 +76,7 @@
|
||||
{{ FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id)->name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ $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) }}
|
||||
<span class="timeago-contextual">@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</span>
|
||||
</td>
|
||||
<td class="fit-content">
|
||||
|
@ -32,10 +32,34 @@
|
||||
'prefillByName' => $prefillByName
|
||||
))
|
||||
|
||||
<div class="form-group">
|
||||
<label for="amount">{{ $L('Amount') }} <span id="amount_qu_unit" class="small text-muted"></span></label>
|
||||
<input type="number" class="form-control" id="amount" name="amount" value="@if($mode == 'edit'){{ $recipePos->amount }}@else{{1}}@endif" min="0" required>
|
||||
<div class="invalid-feedback">{{ $L('This cannot be negative') }}</div>
|
||||
<div class="form-group row">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="form-group col-4">
|
||||
<label for="amount">{{ $L('Amount') }}</label>
|
||||
<input type="number" class="form-control" id="amount" name="amount" value="@if($mode == 'edit'){{ $recipePos->amount }}@else{{1}}@endif" min="0" required>
|
||||
<div class="invalid-feedback">{{ $L('This cannot be negative') }}</div>
|
||||
</div>
|
||||
<div class="form-group col-8">
|
||||
<label for="qu_id">{{ $L('Quantity unit') }}</label>
|
||||
<select required @if($mode == 'create' || ($mode == 'edit' && $recipePos->only_check_single_unit_in_stock != 1)) disabled @endif class="form-control" id="qu_id" name="qu_id">
|
||||
@foreach($quantityUnits as $quantityunit)
|
||||
<option @if($mode == 'edit' && $quantityunit->id == $recipePos->qu_id) selected @endif value="{{ $quantityunit->id }}">{{ $quantityunit->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="invalid-feedback">{{ $L('A quantity unit is required') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="form-check">
|
||||
<input type="hidden" name="only_check_single_unit_in_stock" value="0">
|
||||
<input @if($mode == 'edit' && $recipePos->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">
|
||||
<label class="form-check-label" for="only_check_single_unit_in_stock">{{ $L('Only check if a single unit is in stock (a different quantity can then be used above)') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -71,7 +71,7 @@
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach($selectedRecipePositions as $selectedRecipePosition)
|
||||
<li class="list-group-item">
|
||||
{{ $selectedRecipePosition->amount }} {{ Pluralize($selectedRecipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->qu_id_stock)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }}
|
||||
{{ $selectedRecipePosition->amount }} {{ Pluralize($selectedRecipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }}
|
||||
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $selectedRecipePosition->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', $selectedRecipePosition->id)->missing_amount, FindObjectInArrayByPropertyValue($recipesFulfillment, 'recipe_pos_id', $selectedRecipePosition->id)->amount_on_shopping_list) }} @endif</span>
|
||||
|
||||
@if(!empty($selectedRecipePosition->note))
|
||||
|
Loading…
x
Reference in New Issue
Block a user