diff --git a/changelog/52_UNRELEASED_2019-xx-xx.md b/changelog/52_UNRELEASED_2019-xx-xx.md index fbd7bbda..1b74ca60 100644 --- a/changelog/52_UNRELEASED_2019-xx-xx.md +++ b/changelog/52_UNRELEASED_2019-xx-xx.md @@ -1,12 +1,16 @@ - Stock improvements - - Products can now have variations + - Products can now have variations (nested products) - Define the parent product for a product on the product edit page (only one level is possible, means a product which is used as a parent product in another product, cannot have a parent product itself) - Parent and sub products can have stock (both are regular products, no difference from that side) - On the stock overview page the aggregated amount is displayed next to the amount (sigma sign) - When a recipe needs a parent product, the need is also fulfilled when enough sub product(s) are in stock - - API change (no breaking change): `/stock/products/{productId}` returns additional fields for the aggregated amount(s): `stock_amount_aggregated` and `stock_amount_opened_aggregated` - contains the same for "normal" products, `is_aggregated_amount` indicates if aggregation has happened + - Quantity units can now be linked (related measurements / unit conversion) + - On the quantity unit edit page default conversion can be defined for each unit + - Products "inherit" the default conversion and additionally can have their own / override the default ones - It's now possible to print a "Location Content Sheet" with the current stock per location - new button at the top of the stock overview page - The product description now can have formattings (HTML/WYSIWYG editor like for recipes) +- Recipe improvements + - Based on the new linked quantity units, recipe ingredients can now use any product related unit, the amount is calculated according to the cnoversion factor of the unit relation - Chores improvements - New option "Due date rollover" per chore which means the chore can never be overdue, the due date will shift forward each day when due - Equipment improvements/fixes @@ -15,10 +19,11 @@ - Improved the handling which entry page to use with disabled feature flags (thanks @nielstholenaar) - Fixed that the Userfield type "Preset list" had always the caption "Product group" instead of the configured one (thanks @oncleben31) - Userfields of type "checkbox" are rendered as a checkmark in tables when checked (instead of "1" as till now) -- API improvements - - New endpoint `/stock/shoppinglist/add-product` to add a product to a shopping list (thanks @Forceu) +- API improvements & non-breaking changes + - New endpoint `/stock/shoppinglist/add-product` to add a product to a shopping list (thanks @Forceu) - New endpoint `/stock/shoppinglist/remove-product` to remove a product from a shopping list (thanks @Forceu) - When adding a product (through `stock/product/{productId}/add` or `stock/product/{productId}/inventory`) with omitted best before date and if the given product has "Default best before days" set, the best before date is calculated based on that (so far always today was used which is still the case when no date is supplied and also the product has no "Default best before days set) (thanks @Forceu) - Field `stock_amount` of endpoint `/stock/products/{productId}´ now returns `0` instead of `null` when the given product is not in stock (thanks @Forceu) - New endpoint `/objects/{entity}/search/{searchString}` search for objects by name (contains search) - It's now also possible to provide the API key via a query parameter (same name as the header, so `GROCY-API-KEY`) + - `/stock/products/{productId}` returns additional fields for the aggregated amount(s): `stock_amount_aggregated` and `stock_amount_opened_aggregated` - contains the same for "normal" products, `is_aggregated_amount` indicates if aggregation has happened diff --git a/controllers/RecipesController.php b/controllers/RecipesController.php index fc6d2921..be309aaf 100644 --- a/controllers/RecipesController.php +++ b/controllers/RecipesController.php @@ -69,7 +69,8 @@ class RecipesController extends BaseController 'includedRecipeIdsAbsolute' => $includedRecipeIdsAbsolute, 'selectedRecipeTotalCosts' => FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $selectedRecipe->id)->costs, 'userfields' => $this->UserfieldsService->GetFields('recipes'), - 'userfieldValues' => $this->UserfieldsService->GetAllValues('recipes') + 'userfieldValues' => $this->UserfieldsService->GetAllValues('recipes'), + 'quantityUnitConversionsResolved' => $this->Database->quantity_unit_conversions_resolved() ]); } @@ -96,7 +97,8 @@ class RecipesController extends BaseController 'recipesResolved' => $this->RecipesService->GetRecipesResolved(), 'recipes' => $this->Database->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->orderBy('name'), 'recipeNestings' => $this->Database->recipes_nestings()->where('recipe_id', $recipeId), - 'userfields' => $this->UserfieldsService->GetFields('recipes') + 'userfields' => $this->UserfieldsService->GetFields('recipes'), + 'quantityUnitConversionsResolved' => $this->Database->quantity_unit_conversions_resolved() ]); } @@ -108,7 +110,8 @@ class RecipesController extends BaseController 'mode' => 'create', 'recipe' => $this->Database->recipes($args['recipeId']), 'products' => $this->Database->products()->orderBy('name'), - 'quantityUnits' => $this->Database->quantity_units()->orderBy('name') + 'quantityUnits' => $this->Database->quantity_units()->orderBy('name'), + 'quantityUnitConversionsResolved' => $this->Database->quantity_unit_conversions_resolved() ]); } else @@ -118,7 +121,8 @@ class RecipesController extends BaseController 'recipe' => $this->Database->recipes($args['recipeId']), 'recipePos' => $this->Database->recipes_pos($args['recipePosId']), 'products' => $this->Database->products()->orderBy('name'), - 'quantityUnits' => $this->Database->quantity_units()->orderBy('name') + 'quantityUnits' => $this->Database->quantity_units()->orderBy('name'), + 'quantityUnitConversionsResolved' => $this->Database->quantity_unit_conversions_resolved() ]); } } diff --git a/localization/demo_data.pot b/localization/demo_data.pot index e88e3f38..0a1eb9bb 100644 --- a/localization/demo_data.pot +++ b/localization/demo_data.pot @@ -283,3 +283,8 @@ msgstr "" msgid "Dark Chocolate" msgstr "" + +msgid "Slice" +msgid_plural "Slices" +msgstr[0] "" +msgstr[1] "" diff --git a/localization/strings.pot b/localization/strings.pot index 73035a48..fe783d05 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -1323,19 +1323,28 @@ msgstr "" msgid "Not possible because this product is already used as a parent product in another product" msgstr "" +msgid "Default conversions" +msgstr "" + +msgid "Factor" +msgstr "" + +msgid "1 %s is the same as..." +msgstr "" + msgid "Create QU conversion" msgstr "" +msgid "Default for QU unit" +msgstr "" + msgid "Quantity unit from" msgstr "" msgid "Quantity unit to" msgstr "" -msgid "The amount cannot be lower than %s and must be a valid number" -msgstr "" - -msgid "Factor" +msgid "This cannot be lower than %1$s and must be a valid number with max. %2$s decimal places" msgstr "" msgid "This cannot be equal to %s" @@ -1344,29 +1353,14 @@ msgstr "" msgid "This means 1 %1$s is the same as %2$s %3$s" msgstr "" -msgid "Edit QU conversion" -msgstr "" - -msgid "For product" -msgstr "" - -msgid "Default for QU unit" -msgstr "" - -msgid "Override for product" -msgstr "" - -msgid "Default conversions" -msgstr "" - -msgid "1 %s is the same as..." -msgstr "" - -msgid "Are you sure to remove this conversion?" -msgstr "" - msgid "QU conversions" msgstr "" msgid "Product overrides" msgstr "" + +msgid "Override for product" +msgstr "" + +msgid "This equals %1$s %2$s in stock" +msgstr "" diff --git a/migrations/0082.sql b/migrations/0082.sql index 95a467b1..23e56cee 100644 --- a/migrations/0082.sql +++ b/migrations/0082.sql @@ -50,34 +50,55 @@ CREATE VIEW quantity_unit_conversions_resolved AS -- First: Product "purchase to stock" conversion factor SELECT + p.id AS id, -- Dummy, LessQL needs an id column p.id AS product_id, p.qu_id_purchase AS from_qu_id, + qu_from.name AS from_qu_name, p.qu_id_stock AS to_qu_id, + qu_to.name AS to_qu_name, p.qu_factor_purchase_to_stock AS factor FROM products p +JOIN quantity_units qu_from + ON p.qu_id_purchase = qu_from.id +JOIN quantity_units qu_to + ON p.qu_id_stock = qu_to.id UNION -- Second: Product specific overrides SELECT +p.id AS id, -- Dummy, LessQL needs an id column p.id AS product_id, - p.qu_id_stock AS from_qu_id, + quc.from_qu_id AS from_qu_id, + qu_from.name AS from_qu_name, quc.to_qu_id AS to_qu_id, + qu_to.name AS to_qu_name, quc.factor AS factor FROM products p JOIN quantity_unit_conversions quc ON p.qu_id_stock = quc.from_qu_id AND p.id = quc.product_id +JOIN quantity_units qu_from + ON quc.from_qu_id = qu_from.id +JOIN quantity_units qu_to + ON quc.to_qu_id = qu_to.id UNION -- Third: Default quantity unit conversion factors SELECT +p.id AS id, -- Dummy, LessQL needs an id column p.id AS product_id, p.qu_id_stock AS from_qu_id, + qu_from.name AS from_qu_name, quc.to_qu_id AS to_qu_id, + qu_to.name AS to_qu_name, quc.factor AS factor FROM products p JOIN quantity_unit_conversions quc ON p.qu_id_stock = quc.from_qu_id - AND quc.product_id IS NULL; + AND quc.product_id IS NULL +JOIN quantity_units qu_from + ON quc.from_qu_id = qu_from.id +JOIN quantity_units qu_to + ON quc.to_qu_id = qu_to.id; diff --git a/public/js/extensions.js b/public/js/extensions.js index 269a2c88..c67f92be 100644 --- a/public/js/extensions.js +++ b/public/js/extensions.js @@ -97,3 +97,18 @@ FindObjectInArrayByPropertyValue = function(array, propertyName, propertyValue) return null; } + +FindAllObjectsInArrayByPropertyValue = function(array, propertyName, propertyValue) +{ + var returnArray = []; + + for (var i = 0; i < array.length; i++) + { + if (array[i][propertyName] == propertyValue) + { + returnArray.push(array[i]); + } + } + + return returnArray; +} diff --git a/public/viewjs/components/productamountpicker.js b/public/viewjs/components/productamountpicker.js new file mode 100644 index 00000000..7e60c5b2 --- /dev/null +++ b/public/viewjs/components/productamountpicker.js @@ -0,0 +1,75 @@ +Grocy.Components.ProductAmountPicker = {}; +Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = false; + +Grocy.Components.ProductAmountPicker.Reload = function(productId, destinationQuId, forceInitialDisplayQu = false) +{ + if (!Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled) + { + var conversionsForProduct = FindAllObjectsInArrayByPropertyValue(Grocy.QuantityUnitConversionsResolved, 'product_id', productId); + $("#qu_id").find("option").remove().end(); + $("#qu_id").attr("data-destination-qu-name", FindObjectInArrayByPropertyValue(Grocy.QuantityUnits, 'id', destinationQuId).name); + conversionsForProduct.forEach(conversion => + { + $("#qu_id").append(''); + }); + } + + if (!Grocy.Components.ProductAmountPicker.InitalValueSet || forceInitialDisplayQu) + { + $("#qu_id").val($("#qu_id").attr("data-inital-qu-id")); + } + + if (!Grocy.Components.ProductAmountPicker.InitalValueSet) + { + var convertedAmount = $("#display_amount").val() * $("#qu_id option:selected").attr("data-qu-factor"); + $("#display_amount").val(convertedAmount); + + Grocy.Components.ProductAmountPicker.InitalValueSet = true; + } + + $(".input-group-productamountpicker").trigger("change"); +} + +Grocy.Components.ProductAmountPicker.SetQuantityUnit = function() +{ + $("#qu_id").val(quId); +} + +Grocy.Components.ProductAmountPicker.AllowAnyQu = function(quId) +{ + Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = true; + + $("#qu_id").find("option").remove().end(); + Grocy.QuantityUnits.forEach(qu => + { + $("#qu_id").append(''); + }); + + $(".input-group-productamountpicker").trigger("change"); +} + +$(".input-group-productamountpicker").on("change", function() +{ + var destinationQuName = $("#qu_id").attr("data-destination-qu-name"); + var selectedQuName = $("#qu_id option:selected").text(); + var quFactor = $("#qu_id option:selected").attr("data-qu-factor"); + var amount = $("#display_amount").val(); + var destinationAmount = amount / quFactor; + + if (destinationQuName == selectedQuName || Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled) + { + $("#qu-conversion-info").addClass("d-none"); + } + else + { + $("#qu-conversion-info").removeClass("d-none"); + $("#qu-conversion-info").text(__t("This equals %1$s %2$s in stock", destinationAmount.toLocaleString(), destinationQuName)); + } + + $("#amount").val(destinationAmount); +}); + +$("#display_amount").on("keyup", function() +{ + $(".input-group-productamountpicker").trigger("change"); +}); diff --git a/public/viewjs/productform.js b/public/viewjs/productform.js index 6ed5766b..a30932ba 100644 --- a/public/viewjs/productform.js +++ b/public/viewjs/productform.js @@ -55,7 +55,7 @@ } else { - window.location.href = redirectDestination; + window.location.href = redirectDestination.replace("editobjectid", Grocy.EditObjectId);; } }, function (xhr) @@ -73,7 +73,7 @@ } else { - window.location.href = redirectDestination; + window.location.href = redirectDestination.replace("editobjectid", Grocy.EditObjectId);; } } }); @@ -118,7 +118,7 @@ } else { - window.location.href = redirectDestination; + window.location.href = redirectDestination.replace("editobjectid", Grocy.EditObjectId);; } }, function(xhr) @@ -136,7 +136,7 @@ } else { - window.location.href = redirectDestination; + window.location.href = redirectDestination.replace("editobjectid", Grocy.EditObjectId);; } } }); @@ -233,6 +233,29 @@ $('.input-group-qu').on('change', function(e) $('#product-form input').keyup(function(event) { Grocy.FrontendHelpers.ValidateForm('product-form'); + + if (document.getElementById('product-form').checkValidity() === false) //There is at least one validation error + { + $("#qu-conversion-add-button").addClass("disabled"); + } + else + { + $("#qu-conversion-add-button").removeClass("disabled"); + } +}); + +$('#product-form select').change(function(event) +{ + Grocy.FrontendHelpers.ValidateForm('product-form'); + + if (document.getElementById('product-form').checkValidity() === false) //There is at least one validation error + { + $("#qu-conversion-add-button").addClass("disabled"); + } + else + { + $("#qu-conversion-add-button").removeClass("disabled"); + } }); $('#location_id').change(function(event) @@ -343,6 +366,7 @@ $('#qu-conversions-table tbody').removeClass("d-none"); quConversionsTable.columns.adjust().draw(); Grocy.Components.UserfieldsForm.Load(); +$("#name").trigger("keyup"); $('#name').focus(); $('.input-group-qu').trigger('change'); Grocy.FrontendHelpers.ValidateForm('product-form'); @@ -390,12 +414,12 @@ $(document).on('click', '.qu-conversion-delete-button', function(e) $(document).on('click', '.qu-conversion-edit-button', function (e) { var id = $(e.currentTarget).attr('data-qu-conversion-id'); - Grocy.ProductEditFormRedirectUri = U("/quantityunitconversion/" + id.toString() + "?product=" + Grocy.EditObjectId.toString()); + Grocy.ProductEditFormRedirectUri = U("/quantityunitconversion/" + id.toString() + "?product=editobjectid"); $('#save-product-button').click(); }); $("#qu-conversion-add-button").on("click", function(e) { - Grocy.ProductEditFormRedirectUri = U("/quantityunitconversion/new?product=" + Grocy.EditObjectId.toString()); + Grocy.ProductEditFormRedirectUri = U("/quantityunitconversion/new?product=editobjectid"); $('#save-product-button').click(); }); diff --git a/public/viewjs/quantityunitform.js b/public/viewjs/quantityunitform.js index fcd6b78c..ff33755e 100644 --- a/public/viewjs/quantityunitform.js +++ b/public/viewjs/quantityunitform.js @@ -5,6 +5,15 @@ var jsonData = $('#quantityunit-form').serializeJSON(); Grocy.FrontendHelpers.BeginUiBusy("quantityunit-form"); + if (Grocy.QuantityUnitEditFormRedirectUri !== undefined) + { + redirectDestination = Grocy.QuantityUnitEditFormRedirectUri; + } + else + { + redirectDestination = U('/quantityunits'); + } + if (Grocy.EditMode === 'create') { Grocy.Api.Post('objects/quantity_units', jsonData, @@ -13,7 +22,14 @@ Grocy.EditObjectId = result.created_object_id; Grocy.Components.UserfieldsForm.Save(function() { - window.location.href = U('/quantityunits'); + if (redirectDestination == "reload") + { + window.location.reload(); + } + else + { + window.location.href = redirectDestination.replace("editobjectid", Grocy.EditObjectId); + } }); }, function(xhr) @@ -30,7 +46,14 @@ { Grocy.Components.UserfieldsForm.Save(function() { - window.location.href = U('/quantityunits'); + if (redirectDestination == "reload") + { + window.location.reload(); + } + else + { + window.location.href = redirectDestination.replace("editobjectid", Grocy.EditObjectId); + } }); }, function(xhr) @@ -44,6 +67,24 @@ $('#quantityunit-form input').keyup(function(event) { + if (!$("#name").val().isEmpty()) + { + $("#qu-conversion-headline-info").text(__t('1 %s is the same as...', $("#name").val())); + } + else + { + $("#qu-conversion-headline-info").text(""); + } + + if (document.getElementById('quantityunit-form').checkValidity() === false) //There is at least one validation error + { + $("#qu-conversion-add-button").addClass("disabled"); + } + else + { + $("#qu-conversion-add-button").removeClass("disabled"); + } + Grocy.FrontendHelpers.ValidateForm('quantityunit-form'); }); @@ -88,6 +129,7 @@ $('#qu-conversions-table tbody').removeClass("d-none"); quConversionsTable.columns.adjust().draw(); Grocy.Components.UserfieldsForm.Load(); +$("#name").trigger("keyup"); $('#name').focus(); Grocy.FrontendHelpers.ValidateForm('quantityunit-form'); @@ -114,7 +156,8 @@ $(document).on('click', '.qu-conversion-delete-button', function(e) Grocy.Api.Delete('objects/quantity_unit_conversions/' + objectId, { }, function(result) { - window.location.reload(); + Grocy.QuantityUnitEditFormRedirectUri = "reload"; + $('#save-quantityunit-button').click(); }, function(xhr) { @@ -129,29 +172,12 @@ $(document).on('click', '.qu-conversion-delete-button', function(e) $(document).on('click', '.qu-conversion-edit-button', function (e) { var id = $(e.currentTarget).attr('data-qu-conversion-id'); - - Grocy.Api.Put('objects/quantity_units/' + Grocy.EditObjectId, $('#quantityunit-form').serializeJSON(), - function(result) - { - window.location.href = U("/quantityunitconversion/" + id.toString() + "?qu-unit=" + Grocy.EditObjectId.toString()); - }, - function(xhr) - { - console.error(xhr); - } - ); + Grocy.QuantityUnitEditFormRedirectUri = U("/quantityunitconversion/" + id.toString() + "?qu-unit=editobjectid"); + $('#save-quantityunit-button').click(); }); $("#qu-conversion-add-button").on("click", function(e) { - Grocy.Api.Put('objects/quantity_units/' + Grocy.EditObjectId, $('#quantityunit-form').serializeJSON(), - function(result) - { - window.location.href = U("/quantityunitconversion/new?qu-unit=" + Grocy.EditObjectId.toString()); - }, - function(xhr) - { - console.error(xhr); - } - ); + Grocy.QuantityUnitEditFormRedirectUri = U("/quantityunitconversion/new?qu-unit=editobjectid"); + $('#save-quantityunit-button').click(); }); diff --git a/public/viewjs/recipeposform.js b/public/viewjs/recipeposform.js index 6320191b..e6d8ed1e 100644 --- a/public/viewjs/recipeposform.js +++ b/public/viewjs/recipeposform.js @@ -1,9 +1,12 @@ -$('#save-recipe-pos-button').on('click', function(e) +Grocy.RecipePosFormProductChangeCount = 0; + +$('#save-recipe-pos-button').on('click', function (e) { e.preventDefault(); var jsonData = $('#recipe-pos-form').serializeJSON({ checkboxUncheckedValue: "0" }); jsonData.recipe_id = Grocy.EditObjectParentId; + delete jsonData.display_amount; Grocy.FrontendHelpers.BeginUiBusy("recipe-pos-form"); @@ -44,31 +47,37 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e) if (productId) { Grocy.Components.ProductCard.Refresh(productId); - + Grocy.Api.Get('stock/products/' + productId, function(productDetails) { - if (!$("#only_check_single_unit_in_stock").is(":checked")) + Grocy.RecipePosFormProductChangeCount++; + console.log(Grocy.RecipePosFormProductChangeCount); + if (Grocy.RecipePosFormProductChangeCount < 3) // This triggers twice on inital page load, however { - $("#qu_id").val(productDetails.quantity_unit_stock.id); + Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id, true); + } + else + { + Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id); } if (productDetails.product.allow_partial_units_in_stock == 1) { - $("#amount").attr("min", "0.01"); - $("#amount").attr("step", "0.01"); - $("#amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', 0.01.toLocaleString())); + $("#display_amount").attr("min", "0.01"); + $("#display_amount").attr("step", "0.01"); + $("#display_amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', 0.01.toLocaleString())); } else { - $("#amount").attr("min", "1"); - $("#amount").attr("step", "1"); - $("#amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', '1')); + $("#display_amount").attr("min", "1"); + $("#display_amount").attr("step", "1"); + $("#display_amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', '1')); } $("#not_check_stock_fulfillment").prop("checked", productDetails.product.not_check_stock_fulfillment_for_recipes == 1); - $('#amount').focus(); + $('#display_amount').focus(); Grocy.FrontendHelpers.ValidateForm('recipe-pos-form'); }, function(xhr) @@ -87,7 +96,7 @@ if (Grocy.Components.ProductPicker.InProductAddWorkflow() === false) } Grocy.Components.ProductPicker.GetPicker().trigger('change'); -$('#amount').on('focus', function(e) +$('#display_amount').on('focus', function(e) { if (Grocy.Components.ProductPicker.GetValue().length === 0) { @@ -125,19 +134,19 @@ $("#only_check_single_unit_in_stock").on("click", function() { if (this.checked) { - $("#qu_id").removeAttr("disabled"); - $("#amount").attr("min", "0.01"); - $("#amount").attr("step", "0.01"); - $("#amount").parent().find(".invalid-feedback").text(__t("This cannot be negative")); + $("#display_amount").attr("min", "0.01"); + $("#display_amount").attr("step", "0.01"); + $("#display_amount").parent().find(".invalid-feedback").text(__t("This cannot be negative")); + Grocy.Components.ProductAmountPicker.AllowAnyQu(); Grocy.FrontendHelpers.ValidateForm("recipe-pos-form"); } else { - $("#qu_id").attr("disabled", ""); - $("#amount").attr("min", "0"); - $("#amount").attr("step", "1"); + $("#display_amount").attr("min", "0"); + $("#display_amount").attr("step", "1"); Grocy.Components.ProductPicker.GetPicker().trigger("change"); // Selects the default quantity unit of the selected product - $("#amount").parent().find(".invalid-feedback").text(__t("This cannot be negative and must be an integral number")); + $("#display_amount").parent().find(".invalid-feedback").text(__t("This cannot be negative and must be an integral number")); + Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = false; Grocy.FrontendHelpers.ValidateForm("recipe-pos-form"); } }); diff --git a/services/DemoDataGeneratorService.php b/services/DemoDataGeneratorService.php index 09091ddf..5074f76a 100644 --- a/services/DemoDataGeneratorService.php +++ b/services/DemoDataGeneratorService.php @@ -48,6 +48,7 @@ class DemoDataGeneratorService extends BaseService INSERT INTO quantity_units (name, name_plural) VALUES ('{$this->__n_sql(1, 'Liter', 'Liters')}', '{$this->__n_sql(2, 'Liter', 'Liters')}'); --9 INSERT INTO quantity_units (name, name_plural) VALUES ('{$this->__n_sql(1, 'Bottle', 'Bottles')}', '{$this->__n_sql(2, 'Bottle', 'Bottles')}'); --10 INSERT INTO quantity_units (name, name_plural) VALUES ('{$this->__n_sql(1, 'Milliliter', 'Milliliters')}', '{$this->__n_sql(2, 'Milliliter', 'Milliliters')}'); --11 + INSERT INTO quantity_units (name, name_plural) VALUES ('{$this->__n_sql(1, 'Slice', 'Slices')}', '{$this->__n_sql(2, 'Slice', 'Slices')}'); --12 INSERT INTO product_groups(name) VALUES ('01 {$this->__t_sql('Sweets')}'); --1 INSERT INTO product_groups(name) VALUES ('02 {$this->__t_sql('Bakery products')}'); --2 @@ -83,6 +84,8 @@ class DemoDataGeneratorService extends BaseService INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock, product_group_id, parent_product_id) VALUES ('{$this->__t_sql('Milk Chocolate')}', 4, 3, 3, 1, 1, 2); --24 INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock, product_group_id, parent_product_id) VALUES ('{$this->__t_sql('Dark Chocolate')}', 4, 3, 3, 1, 1, 2); --25 + INSERT INTO quantity_unit_conversions (from_qu_id, to_qu_id, factor, product_id) VALUES (3, 12, 10, 10); + INSERT INTO shopping_list (note, amount) VALUES ('{$this->__t_sql('Some good snacks')}', 1); INSERT INTO shopping_list (product_id, amount) VALUES (20, 1); INSERT INTO shopping_list (product_id, amount) VALUES (17, 1); @@ -102,7 +105,7 @@ class DemoDataGeneratorService extends BaseService INSERT INTO recipes_pos (recipe_id, product_id, amount) VALUES (2, 10, 1); INSERT INTO recipes_pos (recipe_id, product_id, amount, note) VALUES (2, 17, 1, '{$this->__t_sql('This is the note content of the recipe ingredient')}'); 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, qu_id) VALUES (3, 10, 0.2, 12); 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); diff --git a/views/components/productamountpicker.blade.php b/views/components/productamountpicker.blade.php new file mode 100644 index 00000000..10c245e7 --- /dev/null +++ b/views/components/productamountpicker.blade.php @@ -0,0 +1,34 @@ +@push('componentScripts') + +@endpush + +@php if(empty($additionalGroupCssClasses)) { $additionalGroupCssClasses = ''; } @endphp + +
+ @php
+ $product = FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id);
+ $productQuConversions = FindAllObjectsInArrayByPropertyValue($quantityUnitConversionsResolved, 'product_id', $product->id);
+ $productQuConversions = FindAllObjectsInArrayByPropertyValue($productQuConversions, 'from_qu_id', $product->qu_id_stock);
+ $productQuConversion = FindObjectInArrayByPropertyValue($productQuConversions, 'to_qu_id', $recipePosition->qu_id);
+ if ($productQuConversion)
+ {
+ $recipePosition->amount = $recipePosition->amount * $productQuConversion->factor;
+ }
+ @endphp
@if(!empty($recipePosition->variable_amount))
{{ $recipePosition->variable_amount }}
@else
diff --git a/views/recipeposform.blade.php b/views/recipeposform.blade.php
index 7fa5e41f..a07dbfff 100644
--- a/views/recipeposform.blade.php
+++ b/views/recipeposform.blade.php
@@ -17,6 +17,9 @@
@if($mode == 'edit')
@@ -32,39 +35,20 @@
'prefillByName' => $prefillByName
))
-
+ @php if($mode == 'edit') { $value = $recipePos->amount; } else { $value = 1; } @endphp
+ @php if($mode == 'edit') { $initialQuId = $recipePos->qu_id; } else { $initialQuId = ''; } @endphp
+ @include('components.productamountpicker', array(
+ 'value' => $value,
+ 'initialQuId' => $initialQuId,
+ 'additionalGroupCssClasses' => 'mb-0'
+ ))
+
+
-
diff --git a/views/recipes.blade.php b/views/recipes.blade.php
index c4972dc9..80d2ead4 100644
--- a/views/recipes.blade.php
+++ b/views/recipes.blade.php
@@ -5,6 +5,11 @@
@section('viewJsName', 'recipes')
@section('content')
+
+
-
- @php if($mode == 'edit') { $value = $recipePos->amount; } else { $value = 1; } @endphp
- @include('components.numberpicker', array(
- 'id' => 'amount',
- 'label' => 'Amount',
- 'min' => 0,
- 'value' => $value,
- 'invalidFeedback' => $__t('This cannot be negative and must be an integral number'),
- 'additionalGroupCssClasses' => 'col-4'
- ))
-
-
-
-
-
-
-
- {{ $__t('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">
-
-
-
+
+ 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">
+
@@ -183,6 +188,16 @@
{{ $selectedRecipePosition->ingredient_group }}@endif{{ $selectedRecipePosition->ingredient_group }}@endif |