mirror of
https://github.com/grocy/grocy.git
synced 2026-04-05 12:26:15 +02:00
Merge branch 'master' of https://github.com/grocy/grocy
This commit is contained in:
commit
d674239d3a
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
|
|
|||
129
migrations/0092.sql
Normal file
129
migrations/0092.sql
Normal file
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ var calendar = $("#calendar").fullCalendar({
|
|||
weekRecipeOrderMissingButtonHtml = '<a class="ml-1 btn btn-outline-primary btn-xs recipe-order-missing-button ' + weekRecipeOrderMissingButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Put missing products on shopping list") + '" data-recipe-id="' + weekRecipe.id.toString() + '" data-recipe-name="' + weekRecipe.name + '" data-recipe-type="' + weekRecipe.type + '"><i class="fas fa-cart-plus"></i></a>'
|
||||
weekRecipeConsumeButtonHtml = '<a class="ml-1 btn btn-outline-success btn-xs recipe-consume-button ' + weekRecipeConsumeButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Consume all ingredients needed by this recipe") + '" data-recipe-id="' + weekRecipe.id.toString() + '" data-recipe-name="' + weekRecipe.name + '" data-recipe-type="' + weekRecipe.type + '"><i class="fas fa-utensils"></i></a>'
|
||||
}
|
||||
$(".fc-header-toolbar .fc-center").html("<h4>" + __t("Week costs") + ': <span class="locale-number-format" data-format="currency">' + weekCosts.toString() + "</span> " + weekRecipeOrderMissingButtonHtml + weekRecipeConsumeButtonHtml + "</h4>");
|
||||
$(".fc-header-toolbar .fc-center").html("<h4>" + __t("Week costs") + ': <span class="locale-number locale-number-currency">' + weekCosts.toString() + "</span> " + weekRecipeOrderMissingButtonHtml + weekRecipeConsumeButtonHtml + "</h4>");
|
||||
},
|
||||
"eventRender": function(event, element)
|
||||
{
|
||||
|
|
@ -98,7 +98,7 @@ var calendar = $("#calendar").fullCalendar({
|
|||
<h5 class="text-truncate">' + recipe.name + '<h5> \
|
||||
<h5 class="small text-truncate">' + __n(mealPlanEntry.servings, "%s serving", "%s servings") + '</h5> \
|
||||
<h5 class="small timeago-contextual text-truncate">' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '</h5> \
|
||||
<h5 class="small text-truncate"><span class="locale-number-format" data-format="currency">' + resolvedRecipe.costs + '</span> ' + __t('per serving') + '<h5> \
|
||||
<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + resolvedRecipe.costs + '</span> ' + __t('per serving') + '<h5> \
|
||||
<h5> \
|
||||
<a class="ml-1 btn btn-outline-danger btn-xs remove-recipe-button" href="#"><i class="fas fa-trash"></i></a> \
|
||||
<a class="ml-1 btn btn-outline-primary btn-xs recipe-order-missing-button ' + recipeOrderMissingButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Put missing products on shopping list") + '" data-recipe-id="' + recipe.id.toString() + '" data-recipe-name="' + recipe.name + '" data-recipe-type="' + recipe.type + '"><i class="fas fa-cart-plus"></i></a> \
|
||||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@
|
|||
<div class="form-group">
|
||||
<label for="scanned_codes">{{ $__t('Scanned barcodes') }}</label>
|
||||
<div class="float-right font-weight-bold">
|
||||
<span class="text-success">{{ $__t('Hit') }}: <span id="hit-count" class="locale-number-format" data-format="generic">0</span></span> //
|
||||
<span class="text-danger">{{ $__t('Miss') }}: <span id="miss-count" class="locale-number-format" data-format="generic">0</span></span>
|
||||
<span class="text-success">{{ $__t('Hit') }}: <span id="hit-count" class="locale-number locale-number-generic">0</span></span> //
|
||||
<span class="text-danger">{{ $__t('Miss') }}: <span id="miss-count" class="locale-number locale-number-generic">0</span></span>
|
||||
</div>
|
||||
<select class="form-control" id="scanned_codes" name="scanned_codes" multiple size="30"></select>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@
|
|||
|
||||
<input type="hidden" id="period_config" name="period_config" value="@if($mode == 'edit'){{ $chore->period_config }}@endif">
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<div class="form-group">
|
||||
<label for="assignment_type">{{ $__t('Assignment type') }} <span id="chore-assignment-type-info" class="small text-muted"></span></label>
|
||||
<select required class="form-control input-group-chore-assignment-type" id="assignment_type" name="assignment_type">
|
||||
|
|
@ -105,6 +106,10 @@
|
|||
</select>
|
||||
<div class="invalid-feedback">{{ $__t('This assignment type requires that at least one is assigned') }}</div>
|
||||
</div>
|
||||
@else
|
||||
<input type="hidden" id="assignment_type" name="assignment_type" value="{{ \Grocy\Services\ChoresService::CHORE_ASSIGNMENT_TYPE_NO_ASSIGNMENT }}">
|
||||
<input type="hidden" id="assignment_config" name="assignment_config" value="">
|
||||
@endif
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@
|
|||
<th class="border-right"></th>
|
||||
<th>{{ $__t('Chore') }}</th>
|
||||
<th>{{ $__t('Tracked time') }}</th>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<th>{{ $__t('Done by') }}</th>
|
||||
@endif
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
|
|
@ -58,6 +60,7 @@
|
|||
<span>{{ $choreLogEntry->tracked_time }}</span>
|
||||
<time class="timeago timeago-contextual @if(FindObjectInArrayByPropertyValue($chores, 'id', $choreLogEntry->chore_id)->track_date_only == 1) timeago-date-only @endif" datetime="{{ $choreLogEntry->tracked_time }}"></time>
|
||||
</td>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<td>
|
||||
@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
|
||||
</td>
|
||||
@endif
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@
|
|||
</h1>
|
||||
<p id="info-due-chores" data-status-filter="duesoon" data-next-x-days="{{ $nextXDays }}" class="btn btn-lg btn-warning status-filter-button responsive-button mr-2"></p>
|
||||
<p id="info-overdue-chores" data-status-filter="overdue" class="btn btn-lg btn-danger status-filter-button responsive-button mr-2"></p>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<p id="info-assigned-to-me-chores" data-user-filter="xx{{ GROCY_USER_ID }}xx" class="btn btn-lg btn-secondary user-filter-button responsive-button"></p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -35,15 +37,17 @@
|
|||
<option class="bg-danger" value="overdue">{{ $__t('Overdue') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<div class="col-xs-12 col-md-6 col-xl-3">
|
||||
<label for="user-filter">{{ $__t('Filter by assignment') }}</label> <i class="fas fa-filter"></i>
|
||||
<select class="form-control input-group-filter" id="user-filter">
|
||||
<option></option>
|
||||
@foreach($users as $user)
|
||||
<option class="@if($user->id == GROCY_USER_ID) bg-secondary text-white @endif" data-user-id="{{ $user->id }}" value="xx{{ $user->id }}xx">{{ $user->display_name }}</option>
|
||||
<option class="@if($user->id == GROCY_USER_ID) bg-secondary text-white @endif" data-user-id="{{ $user->id }}" value="xx{{ $user->id }}xx">{{ $user->display_name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
|
@ -55,9 +59,13 @@
|
|||
<th>{{ $__t('Chore') }}</th>
|
||||
<th>{{ $__t('Next estimated tracking') }}</th>
|
||||
<th>{{ $__t('Last tracked') }}</th>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<th>{{ $__t('Assigned to') }}</th>
|
||||
@endif
|
||||
<th class="d-none">Hidden status</th>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<th class="d-none">Hidden assigned to user id</th>
|
||||
@endif
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
|
|
@ -106,6 +114,7 @@
|
|||
<span id="chore-{{ $curentChoreEntry->chore_id }}-last-tracked-time">{{ $curentChoreEntry->last_tracked_time }}</span>
|
||||
<time id="chore-{{ $curentChoreEntry->chore_id }}-last-tracked-time-timeago" class="timeago timeago-contextual @if($curentChoreEntry->track_date_only == 1) timeago-date-only @endif" datetime="{{ $curentChoreEntry->last_tracked_time }}"></time>
|
||||
</td>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<td>
|
||||
<span id="chore-{{ $curentChoreEntry->chore_id }}-next-execution-assigned-user">
|
||||
@if(!empty($curentChoreEntry->next_execution_assigned_to_user_id))
|
||||
|
|
@ -115,14 +124,17 @@
|
|||
@endif
|
||||
</span>
|
||||
</td>
|
||||
@endif
|
||||
<td id="chore-{{ $curentChoreEntry->chore_id }}-due-filter-column" class="d-none">
|
||||
@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
|
||||
</td>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<td class="d-none">
|
||||
@if(!empty($curentChoreEntry->next_execution_assigned_to_user_id))
|
||||
xx{{ $curentChoreEntry->next_execution_assigned_to_user_id }}xx
|
||||
@endif
|
||||
</td>
|
||||
@endif
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
'userfields' => $userfields,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
<input type="hidden" id="user_id" name="user_id" value="{{ GROCY_USER_ID }}">
|
||||
@endif
|
||||
|
||||
<button id="save-choretracking-button" class="btn btn-success">{{ $__t('OK') }}</button>
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<div class="card-body">
|
||||
<h3><span id="batterycard-battery-name"></span></h3>
|
||||
<strong>{{ $__t('Used in') }}:</strong> <span id="batterycard-battery-used_in"></span><br>
|
||||
<strong>{{ $__t('Charge cycles count') }}:</strong> <span id="batterycard-battery-charge-cycles-count"></span><br>
|
||||
<strong>{{ $__t('Charge cycles count') }}:</strong> <span id="batterycard-battery-charge-cycles-count" class="locale-number locale-number-generic"></span><br>
|
||||
<strong>{{ $__t('Last charged') }}:</strong> <span id="batterycard-battery-last-charged"></span> <time id="batterycard-battery-last-charged-timeago" class="timeago timeago-contextual"></time><br>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@
|
|||
</div>
|
||||
<div class="card-body">
|
||||
<h3><span id="chorecard-chore-name"></span></h3>
|
||||
<strong>{{ $__t('Tracked count') }}:</strong> <span id="chorecard-chore-tracked-count"></span><br>
|
||||
<strong>{{ $__t('Tracked count') }}:</strong> <span id="chorecard-chore-tracked-count" class="locale-number locale-number-generic"></span><br>
|
||||
<strong>{{ $__t('Last tracked') }}:</strong> <span id="chorecard-chore-last-tracked"></span> <time id="chorecard-chore-last-tracked-timeago" class="timeago timeago-contextual"></time><br>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<strong>{{ $__t('Last done by') }}:</strong> <span id="chorecard-chore-last-done-by"></span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
<a class="collapsed" data-toggle="collapse" href="#productcard-product-description">{{ $__t('Show more') }}</a>
|
||||
</div>
|
||||
|
||||
<strong>{{ $__t('Stock amount') . ' / ' . $__t('Quantity unit') }}:</strong> <span id="productcard-product-stock-amount"></span> <span id="productcard-product-stock-qu-name"></span> <span id="productcard-product-stock-opened-amount" class="small font-italic"></span>
|
||||
<span id="productcard-aggregated-amounts" class="pl-2 text-secondary d-none"><i class="fas fa-custom-sigma-sign"></i> <span id="productcard-product-stock-amount-aggregated"></span> <span id="productcard-product-stock-qu-name-aggregated"></span> <span id="productcard-product-stock-opened-amount-aggregated" class="small font-italic"></span></span><br>
|
||||
<strong>{{ $__t('Stock amount') . ' / ' . $__t('Quantity unit') }}:</strong> <span id="productcard-product-stock-amount" class="locale-number locale-number-quantity-amount"></span> <span id="productcard-product-stock-qu-name"></span> <span id="productcard-product-stock-opened-amount" class="small font-italic locale-number locale-number-quantity-amount"></span>
|
||||
<span id="productcard-aggregated-amounts" class="pl-2 text-secondary d-none"><i class="fas fa-custom-sigma-sign"></i> <span id="productcard-product-stock-amount-aggregated" class="locale-number locale-number-quantity-amount"></span> <span id="productcard-product-stock-qu-name-aggregated"></span> <span id="productcard-product-stock-opened-amount-aggregated locale-number locale-number-quantity-amount" class="small font-italic"></span></span><br>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)<strong>{{ $__t('Location') }}:</strong> <span id="productcard-product-location"></span><br>@endif
|
||||
<strong>{{ $__t('Last purchased') }}:</strong> <span id="productcard-product-last-purchased"></span> <time id="productcard-product-last-purchased-timeago" class="timeago timeago-contextual"></time><br>
|
||||
<strong>{{ $__t('Last used') }}:</strong> <span id="productcard-product-last-used"></span> <time id="productcard-product-last-used-timeago" class="timeago timeago-contextual"></time><br>
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@
|
|||
<div id="selectedEquipmentInstructionManualCard" class="card">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-toolbox"></i> <span class="selected-equipment-name"></span>
|
||||
<a class="btn btn-sm btn-outline-info py-0 equipment-edit-button" href="#">
|
||||
<a class="btn btn-sm btn-outline-info py-0 equipment-edit-button hide-on-fullscreen-card" href="#">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-outline-danger py-0 equipment-delete-button" href="#" data-equipment-id="{{ $equipmentItem->id }}" data-equipment-name="{{ $equipmentItem->name }}">
|
||||
<a class="btn btn-sm btn-outline-danger py-0 equipment-delete-button hide-on-fullscreen-card" href="#" data-equipment-id="{{ $equipmentItem->id }}" data-equipment-name="{{ $equipmentItem->name }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<a id="selectedEquipmentInstructionManualToggleFullscreenButton" class="btn btn-sm btn-outline-secondary py-0 float-right" href="#" data-toggle="tooltip" title="{{ $__t('Expand to fullscreen') }}">
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
</a>
|
||||
</div>
|
||||
<div class="card-body py-0 px-0">
|
||||
<p id="selected-equipment-has-no-instruction-manual-hint" class="text-muted font-italic d-none">{{ $__t('The selected equipment has no instruction manual') }}</p>
|
||||
<p id="selected-equipment-has-no-instruction-manual-hint" class="text-muted font-italic d-none pt-3 pl-3">{{ $__t('The selected equipment has no instruction manual') }}</p>
|
||||
<embed id="selected-equipment-instruction-manual" class="embed-responsive embed-responsive-4by3" src="" type="application/pdf">
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -81,10 +81,10 @@
|
|||
<div id="selectedEquipmentDescriptionCard" class="card">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-toolbox"></i> <span class="selected-equipment-name"></span>
|
||||
<a class="btn btn-sm btn-outline-info py-0 equipment-edit-button" href="#">
|
||||
<a class="btn btn-sm btn-outline-info py-0 equipment-edit-button hide-on-fullscreen-card" href="#">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-outline-danger py-0 equipment-delete-button" href="#" data-equipment-id="{{ $equipmentItem->id }}" data-equipment-name="{{ $equipmentItem->name }}">
|
||||
<a class="btn btn-sm btn-outline-danger py-0 equipment-delete-button hide-on-fullscreen-card" href="#" data-equipment-id="{{ $equipmentItem->id }}" data-equipment-name="{{ $equipmentItem->name }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<a id="selectedEquipmentDescriptionToggleFullscreenButton" class="btn btn-sm btn-outline-secondary py-0 float-right" href="#" data-toggle="tooltip" title="{{ $__t('Expand to fullscreen') }}">
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
{{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }}
|
||||
</td>
|
||||
<td>
|
||||
<span>{{ $currentStockEntry->amount }}</span> <span id="product-{{ $currentStockEntry->product_id }}-qu-name">{{ $__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) }}</span>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount }}</span> <span id="product-{{ $currentStockEntry->product_id }}-qu-name">{{ $__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) }}</span>
|
||||
<span class="small font-italic">@if($currentStockEntry->amount_opened > 0){{ $__t('%s opened', $currentStockEntry->amount_opened) }}@endif</span>
|
||||
</td>
|
||||
<td class=""></td>
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
))
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input type="hidden" name="cumulate_min_stock_amount_of_sub_products" value="0">
|
||||
<input @if($mode == 'edit' && $product->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">
|
||||
<label class="form-check-label" for="cumulate_min_stock_amount_of_sub_products">{{ $__t('Accumulate sub products min. stock amount') }}
|
||||
<span class="text-muted small">{{ $__t('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') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@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 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group mb-1">
|
||||
<div class="form-check">
|
||||
<input type="hidden" name="enable_tare_weight_handling" value="0">
|
||||
<input @if($mode == 'edit' && $product->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 @@
|
|||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ $quConversion->factor }}
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $quConversion->factor }}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->to_qu_id)->name }}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
{{ FindObjectInArrayByPropertyValue($locations, 'id', $product->location_id)->name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ $product->min_stock_amount }}
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $product->min_stock_amount }}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_purchase)->name }}
|
||||
|
|
@ -86,7 +86,7 @@
|
|||
{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_stock)->name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ $product->qu_factor_purchase_to_stock }}
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $product->qu_factor_purchase_to_stock }}</span>
|
||||
</td>
|
||||
<td>
|
||||
@if(!empty($product->product_group_id)) {{ FindObjectInArrayByPropertyValue($productGroups, 'id', $product->product_group_id)->name }} @endif
|
||||
|
|
|
|||
|
|
@ -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
|
||||
<span class="locale-number locale-number-quantity-amount">@if($recipePosition->amount == round($recipePosition->amount)){{ round($recipePosition->amount) }}@else{{ $recipePosition->amount }}@endif</span>
|
||||
@endif
|
||||
{{ $__n($recipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural) }}
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@
|
|||
<div class="col-2">
|
||||
<label>{{ $__t('Energy (kcal)') }}</label>
|
||||
<p class="mb-0">
|
||||
<h3 class="locale-number-format pt-0" data-format="generic">{{ $selectedRecipeTotalCalories }}</h3>
|
||||
<h3 class="locale-number locale-number-generic pt-0">{{ $selectedRecipeTotalCalories }}</h3>
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
|
@ -174,7 +174,7 @@
|
|||
<span class="small text-muted">{{ $__t('Based on the prices of the last purchase per product') }}</span>
|
||||
</label>
|
||||
<p class="mb-0">
|
||||
<h3 class="locale-number-format pt-0" data-format="currency">{{ $selectedRecipeTotalCosts }}</h3>
|
||||
<h3 class="locale-number locale-number-currency pt-0">{{ $selectedRecipeTotalCosts }}</h3>
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
|
@ -216,7 +216,7 @@
|
|||
@if(!empty($selectedRecipePosition->recipe_variable_amount))
|
||||
{{ $selectedRecipePosition->recipe_variable_amount }}
|
||||
@else
|
||||
<span class="locale-number-format" data-format="quantity-amount">@if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif</span>
|
||||
<span class="llocale-number locale-number-quantity-amount">@if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif</span>
|
||||
@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)<i class="fas fa-check text-success"></i>@elseif($selectedRecipePosition->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
|
||||
|
|
@ -267,7 +267,7 @@
|
|||
@if(!empty($selectedRecipePosition->recipe_variable_amount))
|
||||
{{ $selectedRecipePosition->recipe_variable_amount }}
|
||||
@else
|
||||
<span class="locale-number-format" data-format="quantity-amount">@if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif</span>
|
||||
<span class="locale-number locale-number-quantity-amount">@if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif</span>
|
||||
@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)<i class="fas fa-check text-success"></i>@elseif($selectedRecipePosition->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@
|
|||
@if(!empty($listItem->product_id)) {{ FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id)->name }}<br>@endif<em>{!! nl2br($listItem->note) !!}</em>
|
||||
</td>
|
||||
<td>
|
||||
{{ $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
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $listItem->amount }}</span> @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
|
||||
</td>
|
||||
<td class="d-none">
|
||||
@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 <span class="font-italic font-weight-light">{{ $__t('Ungrouped') }}</span> @endif
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
@endif
|
||||
</td>
|
||||
<td>
|
||||
{{ $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) }}
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $stockLogEntry->amount }}</span> {{ $__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) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ $stockLogEntry->row_created_timestamp }}
|
||||
|
|
|
|||
|
|
@ -174,11 +174,11 @@
|
|||
{{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }}
|
||||
</td>
|
||||
<td>
|
||||
<span id="product-{{ $currentStockEntry->product_id }}-amount">{{ $currentStockEntry->amount }}</span> <span id="product-{{ $currentStockEntry->product_id }}-qu-name">{{ $__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) }}</span>
|
||||
<span id="product-{{ $currentStockEntry->product_id }}-amount" class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount }}</span> <span id="product-{{ $currentStockEntry->product_id }}-qu-name">{{ $__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) }}</span>
|
||||
<span id="product-{{ $currentStockEntry->product_id }}-opened-amount" class="small font-italic">@if($currentStockEntry->amount_opened > 0){{ $__t('%s opened', $currentStockEntry->amount_opened) }}@endif</span>
|
||||
@if($currentStockEntry->is_aggregated_amount == 1)
|
||||
<span class="pl-1 text-secondary">
|
||||
<i class="fas fa-custom-sigma-sign"></i> {{ $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) }}
|
||||
<i class="fas fa-custom-sigma-sign"></i> <span class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount_aggregated }}</span> {{ $__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)<span class="small font-italic">{{ $__t('%s opened', $currentStockEntry->amount_opened_aggregated) }}</span>@endif
|
||||
</span>
|
||||
@endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user