diff --git a/changelog/53_UNRELEASED_2019-xx-xx.md b/changelog/53_UNRELEASED_2019-xx-xx.md index fc4244eb..989ccdb1 100644 --- a/changelog/53_UNRELEASED_2019-xx-xx.md +++ b/changelog/53_UNRELEASED_2019-xx-xx.md @@ -1,12 +1,26 @@ -- Fixed that barcode lookups now compare the whole barcode, not parts of it (e. g. when you have two products with the barcodes "$1" and "$10" and scan "$1" maybe the product of "$10" was found till now) +### Stock improvements/fixes +- Fixed that barcode lookups now compare the whole barcode, not parts of it (e. g. when you have two products with the barcodes `$1` and `$10` and scan `$1` maybe the product of `$10` was found till now) +- Fixed that the "X products are already expired" count on the stock overview page was wrong +- It's now possible to accumulate min. stock amounts on parent product level (new option per product, means the sub product will never be "missing" then, only the parent product) +- When adding a product to the shopping list from the new context/more menu from the stock overview page and if the product is already on the shopping list, the amount of that entry will be updated acccordingly instead of adding a new (double) shopping list item + +### Recipe improvements/fixes +- Fixed a problem regarding quantity unit conversion handling for recipe ingredients of products with no unit relations, but only a different purchase/stock quantity unit - It's now possible to display a recipe directly from the meal plan (new "eye button") (thanks @kriddles) - Improved the responsiveness of the meal plan and calendar page by automatically switching to a day calendar view on smaller screens (thanks for the idea @kriddles) + +### Chores improvements +- There is now a new sub feature flag `FEATURE_FLAG_CHORES_ASSIGNMENTS` to disable chore assignments if you don't need them (defaults to `true`, so no changed behavior when not configured) + +### Calendar improvements - The calendar now also contains all planned recipes from the meal plan on the corresponding day -- When adding a product to the shopping list from the new context/more menu from the stock overview page and if the product is already on the shopping list, the amount of that entry will be updated acccordingly instead of adding a new (double) shopping list item +- Improved that dates in the iCal calendar export now include the server timezone + +### General & other improvements/fixes - Fixed that the browser barcode scanner button was not clickable on iOS Safari (thanks @DeeeeLAN) -- Fixed a problem regarding quantity unit conversion handling for recipe ingredients of products with no unit relations, but only a different purchase/stock quantity unit -- Improved that dates in the iCal calendar export now includes the server timezone - It's now also possible to set the meal plan page as the default/entry page (`config.php` setting `ENTRY_PAGE`) (thanks @lwis) +- Some UI detail-refinements + +### API improvements/fixes - The API Endpoint `GET /files/{group}/{fileName}` now also returns a `Cache-Control` header (defaults fixed to 30 days) to further increase page load times - Fixed that the API endpoint `/stock/shoppinglist/add-product` failed when a product should be added which was not already on the shopping list (thanks @Forceu) -- Some style/CSS detail-refinements diff --git a/config-dist.php b/config-dist.php index 53817a24..c83286bb 100644 --- a/config-dist.php +++ b/config-dist.php @@ -124,6 +124,7 @@ Setting('FEATURE_FLAG_STOCK_PRICE_TRACKING', true); Setting('FEATURE_FLAG_STOCK_LOCATION_TRACKING', true); Setting('FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING', true); Setting('FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING', true); +Setting('FEATURE_FLAG_CHORES_ASSIGNMENTS', true); # Feature settings diff --git a/controllers/StockController.php b/controllers/StockController.php index 64dd3fc6..ec946ae0 100644 --- a/controllers/StockController.php +++ b/controllers/StockController.php @@ -28,7 +28,7 @@ class StockController extends BaseController 'products' => $this->Database->products()->orderBy('name'), 'quantityunits' => $this->Database->quantity_units()->orderBy('name'), 'locations' => $this->Database->locations()->orderBy('name'), - 'currentStock' => $this->StockService->GetCurrentStock(), + 'currentStock' => $this->StockService->GetCurrentStock(true), 'currentStockLocations' => $this->StockService->GetCurrentStockLocations(), 'missingProducts' => $this->StockService->GetMissingProducts(), 'nextXDays' => $nextXDays, diff --git a/localization/strings.pot b/localization/strings.pot index 136583c5..89cc8cfe 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -1519,3 +1519,12 @@ msgstr "" msgid "Display recipe" msgstr "" + +msgid "Accumulate sub products min. stock amount" +msgstr "" + +msgid "If enabled, the min. stock amount of sub products will be accumulated into this product, means the sub product will never be \"missing\", only this product" +msgstr "" + +msgid "Are you sure to remove this conversion?" +msgstr "" diff --git a/migrations/0092.sql b/migrations/0092.sql new file mode 100644 index 00000000..84c39cab --- /dev/null +++ b/migrations/0092.sql @@ -0,0 +1,129 @@ +ALTER TABLE products +ADD cumulate_min_stock_amount_of_sub_products TINYINT DEFAULT 0; + +CREATE VIEW products_view +AS +SELECT *, CASE WHEN (SELECT 1 FROM products WHERE parent_product_id = p.id) NOTNULL THEN 1 ELSE 0 END AS has_sub_products +FROM products p; + +DROP VIEW stock_missing_products; +CREATE VIEW stock_missing_products +AS + +-- Products WITHOUT sub products where the amount of the sub products SHOULD NOT be cumulated +SELECT + p.id, + MAX(p.name) AS name, + p.min_stock_amount - IFNULL(SUM(s.amount), 0) AS amount_missing, + CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock +FROM products_view p +LEFT JOIN stock_current s + ON p.id = s.product_id +WHERE p.min_stock_amount != 0 + AND p.cumulate_min_stock_amount_of_sub_products = 0 + AND p.has_sub_products = 0 + AND p.parent_product_id IS NULL +GROUP BY p.id +HAVING IFNULL(SUM(s.amount), 0) < p.min_stock_amount + +UNION + +-- Parent products WITH sub products where the amount of the sub products SHOULD be cumulated +SELECT + p.id, + MAX(p.name) AS name, + SUM(sub_p.min_stock_amount) - IFNULL(SUM(s.amount_aggregated), 0) AS amount_missing, + CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock +FROM products_view p +JOIN products_resolved pr + ON p.id = pr.parent_product_id +JOIN products sub_p + ON pr.sub_product_id = sub_p.id +LEFT JOIN stock_current s + ON pr.sub_product_id = s.product_id +WHERE sub_p.min_stock_amount != 0 + AND p.cumulate_min_stock_amount_of_sub_products = 1 +GROUP BY p.id +HAVING IFNULL(SUM(s.amount_aggregated), 0) < SUM(sub_p.min_stock_amount) + +UNION + +-- Sub products where the amount SHOULD NOT be cumulated into the parent product +SELECT + sub_p.id, + MAX(sub_p.name) AS name, + SUM(sub_p.min_stock_amount) - IFNULL(SUM(s.amount), 0) AS amount_missing, + CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock +FROM products p +JOIN products_resolved pr + ON p.id = pr.parent_product_id +JOIN products sub_p + ON pr.sub_product_id = sub_p.id +LEFT JOIN stock_current s + ON pr.sub_product_id = s.product_id +WHERE sub_p.min_stock_amount != 0 + AND p.cumulate_min_stock_amount_of_sub_products = 0 +GROUP BY sub_p.id +HAVING IFNULL(SUM(s.amount), 0) < sub_p.min_stock_amount; + +DROP VIEW stock_missing_products_including_opened; +CREATE VIEW stock_missing_products_including_opened +AS + +/* This is basically the same view as stock_missing_products, but the column "amount_missing" includes opened amounts */ + +-- Products WITHOUT sub products where the amount of the sub products SHOULD NOT be cumulated +SELECT + p.id, + MAX(p.name) AS name, + p.min_stock_amount - (IFNULL(SUM(s.amount), 0) - IFNULL(SUM(s.amount_opened), 0)) AS amount_missing, + CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock +FROM products_view p +LEFT JOIN stock_current s + ON p.id = s.product_id +WHERE p.min_stock_amount != 0 + AND p.cumulate_min_stock_amount_of_sub_products = 0 + AND p.has_sub_products = 0 + AND p.parent_product_id IS NULL +GROUP BY p.id +HAVING IFNULL(SUM(s.amount), 0) < p.min_stock_amount + +UNION + +-- Parent products WITH sub products where the amount of the sub products SHOULD be cumulated +SELECT + p.id, + MAX(p.name) AS name, + SUM(sub_p.min_stock_amount) - (IFNULL(SUM(s.amount_aggregated), 0) - IFNULL(SUM(s.amount_opened), 0)) AS amount_missing, + CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock +FROM products_view p +JOIN products_resolved pr + ON p.id = pr.parent_product_id +JOIN products sub_p + ON pr.sub_product_id = sub_p.id +LEFT JOIN stock_current s + ON pr.sub_product_id = s.product_id +WHERE sub_p.min_stock_amount != 0 + AND p.cumulate_min_stock_amount_of_sub_products = 1 +GROUP BY p.id +HAVING IFNULL(SUM(s.amount_aggregated), 0) < SUM(sub_p.min_stock_amount) + +UNION + +-- Sub products where the amount SHOULD NOT be cumulated into the parent product +SELECT + sub_p.id, + MAX(sub_p.name) AS name, + SUM(sub_p.min_stock_amount) - (IFNULL(SUM(s.amount), 0) - IFNULL(SUM(s.amount_opened), 0)) AS amount_missing, + CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock +FROM products p +JOIN products_resolved pr + ON p.id = pr.parent_product_id +JOIN products sub_p + ON pr.sub_product_id = sub_p.id +LEFT JOIN stock_current s + ON pr.sub_product_id = s.product_id +WHERE sub_p.min_stock_amount != 0 + AND p.cumulate_min_stock_amount_of_sub_products = 0 +GROUP BY sub_p.id +HAVING IFNULL(SUM(s.amount), 0) < sub_p.min_stock_amount; diff --git a/public/css/grocy_night_mode.css b/public/css/grocy_night_mode.css index 89d7091d..801f9901 100644 --- a/public/css/grocy_night_mode.css +++ b/public/css/grocy_night_mode.css @@ -4,7 +4,8 @@ .night-mode .table-info, .night-mode .table-info > td, -.night-mode .table-info > th { +.night-mode .table-info > th, +.night-mode .alert-info { background-color: #07373f; color: #6c757d; } @@ -54,14 +55,17 @@ border-color: #0d3a18; } -.night-mode .btn-light { +.night-mode .btn-light, +.night-mode .input-group-text, +.night-mode .note-editor.note-frame .note-statusbar, +.night-mode .img-thumbnail { color: #c1c1c1; background-color: #292b2a; border-color: #292b2a; } .night-mode .form-control { - color: #495057; + color: #ced4da; background-color: #333131; border: 1px solid #ced4da; } @@ -100,7 +104,7 @@ } .night-mode .form-control:focus { - color: #495057; + color: #ced4da; background-color: #333131; border-color: #80bdff; } diff --git a/public/js/grocy.js b/public/js/grocy.js index 8d3e725e..637e5503 100644 --- a/public/js/grocy.js +++ b/public/js/grocy.js @@ -298,8 +298,21 @@ RefreshContextualTimeago = function() $("time.timeago").each(function() { var element = $(this); + + if (!element.hasAttr("datetime")) + { + element.text("") + return + } + var timestamp = element.attr("datetime"); + if (timestamp.isEmpty()) + { + element.text("") + return + } + var isNever = timestamp && timestamp.substring(0, 10) == "2999-12-31"; var isToday = timestamp && timestamp.substring(0, 10) == moment().format("YYYY-MM-DD"); var isDateWithoutTime = element.hasClass("timeago-date-only"); @@ -515,18 +528,33 @@ $("#about-dialog-link").on("click", function() function RefreshLocaleNumberDisplay() { - $(".locale-number-format[data-format='currency']").each(function() + $(".locale-number.locale-number-currency").each(function() { + if (isNaN(parseFloat($(this).text()))) + { + return; + } + $(this).text(parseFloat($(this).text()).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency })); }); - $(".locale-number-format[data-format='quantity-amount']").each(function() + $(".locale-number.locale-number-quantity-amount").each(function() { + if (isNaN(parseFloat($(this).text()))) + { + return; + } + $(this).text(parseFloat($(this).text()).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 3 })); }); - $(".locale-number-format[data-format='generic']").each(function () + $(".locale-number.locale-number-generic").each(function () { + if (isNaN(parseFloat($(this).text()))) + { + return; + } + $(this).text(parseFloat($(this).text()).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 })); }); } diff --git a/public/viewjs/choreform.js b/public/viewjs/choreform.js index 730a2bcd..1bd77713 100644 --- a/public/viewjs/choreform.js +++ b/public/viewjs/choreform.js @@ -3,7 +3,11 @@ e.preventDefault(); var jsonData = $('#chore-form').serializeJSON({ checkboxUncheckedValue: "0" }); - jsonData.assignment_config = $("#assignment_config").val().join(","); + if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) + { + jsonData.assignment_config = $("#assignment_config").val().join(","); + } + Grocy.FrontendHelpers.BeginUiBusy("chore-form"); if (Grocy.EditMode === 'create') diff --git a/public/viewjs/choretracking.js b/public/viewjs/choretracking.js index 6218008b..05b60fa9 100644 --- a/public/viewjs/choretracking.js +++ b/public/viewjs/choretracking.js @@ -8,7 +8,7 @@ Grocy.Api.Get('chores/' + jsonForm.chore_id, function (choreDetails) { - Grocy.Api.Post('chores/' + jsonForm.chore_id + '/execute', { 'tracked_time': Grocy.Components.DateTimePicker.GetValue(), 'done_by': Grocy.Components.UserPicker.GetValue() }, + Grocy.Api.Post('chores/' + jsonForm.chore_id + '/execute', { 'tracked_time': Grocy.Components.DateTimePicker.GetValue(), 'done_by': $("#user_id").val() }, function(result) { Grocy.FrontendHelpers.EndUiBusy("choretracking-form"); diff --git a/public/viewjs/equipment.js b/public/viewjs/equipment.js index 7d376510..f9198cf9 100644 --- a/public/viewjs/equipment.js +++ b/public/viewjs/equipment.js @@ -131,4 +131,5 @@ $("#selectedEquipmentDescriptionToggleFullscreenButton").on('click', function(e) $("#selectedEquipmentDescriptionCard").toggleClass("fullscreen"); $("#selectedEquipmentDescriptionCard .card-header").toggleClass("fixed-top"); $("#selectedEquipmentDescriptionCard .card-body").toggleClass("mt-5"); + $("body").toggleClass("fullscreen-card"); }); diff --git a/public/viewjs/mealplan.js b/public/viewjs/mealplan.js index 240d502c..9c987d07 100644 --- a/public/viewjs/mealplan.js +++ b/public/viewjs/mealplan.js @@ -54,7 +54,7 @@ var calendar = $("#calendar").fullCalendar({ weekRecipeOrderMissingButtonHtml = '' weekRecipeConsumeButtonHtml = '' } - $(".fc-header-toolbar .fc-center").html("

" + __t("Week costs") + ': ' + weekCosts.toString() + " " + weekRecipeOrderMissingButtonHtml + weekRecipeConsumeButtonHtml + "

"); + $(".fc-header-toolbar .fc-center").html("

" + __t("Week costs") + ': ' + weekCosts.toString() + " " + weekRecipeOrderMissingButtonHtml + weekRecipeConsumeButtonHtml + "

"); }, "eventRender": function(event, element) { @@ -98,7 +98,7 @@ var calendar = $("#calendar").fullCalendar({
' + recipe.name + '
\
' + __n(mealPlanEntry.servings, "%s serving", "%s servings") + '
\
' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '
\ -
' + resolvedRecipe.costs + ' ' + __t('per serving') + '
\ +
' + resolvedRecipe.costs + ' ' + __t('per serving') + '
\
\ \ \ @@ -116,6 +116,7 @@ var calendar = $("#calendar").fullCalendar({ { RefreshLocaleNumberDisplay(); LoadImagesLazy(); + $('[data-toggle="tooltip"]').tooltip(); if (GetUriParam("week") !== undefined) { @@ -213,6 +214,10 @@ $(document).on("keyodwn", "#servings", function(e) $(document).on('click', '.recipe-order-missing-button', function(e) { + // Remove the focus from the current button + // to prevent that the tooltip stays until clicked anywhere else + document.activeElement.blur(); + var objectName = $(e.currentTarget).attr('data-recipe-name'); var objectId = $(e.currentTarget).attr('data-recipe-id'); var button = $(this); @@ -262,6 +267,10 @@ $(document).on('click', '.recipe-order-missing-button', function(e) $(document).on('click', '.recipe-consume-button', function(e) { + // Remove the focus from the current button + // to prevent that the tooltip stays until clicked anywhere else + document.activeElement.blur(); + var objectName = $(e.currentTarget).attr('data-recipe-name'); var objectId = $(e.currentTarget).attr('data-recipe-id'); @@ -304,6 +313,10 @@ $(document).on('click', '.recipe-consume-button', function(e) $(document).on("click", ".recipe-popup-button", function(e) { + // Remove the focus from the current button + // to prevent that the tooltip stays until clicked anywhere else + document.activeElement.blur(); + var objectId = $(e.currentTarget).attr('data-recipe-id'); bootbox.dialog({ diff --git a/public/viewjs/productform.js b/public/viewjs/productform.js index 3df99660..eb81f018 100644 --- a/public/viewjs/productform.js +++ b/public/viewjs/productform.js @@ -222,6 +222,7 @@ $('.input-group-qu').on('change', function(e) $('#product-form input').keyup(function(event) { Grocy.FrontendHelpers.ValidateForm('product-form'); + $(".input-group-qu").trigger("change"); if (document.getElementById('product-form').checkValidity() === false) //There is at least one validation error { diff --git a/public/viewjs/stockoverview.js b/public/viewjs/stockoverview.js index ac5d0788..d66f73c1 100644 --- a/public/viewjs/stockoverview.js +++ b/public/viewjs/stockoverview.js @@ -319,6 +319,9 @@ function RefreshProductRow(productId) productRow.removeClass("table-warning"); productRow.removeClass("table-danger"); + productRow.removeClass("table-info"); + productRow.removeClass("d-none"); + productRow.removeAttr("style"); if (now.isAfter(nextBestBeforeDate)) { productRow.addClass("table-danger"); @@ -328,12 +331,12 @@ function RefreshProductRow(productId) productRow.addClass("table-warning"); } - if (result.stock_amount <= 0) + if (result.stock_amount == 0 && result.product.min_stock_amount == 0) { $('#product-' + productId + '-row').fadeOut(500, function() { $(this).tooltip("hide"); - $(this).remove(); + $(this).addClass("d-none"); }); } else @@ -366,6 +369,11 @@ function RefreshProductRow(productId) $(this).text("").fadeIn(500); } }); + + if (result.stock_amount == 0 && result.product.min_stock_amount > 0) + { + productRow.addClass("table-info"); + } } $('#product-' + productId + '-next-best-before-date').parent().effect('highlight', {}, 500); diff --git a/services/StockService.php b/services/StockService.php index 67a01e14..48276380 100644 --- a/services/StockService.php +++ b/services/StockService.php @@ -11,15 +11,15 @@ class StockService extends BaseService public function GetCurrentStock($includeNotInStockButMissingProducts = false) { - $missingProductsView = 'stock_missing_products_including_opened'; - if (!GROCY_FEATURE_SETTING_STOCK_COUNT_OPENED_PRODUCTS_AGAINST_MINIMUM_STOCK_AMOUNT) - { - $missingProductsView = 'stock_missing_products'; - } - - $sql = 'SELECT * FROM stock_current UNION SELECT id, 0, 0, null, 0, 0, 0 FROM ' . $missingProductsView . ' WHERE id NOT IN (SELECT product_id FROM stock_current)'; + $sql = 'SELECT * FROM stock_current'; if ($includeNotInStockButMissingProducts) { + $missingProductsView = 'stock_missing_products_including_opened'; + if (!GROCY_FEATURE_SETTING_STOCK_COUNT_OPENED_PRODUCTS_AGAINST_MINIMUM_STOCK_AMOUNT) + { + $missingProductsView = 'stock_missing_products'; + } + $sql = 'SELECT * FROM stock_current WHERE best_before_date IS NOT NULL UNION SELECT id, 0, 0, null, 0, 0, 0 FROM ' . $missingProductsView . ' WHERE id NOT IN (SELECT product_id FROM stock_current)'; } @@ -69,7 +69,7 @@ class StockService extends BaseService public function GetExpiringProducts(int $days = 5, bool $excludeExpired = false) { - $currentStock = $this->GetCurrentStock(true); + $currentStock = $this->GetCurrentStock(false); $currentStock = FindAllObjectsInArrayByPropertyValue($currentStock, 'best_before_date', date('Y-m-d 23:59:59', strtotime("+$days days")), '<'); if ($excludeExpired) diff --git a/views/barcodescannertesting.blade.php b/views/barcodescannertesting.blade.php index 60f72a5f..c68d0c1a 100644 --- a/views/barcodescannertesting.blade.php +++ b/views/barcodescannertesting.blade.php @@ -30,8 +30,8 @@
- {{ $__t('Hit') }}: 0 // - {{ $__t('Miss') }}: 0 + {{ $__t('Hit') }}: 0 // + {{ $__t('Miss') }}: 0
diff --git a/views/choreform.blade.php b/views/choreform.blade.php index e965d1eb..0287b65e 100644 --- a/views/choreform.blade.php +++ b/views/choreform.blade.php @@ -86,6 +86,7 @@ + @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
{{ $__t('This assignment type requires that at least one is assigned') }}
+ @else + + + @endif
diff --git a/views/choresjournal.blade.php b/views/choresjournal.blade.php index 96cab621..e686a797 100644 --- a/views/choresjournal.blade.php +++ b/views/choresjournal.blade.php @@ -35,7 +35,9 @@ {{ $__t('Chore') }} {{ $__t('Tracked time') }} + @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) {{ $__t('Done by') }} + @endif @@ -58,6 +60,7 @@ {{ $choreLogEntry->tracked_time }} + @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) @if ($choreLogEntry->done_by_user_id !== null && !empty($choreLogEntry->done_by_user_id)) {{ GetUserDisplayName(FindObjectInArrayByPropertyValue($users, 'id', $choreLogEntry->done_by_user_id)) }} @@ -65,6 +68,7 @@ {{ $__t('Unknown') }} @endif + @endif @endforeach diff --git a/views/choresoverview.blade.php b/views/choresoverview.blade.php index d6e6b721..abfdc843 100644 --- a/views/choresoverview.blade.php +++ b/views/choresoverview.blade.php @@ -18,7 +18,9 @@

+ @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)

+ @endif @@ -35,15 +37,17 @@ + @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
+ @endif
@@ -55,9 +59,13 @@ {{ $__t('Chore') }} {{ $__t('Next estimated tracking') }} {{ $__t('Last tracked') }} + @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) {{ $__t('Assigned to') }} + @endif Hidden status + @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) Hidden assigned to user id + @endif @include('components.userfields_thead', array( 'userfields' => $userfields @@ -106,6 +114,7 @@ {{ $curentChoreEntry->last_tracked_time }} + @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) @if(!empty($curentChoreEntry->next_execution_assigned_to_user_id)) @@ -115,14 +124,17 @@ @endif + @endif @if(FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY && $curentChoreEntry->next_estimated_execution_time < date('Y-m-d H:i:s')) overdue @elseif(FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY && $curentChoreEntry->next_estimated_execution_time < date('Y-m-d H:i:s', strtotime("+$nextXDays days"))) duesoon @endif + @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) @if(!empty($curentChoreEntry->next_execution_assigned_to_user_id)) xx{{ $curentChoreEntry->next_execution_assigned_to_user_id }}xx @endif + @endif @include('components.userfields_tbody', array( 'userfields' => $userfields, diff --git a/views/choretracking.blade.php b/views/choretracking.blade.php index 48cce595..53f578f7 100644 --- a/views/choretracking.blade.php +++ b/views/choretracking.blade.php @@ -32,12 +32,16 @@ 'invalidFeedback' => $__t('This can only be before now') )) + @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) @include('components.userpicker', array( 'label' => 'Done by', 'users' => $users, 'nextInputSelector' => '#user_id', 'prefillByUserId' => GROCY_USER_ID )) + @else + + @endif diff --git a/views/components/batterycard.blade.php b/views/components/batterycard.blade.php index 80b71b63..91f2a1c0 100644 --- a/views/components/batterycard.blade.php +++ b/views/components/batterycard.blade.php @@ -12,7 +12,7 @@

{{ $__t('Used in') }}:
- {{ $__t('Charge cycles count') }}:
+ {{ $__t('Charge cycles count') }}:
{{ $__t('Last charged') }}:
diff --git a/views/components/chorecard.blade.php b/views/components/chorecard.blade.php index b89f59db..6a04def9 100644 --- a/views/components/chorecard.blade.php +++ b/views/components/chorecard.blade.php @@ -11,8 +11,10 @@

- {{ $__t('Tracked count') }}:
+ {{ $__t('Tracked count') }}:
{{ $__t('Last tracked') }}:
+ @if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) {{ $__t('Last done by') }}: + @endif
diff --git a/views/components/productcard.blade.php b/views/components/productcard.blade.php index 0a5474cf..aba5b267 100644 --- a/views/components/productcard.blade.php +++ b/views/components/productcard.blade.php @@ -18,8 +18,8 @@ - {{ $__t('Stock amount') . ' / ' . $__t('Quantity unit') }}: -
+ {{ $__t('Stock amount') . ' / ' . $__t('Quantity unit') }}: +
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING){{ $__t('Location') }}:
@endif {{ $__t('Last purchased') }}:
{{ $__t('Last used') }}:
diff --git a/views/equipment.blade.php b/views/equipment.blade.php index 4667288b..cca752f8 100644 --- a/views/equipment.blade.php +++ b/views/equipment.blade.php @@ -61,10 +61,10 @@
   - + - + @@ -72,7 +72,7 @@
-

{{ $__t('The selected equipment has no instruction manual') }}

+

{{ $__t('The selected equipment has no instruction manual') }}

@@ -81,10 +81,10 @@
   - + - + diff --git a/views/locationcontentsheet.blade.php b/views/locationcontentsheet.blade.php index 84d247ba..a1f67915 100644 --- a/views/locationcontentsheet.blade.php +++ b/views/locationcontentsheet.blade.php @@ -69,7 +69,7 @@ {{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }} - {{ $currentStockEntry->amount }} {{ $__n($currentStockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural) }} + {{ $currentStockEntry->amount }} {{ $__n($currentStockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural) }} @if($currentStockEntry->amount_opened > 0){{ $__t('%s opened', $currentStockEntry->amount_opened) }}@endif diff --git a/views/productform.blade.php b/views/productform.blade.php index f6ccda39..bedeb439 100644 --- a/views/productform.blade.php +++ b/views/productform.blade.php @@ -94,9 +94,20 @@ 'label' => 'Minimum stock amount', 'min' => 0, 'value' => $value, - 'invalidFeedback' => $__t('The amount cannot be lower than %s', '0') + 'invalidFeedback' => $__t('The amount cannot be lower than %s', '0'), + 'additionalGroupCssClasses' => 'mb-1' )) +
+
+ + cumulate_min_stock_amount_of_sub_products == 1) checked @endif class="form-check-input" type="checkbox" id="cumulate_min_stock_amount_of_sub_products" name="cumulate_min_stock_amount_of_sub_products" value="1"> + +
+
+ @php if($mode == 'edit') { $value = $product->default_best_before_days; } else { $value = 0; } @endphp @include('components.numberpicker', array( 'id' => 'default_best_before_days', @@ -168,7 +179,7 @@
-
+
enable_tare_weight_handling == 1) checked @endif class="form-check-input" type="checkbox" id="enable_tare_weight_handling" name="enable_tare_weight_handling" value="1"> @@ -267,7 +278,7 @@ - {{ $quConversion->factor }} + {{ $quConversion->factor }} {{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->to_qu_id)->name }} diff --git a/views/products.blade.php b/views/products.blade.php index 74e22829..10f6823f 100644 --- a/views/products.blade.php +++ b/views/products.blade.php @@ -77,7 +77,7 @@ {{ FindObjectInArrayByPropertyValue($locations, 'id', $product->location_id)->name }} - {{ $product->min_stock_amount }} + {{ $product->min_stock_amount }} {{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_purchase)->name }} @@ -86,7 +86,7 @@ {{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_stock)->name }} - {{ $product->qu_factor_purchase_to_stock }} + {{ $product->qu_factor_purchase_to_stock }} @if(!empty($product->product_group_id)) {{ FindObjectInArrayByPropertyValue($productGroups, 'id', $product->product_group_id)->name }} @endif diff --git a/views/recipeform.blade.php b/views/recipeform.blade.php index 9b1d2f14..a1246f18 100644 --- a/views/recipeform.blade.php +++ b/views/recipeform.blade.php @@ -142,7 +142,7 @@ @if(!empty($recipePosition->variable_amount)) {{ $recipePosition->variable_amount }} @else - @if($recipePosition->amount == round($recipePosition->amount)){{ round($recipePosition->amount) }}@else{{ $recipePosition->amount }}@endif + @if($recipePosition->amount == round($recipePosition->amount)){{ round($recipePosition->amount) }}@else{{ $recipePosition->amount }}@endif @endif {{ $__n($recipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural) }} diff --git a/views/recipes.blade.php b/views/recipes.blade.php index 0c42b6eb..47f9058b 100644 --- a/views/recipes.blade.php +++ b/views/recipes.blade.php @@ -164,7 +164,7 @@

-

{{ $selectedRecipeTotalCalories }}

+

{{ $selectedRecipeTotalCalories }}

@endif @@ -174,7 +174,7 @@ {{ $__t('Based on the prices of the last purchase per product') }}

-

{{ $selectedRecipeTotalCosts }}

+

{{ $selectedRecipeTotalCosts }}

@endif @@ -216,7 +216,7 @@ @if(!empty($selectedRecipePosition->recipe_variable_amount)) {{ $selectedRecipePosition->recipe_variable_amount }} @else - @if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif + @if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif @endif {{ $__n($selectedRecipePosition->recipe_amount, FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }} @if($selectedRecipePosition->need_fulfilled == 1)@elseif($selectedRecipePosition->need_fulfilled_with_shopping_list == 1)@else@endif @@ -267,7 +267,7 @@ @if(!empty($selectedRecipePosition->recipe_variable_amount)) {{ $selectedRecipePosition->recipe_variable_amount }} @else - @if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif + @if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif @endif {{ $__n($selectedRecipePosition->recipe_amount, FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }} @if($selectedRecipePosition->need_fulfilled == 1)@elseif($selectedRecipePosition->need_fulfilled_with_shopping_list == 1)@else@endif diff --git a/views/shoppinglist.blade.php b/views/shoppinglist.blade.php index d2483710..edf97320 100644 --- a/views/shoppinglist.blade.php +++ b/views/shoppinglist.blade.php @@ -120,7 +120,7 @@ @if(!empty($listItem->product_id)) {{ FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->name }}
@endif{!! nl2br($listItem->note) !!} - {{ $listItem->amount }} @if(!empty($listItem->product_id)){{ $__n($listItem->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->qu_id_purchase)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->qu_id_purchase)->name_plural) }}@endif + {{ $listItem->amount }} @if(!empty($listItem->product_id)){{ $__n($listItem->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->qu_id_purchase)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->qu_id_purchase)->name_plural) }}@endif @if(!empty(FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->product_group_id)) {{ FindObjectInArrayByPropertyValue($productGroups, 'id', FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->product_group_id)->name }} @else {{ $__t('Ungrouped') }} @endif diff --git a/views/stockjournal.blade.php b/views/stockjournal.blade.php index a95acf75..5704a90d 100644 --- a/views/stockjournal.blade.php +++ b/views/stockjournal.blade.php @@ -56,7 +56,7 @@ @endif - {{ $stockLogEntry->amount }} {{ $__n($stockLogEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockLogEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockLogEntry->product_id)->qu_id_stock)->name_plural) }} + {{ $stockLogEntry->amount }} {{ $__n($stockLogEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockLogEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockLogEntry->product_id)->qu_id_stock)->name_plural) }} {{ $stockLogEntry->row_created_timestamp }} diff --git a/views/stockoverview.blade.php b/views/stockoverview.blade.php index 1ef2b19f..5abc1ac2 100644 --- a/views/stockoverview.blade.php +++ b/views/stockoverview.blade.php @@ -174,11 +174,11 @@ {{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }} - {{ $currentStockEntry->amount }} {{ $__n($currentStockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural) }} + {{ $currentStockEntry->amount }} {{ $__n($currentStockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural) }} @if($currentStockEntry->amount_opened > 0){{ $__t('%s opened', $currentStockEntry->amount_opened) }}@endif @if($currentStockEntry->is_aggregated_amount == 1) - {{ $currentStockEntry->amount_aggregated }} {{ $__n($currentStockEntry->amount_aggregated, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural) }} + {{ $currentStockEntry->amount_aggregated }} {{ $__n($currentStockEntry->amount_aggregated, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural) }} @if($currentStockEntry->amount_opened_aggregated > 0){{ $__t('%s opened', $currentStockEntry->amount_opened_aggregated) }}@endif @endif