mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 17:45:39 +00:00
Squashed commit
Fixed recipe ingredient costs/calories calculation when having different QUs and when only_check_single_unit_in_stock is set (fixes #2529) Added a new column "Product picture" on /products (closes #2640) Fixed partly opening stock entries stock_id handling (fixes #2391)
This commit is contained in:
parent
e7cea3d949
commit
f4d5f21832
@ -19,7 +19,9 @@
|
|||||||
- => Quick video demo (using a Barcode Laser Scanner): https://www.youtube.com/watch?v=-moXPA-VvGc
|
- => Quick video demo (using a Barcode Laser Scanner): https://www.youtube.com/watch?v=-moXPA-VvGc
|
||||||
- => Quick video demo (using Browser Camera Barcode Scanning): https://www.youtube.com/watch?v=veezFX4X1JU
|
- => Quick video demo (using Browser Camera Barcode Scanning): https://www.youtube.com/watch?v=veezFX4X1JU
|
||||||
- Optimized that when moving a product to a freezer location (so when freezing it) the due date will no longer be replaced when the product option "Default due days after freezing" is set to `0`
|
- Optimized that when moving a product to a freezer location (so when freezing it) the due date will no longer be replaced when the product option "Default due days after freezing" is set to `0`
|
||||||
|
- Added a new column "Product picture" on the products list (master data) page (hidden by default)
|
||||||
- Fixed that a once set quantity unit on a product barcode could not be removed on edit
|
- Fixed that a once set quantity unit on a product barcode could not be removed on edit
|
||||||
|
- Fixed that when consuming a specific stock entry which is opened, and which originated from a before partly opened stock entry, the unopened one was wrongly consume instead
|
||||||
|
|
||||||
### Shopping list
|
### Shopping list
|
||||||
|
|
||||||
@ -27,7 +29,7 @@
|
|||||||
|
|
||||||
### Recipes
|
### Recipes
|
||||||
|
|
||||||
- xxx
|
- Fixed that calories/costs of recipe ingredients were wrong when the ingredient option "Only check if any amount is in stock" was set and the on the ingredient used quantity unit was different from the product's QU stock
|
||||||
|
|
||||||
### Meal plan
|
### Meal plan
|
||||||
|
|
||||||
|
@ -2,10 +2,7 @@
|
|||||||
|
|
||||||
// This is executed inside DatabaseMigrationService class/context
|
// This is executed inside DatabaseMigrationService class/context
|
||||||
|
|
||||||
use Grocy\Services\LocalizationService;
|
|
||||||
|
|
||||||
$localizationService = $this->getLocalizationService();
|
$localizationService = $this->getLocalizationService();
|
||||||
|
|
||||||
$db = $this->getDatabaseService()->GetDbConnection();
|
$db = $this->getDatabaseService()->GetDbConnection();
|
||||||
|
|
||||||
if ($db->quantity_units()->count() === 0)
|
if ($db->quantity_units()->count() === 0)
|
||||||
|
@ -2,10 +2,7 @@
|
|||||||
|
|
||||||
// This is executed inside DatabaseMigrationService class/context
|
// This is executed inside DatabaseMigrationService class/context
|
||||||
|
|
||||||
use Grocy\Services\LocalizationService;
|
|
||||||
|
|
||||||
$localizationService = $this->getLocalizationService();
|
$localizationService = $this->getLocalizationService();
|
||||||
|
|
||||||
$db = $this->getDatabaseService()->GetDbConnection();
|
$db = $this->getDatabaseService()->GetDbConnection();
|
||||||
|
|
||||||
$defaultShoppingList = $db->shopping_lists()->where('id = 1')->fetch();
|
$defaultShoppingList = $db->shopping_lists()->where('id = 1')->fetch();
|
||||||
|
@ -2,6 +2,4 @@
|
|||||||
|
|
||||||
// This is executed inside DatabaseMigrationService class/context
|
// This is executed inside DatabaseMigrationService class/context
|
||||||
|
|
||||||
use Grocy\Services\StockService;
|
|
||||||
|
|
||||||
$this->getStockService()->CompactStockEntries();
|
$this->getStockService()->CompactStockEntries();
|
||||||
|
135
migrations/0241.sql
Normal file
135
migrations/0241.sql
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
DROP VIEW recipes_pos_resolved;
|
||||||
|
CREATE VIEW recipes_pos_resolved
|
||||||
|
AS
|
||||||
|
|
||||||
|
-- Multiplication by 1.0 to force conversion to float (REAL)
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
r.id AS recipe_id,
|
||||||
|
rp.id AS recipe_pos_id,
|
||||||
|
rp.product_id AS product_id,
|
||||||
|
CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) END AS recipe_amount,
|
||||||
|
IFNULL(sc.amount_aggregated, 0) AS stock_amount,
|
||||||
|
CASE WHEN IFNULL(sc.amount_aggregated, 0) >= CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 0.00000001 ELSE CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) END END THEN 1 ELSE 0 END AS need_fulfilled,
|
||||||
|
CASE WHEN IFNULL(sc.amount_aggregated, 0) - CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 0.00000001 ELSE CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) END END < 0 THEN ABS(IFNULL(sc.amount_aggregated, 0) - (CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) END)) ELSE 0 END AS missing_amount,
|
||||||
|
IFNULL(sl.amount, 0) AS amount_on_shopping_list,
|
||||||
|
CASE WHEN ROUND(IFNULL(sc.amount_aggregated, 0) + CASE WHEN r.not_check_shoppinglist = 1 THEN 0 ELSE IFNULL(sl.amount, 0) END, 2) >= ROUND(CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN 0.00000001 ELSE CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) END END, 2) THEN 1 ELSE 0 END AS need_fulfilled_with_shopping_list,
|
||||||
|
rp.qu_id,
|
||||||
|
(r.desired_servings*1.0 / r.base_servings*1.0) * CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN IFNULL(qucr.factor, 1.0) ELSE 1 END * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) * rp.amount * IFNULL(pcp.price, 0) * rp.price_factor * CASE WHEN rp.product_id != p_effective.id THEN IFNULL(qucr.factor, 1.0) ELSE 1.0 END AS costs,
|
||||||
|
CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN 0 ELSE 1 END AS is_nested_recipe_pos,
|
||||||
|
rp.ingredient_group,
|
||||||
|
pg.name as product_group,
|
||||||
|
rp.id, -- Just a dummy id column
|
||||||
|
r.type as recipe_type,
|
||||||
|
rnr.includes_recipe_id as child_recipe_id,
|
||||||
|
rp.note,
|
||||||
|
rp.variable_amount AS recipe_variable_amount,
|
||||||
|
rp.only_check_single_unit_in_stock,
|
||||||
|
rp.amount * CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN IFNULL(qucr.factor, 1.0) ELSE 1 END / r.base_servings*1.0 * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) * IFNULL(p_effective.calories, 0) * CASE WHEN rp.product_id != p_effective.id THEN IFNULL(qucr.factor, 1.0) ELSE 1.0 END AS calories,
|
||||||
|
p.active AS product_active,
|
||||||
|
CASE pvs.current_due_status
|
||||||
|
WHEN 'ok' THEN 0
|
||||||
|
WHEN 'due_soon' THEN 1
|
||||||
|
WHEN 'overdue' THEN 10
|
||||||
|
WHEN 'expired' THEN 20
|
||||||
|
END AS due_score,
|
||||||
|
IFNULL(pcs.product_id_effective, rp.product_id) AS product_id_effective,
|
||||||
|
p.name AS product_name
|
||||||
|
FROM recipes r
|
||||||
|
JOIN recipes_nestings_resolved rnr
|
||||||
|
ON r.id = rnr.recipe_id
|
||||||
|
JOIN recipes rnrr
|
||||||
|
ON rnr.includes_recipe_id = rnrr.id
|
||||||
|
JOIN recipes_pos rp
|
||||||
|
ON rnr.includes_recipe_id = rp.recipe_id
|
||||||
|
JOIN products p
|
||||||
|
ON rp.product_id = p.id
|
||||||
|
JOIN products_volatile_status pvs
|
||||||
|
ON rp.product_id = pvs.product_id
|
||||||
|
LEFT JOIN product_groups pg
|
||||||
|
ON p.product_group_id = pg.id
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT product_id, SUM(amount) AS amount
|
||||||
|
FROM shopping_list
|
||||||
|
GROUP BY product_id) sl
|
||||||
|
ON rp.product_id = sl.product_id
|
||||||
|
LEFT JOIN stock_current sc
|
||||||
|
ON rp.product_id = sc.product_id
|
||||||
|
LEFT JOIN products_current_substitutions pcs
|
||||||
|
ON rp.product_id = pcs.parent_product_id
|
||||||
|
LEFT JOIN products_current_price pcp
|
||||||
|
ON IFNULL(pcs.product_id_effective, rp.product_id) = pcp.product_id
|
||||||
|
LEFT JOIN products p_effective
|
||||||
|
ON IFNULL(pcs.product_id_effective, rp.product_id) = p_effective.id
|
||||||
|
LEFT JOIN cache__quantity_unit_conversions_resolved qucr
|
||||||
|
ON IFNULL(pcs.product_id_effective, rp.product_id) = qucr.product_id
|
||||||
|
AND CASE WHEN rp.product_id != p_effective.id THEN p.qu_id_stock ELSE rp.qu_id END = qucr.from_qu_id
|
||||||
|
AND IFNULL(p_effective.qu_id_stock, p.qu_id_stock) = qucr.to_qu_id
|
||||||
|
WHERE rp.not_check_stock_fulfillment = 0
|
||||||
|
|
||||||
|
UNION
|
||||||
|
|
||||||
|
-- Just add all recipe positions which should not be checked against stock with fulfilled need
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
r.id AS recipe_id,
|
||||||
|
rp.id AS recipe_pos_id,
|
||||||
|
rp.product_id AS product_id,
|
||||||
|
CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) ELSE rp.amount * ((r.desired_servings*1.0) / (r.base_servings*1.0)) * ((rnr.includes_servings*1.0) / (rnrr.base_servings*1.0)) END AS recipe_amount,
|
||||||
|
IFNULL(sc.amount_aggregated, 0) AS stock_amount,
|
||||||
|
1 AS need_fulfilled,
|
||||||
|
0 AS missing_amount,
|
||||||
|
IFNULL(sl.amount, 0) AS amount_on_shopping_list,
|
||||||
|
1 AS need_fulfilled_with_shopping_list,
|
||||||
|
rp.qu_id,
|
||||||
|
(r.desired_servings*1.0 / r.base_servings*1.0) * CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN IFNULL(qucr.factor, 1.0) ELSE 1 END * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) * rp.amount * IFNULL(pcp.price, 0) * rp.price_factor * CASE WHEN rp.product_id != p_effective.id THEN IFNULL(qucr.factor, 1.0) ELSE 1.0 END AS costs,
|
||||||
|
CASE WHEN rnr.recipe_id = rnr.includes_recipe_id THEN 0 ELSE 1 END AS is_nested_recipe_pos,
|
||||||
|
rp.ingredient_group,
|
||||||
|
pg.name as product_group,
|
||||||
|
rp.id, -- Just a dummy id column
|
||||||
|
r.type as recipe_type,
|
||||||
|
rnr.includes_recipe_id as child_recipe_id,
|
||||||
|
rp.note,
|
||||||
|
rp.variable_amount AS recipe_variable_amount,
|
||||||
|
rp.only_check_single_unit_in_stock,
|
||||||
|
rp.amount * CASE WHEN rp.only_check_single_unit_in_stock = 1 THEN IFNULL(qucr.factor, 1.0) ELSE 1 END / r.base_servings*1.0 * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) * IFNULL(p_effective.calories, 0) * CASE WHEN rp.product_id != p_effective.id THEN IFNULL(qucr.factor, 1.0) ELSE 1.0 END AS calories,
|
||||||
|
p.active AS product_active,
|
||||||
|
CASE pvs.current_due_status
|
||||||
|
WHEN 'ok' THEN 0
|
||||||
|
WHEN 'due_soon' THEN 1
|
||||||
|
WHEN 'overdue' THEN 10
|
||||||
|
WHEN 'expired' THEN 20
|
||||||
|
END AS due_score,
|
||||||
|
IFNULL(pcs.product_id_effective, rp.product_id) AS product_id_effective,
|
||||||
|
p.name AS product_name
|
||||||
|
FROM recipes r
|
||||||
|
JOIN recipes_nestings_resolved rnr
|
||||||
|
ON r.id = rnr.recipe_id
|
||||||
|
JOIN recipes rnrr
|
||||||
|
ON rnr.includes_recipe_id = rnrr.id
|
||||||
|
JOIN recipes_pos rp
|
||||||
|
ON rnr.includes_recipe_id = rp.recipe_id
|
||||||
|
JOIN products p
|
||||||
|
ON rp.product_id = p.id
|
||||||
|
JOIN products_volatile_status pvs
|
||||||
|
ON rp.product_id = pvs.product_id
|
||||||
|
LEFT JOIN product_groups pg
|
||||||
|
ON p.product_group_id = pg.id
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT product_id, SUM(amount) AS amount
|
||||||
|
FROM shopping_list
|
||||||
|
GROUP BY product_id) sl
|
||||||
|
ON rp.product_id = sl.product_id
|
||||||
|
LEFT JOIN stock_current sc
|
||||||
|
ON rp.product_id = sc.product_id
|
||||||
|
LEFT JOIN products_current_substitutions pcs
|
||||||
|
ON rp.product_id = pcs.parent_product_id
|
||||||
|
LEFT JOIN products_current_price pcp
|
||||||
|
ON IFNULL(pcs.product_id_effective, rp.product_id) = pcp.product_id
|
||||||
|
LEFT JOIN products p_effective
|
||||||
|
ON IFNULL(pcs.product_id_effective, rp.product_id) = p_effective.id
|
||||||
|
LEFT JOIN cache__quantity_unit_conversions_resolved qucr
|
||||||
|
ON IFNULL(pcs.product_id_effective, rp.product_id) = qucr.product_id
|
||||||
|
AND CASE WHEN rp.product_id != p_effective.id THEN p.qu_id_stock ELSE rp.qu_id END = qucr.from_qu_id
|
||||||
|
AND IFNULL(p_effective.qu_id_stock, p.qu_id_stock) = qucr.to_qu_id
|
||||||
|
WHERE rp.not_check_stock_fulfillment = 1;
|
22
migrations/0242.php
Normal file
22
migrations/0242.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// This is executed inside DatabaseMigrationService class/context
|
||||||
|
|
||||||
|
// Assign a new stock_id to all opened stock entries where there is also an unopened one with the same stock_id
|
||||||
|
$db = $this->getDatabaseService();
|
||||||
|
|
||||||
|
$sql = 'SELECT s1.id
|
||||||
|
FROM stock s1
|
||||||
|
WHERE IFNULL(s1.open, 0) = 1
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM stock s2
|
||||||
|
WHERE s2.stock_id = s1.stock_id
|
||||||
|
AND IFNULL(s2.open, 0) = 0
|
||||||
|
)';
|
||||||
|
|
||||||
|
$rows = $db->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ);
|
||||||
|
foreach ($rows as $row)
|
||||||
|
{
|
||||||
|
$db->ExecuteDbStatement('UPDATE stock SET stock_id = \'' . uniqid() . '\' WHERE id = ' . $row->id);
|
||||||
|
}
|
@ -16,14 +16,14 @@ body {
|
|||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:not(.btn):not(.nav-link):not(.dropdown-item) {
|
a:not(.btn):not(.nav-link):not(.dropdown-item):not(.list-group-item) {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
text-decoration-style: dotted;
|
text-decoration-style: dotted;
|
||||||
text-underline-offset: 0.2rem;
|
text-underline-offset: 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:not(.btn):not(.nav-link):not(.dropdown-item):hover {
|
a:not(.btn):not(.nav-link):not(.dropdown-item):not(.list-group-item):hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,8 @@ body.night-mode,
|
|||||||
color: #c1c1c1;
|
color: #c1c1c1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.night-mode .table {
|
.night-mode .table,
|
||||||
|
.night-mode .list-group-item {
|
||||||
color: #c1c1c1;
|
color: #c1c1c1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +226,6 @@ var sumValue = 0;
|
|||||||
$("#location_id").on('change', function(e)
|
$("#location_id").on('change', function(e)
|
||||||
{
|
{
|
||||||
var locationId = $(e.target).val();
|
var locationId = $(e.target).val();
|
||||||
|
|
||||||
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
||||||
if ($("#use_specific_stock_entry").is(":checked"))
|
if ($("#use_specific_stock_entry").is(":checked"))
|
||||||
{
|
{
|
||||||
@ -293,21 +292,19 @@ function OnLocationChange(locationId, stockId)
|
|||||||
|
|
||||||
if (stockEntry.location_id == locationId)
|
if (stockEntry.location_id == locationId)
|
||||||
{
|
{
|
||||||
if ($("#specific_stock_entry option[value='" + stockEntry.stock_id + "']").length == 0)
|
var noteTxt = "";
|
||||||
|
if (stockEntry.note)
|
||||||
{
|
{
|
||||||
var noteTxt = "";
|
noteTxt = " " + stockEntry.note;
|
||||||
if (stockEntry.note)
|
|
||||||
{
|
|
||||||
noteTxt = " " + stockEntry.note;
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#specific_stock_entry").append($("<option>", {
|
|
||||||
value: stockEntry.stock_id,
|
|
||||||
amount: stockEntry.amount,
|
|
||||||
text: __t("Amount: %1$s; Due on %2$s; Bought on %3$s", stockEntry.amount, moment(stockEntry.best_before_date).format("YYYY-MM-DD"), moment(stockEntry.purchased_date).format("YYYY-MM-DD")) + "; " + openTxt + noteTxt
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$("#specific_stock_entry").append($("<option>", {
|
||||||
|
"value": stockEntry.stock_id,
|
||||||
|
"text": __t("Amount: %1$s; Due on %2$s; Bought on %3$s", stockEntry.amount, moment(stockEntry.best_before_date).format("YYYY-MM-DD"), moment(stockEntry.purchased_date).format("YYYY-MM-DD")) + "; " + openTxt + noteTxt,
|
||||||
|
"data-amount": stockEntry.amount,
|
||||||
|
"data-id": stockEntry.id
|
||||||
|
}));
|
||||||
|
|
||||||
sumValue = sumValue + (stockEntry.amount || 0);
|
sumValue = sumValue + (stockEntry.amount || 0);
|
||||||
|
|
||||||
if (stockEntry.stock_id == stockId)
|
if (stockEntry.stock_id == stockId)
|
||||||
@ -585,7 +582,7 @@ $("#specific_stock_entry").on("change", function(e)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$("#display_amount").attr("max", Number.parseFloat($('option:selected', this).attr('amount')).toFixed(Grocy.UserSettings.stock_decimal_places_amounts));
|
$("#display_amount").attr("max", Number.parseFloat($('option:selected', this).attr('data-amount')).toFixed(Grocy.UserSettings.stock_decimal_places_amounts));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -603,7 +600,6 @@ $("#use_specific_stock_entry").on("change", function()
|
|||||||
$("#specific_stock_entry").attr("disabled", "");
|
$("#specific_stock_entry").attr("disabled", "");
|
||||||
$("#specific_stock_entry").removeAttr("required");
|
$("#specific_stock_entry").removeAttr("required");
|
||||||
$("#specific_stock_entry").val("");
|
$("#specific_stock_entry").val("");
|
||||||
$("#location_id").trigger('change');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Grocy.FrontendHelpers.ValidateForm("consume-form");
|
Grocy.FrontendHelpers.ValidateForm("consume-form");
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
{ 'searchable': false, "targets": 0 },
|
{ 'searchable': false, "targets": 0 },
|
||||||
{ 'visible': false, 'targets': 7 },
|
{ 'visible': false, 'targets': 7 },
|
||||||
{ 'visible': false, 'targets': 8 },
|
{ 'visible': false, 'targets': 8 },
|
||||||
|
{ 'visible': false, 'targets': 9 },
|
||||||
{ "type": "html-num-fmt", "targets": 3 }
|
{ "type": "html-num-fmt", "targets": 3 }
|
||||||
].concat($.fn.dataTable.defaults.columnDefs)
|
].concat($.fn.dataTable.defaults.columnDefs)
|
||||||
});
|
});
|
||||||
|
@ -748,4 +748,7 @@ if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Grocy.Components.UserfieldsForm.Load();
|
if (Grocy.Components.UserfieldsForm)
|
||||||
|
{
|
||||||
|
Grocy.Components.UserfieldsForm.Load();
|
||||||
|
}
|
||||||
|
@ -1069,7 +1069,7 @@ class StockService extends BaseService
|
|||||||
'purchased_date' => $stockEntry->purchased_date,
|
'purchased_date' => $stockEntry->purchased_date,
|
||||||
'location_id' => $stockEntry->location_id,
|
'location_id' => $stockEntry->location_id,
|
||||||
'shopping_location_id' => $stockEntry->shopping_location_id,
|
'shopping_location_id' => $stockEntry->shopping_location_id,
|
||||||
'stock_id' => $stockEntry->stock_id,
|
'stock_id' => uniqid(),
|
||||||
'price' => $stockEntry->price,
|
'price' => $stockEntry->price,
|
||||||
'note' => $stockEntry->note
|
'note' => $stockEntry->note
|
||||||
]);
|
]);
|
||||||
|
@ -128,6 +128,7 @@
|
|||||||
<th class="">{{ $__t('Product group') }}</th>
|
<th class="">{{ $__t('Product group') }}</th>
|
||||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif allow-grouping">{{ $__t('Default store') }}</th>
|
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif allow-grouping">{{ $__t('Default store') }}</th>
|
||||||
<th class="">{{ $__t('Grocycode') }}</th>
|
<th class="">{{ $__t('Grocycode') }}</th>
|
||||||
|
<th>{{ $__t('Product picture') }}</th>
|
||||||
|
|
||||||
@include('components.userfields_thead', array(
|
@include('components.userfields_thead', array(
|
||||||
'userfields' => $userfields
|
'userfields' => $userfields
|
||||||
@ -215,6 +216,12 @@
|
|||||||
<img src="{{ $U('/product/' . $product->id . '/grocycode?size=25') }}"
|
<img src="{{ $U('/product/' . $product->id . '/grocycode?size=25') }}"
|
||||||
loading="lazy">
|
loading="lazy">
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
@if(!empty($product->picture_file_name))
|
||||||
|
<img src="{{ $U('/api/files/productpictures/' . base64_encode($product->picture_file_name) . '?force_serve_as=picture&best_fit_width=64&best_fit_height=64') }}"
|
||||||
|
loading="lazy">
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
|
||||||
@include('components.userfields_tbody', array(
|
@include('components.userfields_tbody', array(
|
||||||
'userfields' => $userfields,
|
'userfields' => $userfields,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user