mirror of
https://github.com/grocy/grocy.git
synced 2025-08-13 17:27:23 +00:00
@@ -39,6 +39,7 @@
|
|||||||
- The amount now defaults to `1` for adding items quicker
|
- The amount now defaults to `1` for adding items quicker
|
||||||
- Fixed that shopping list prints had a grey background (thanks @Forceu)
|
- Fixed that shopping list prints had a grey background (thanks @Forceu)
|
||||||
- Fixed the form validation on the shopping list item page (thanks @Forceu)
|
- Fixed the form validation on the shopping list item page (thanks @Forceu)
|
||||||
|
- Fixed that when adding products to the shopping list from the stock overview page, the used quantity unit was always the products default purchase QU (and not the selected one)
|
||||||
|
|
||||||
### Recipe improvements/fixes
|
### Recipe improvements/fixes
|
||||||
- Recipe printing improvements (thanks @Ape)
|
- Recipe printing improvements (thanks @Ape)
|
||||||
|
@@ -180,6 +180,7 @@ class StockApiController extends BaseApiController
|
|||||||
|
|
||||||
$listId = 1;
|
$listId = 1;
|
||||||
$amount = 1;
|
$amount = 1;
|
||||||
|
$quId = -1;
|
||||||
$productId = null;
|
$productId = null;
|
||||||
$note = null;
|
$note = null;
|
||||||
|
|
||||||
@@ -203,12 +204,17 @@ class StockApiController extends BaseApiController
|
|||||||
$note = $requestBody['note'];
|
$note = $requestBody['note'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('qu_id', $requestBody) && !empty($requestBody['qu_id']))
|
||||||
|
{
|
||||||
|
$quId = $requestBody['qu_id'];
|
||||||
|
}
|
||||||
|
|
||||||
if ($productId == null)
|
if ($productId == null)
|
||||||
{
|
{
|
||||||
throw new \Exception('No product id was supplied');
|
throw new \Exception('No product id was supplied');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getStockService()->AddProductToShoppingList($productId, $amount, $note, $listId);
|
$this->getStockService()->AddProductToShoppingList($productId, $amount, $quId, $note, $listId);
|
||||||
return $this->EmptyApiResponse($response);
|
return $this->EmptyApiResponse($response);
|
||||||
}
|
}
|
||||||
catch (\Exception $ex)
|
catch (\Exception $ex)
|
||||||
|
103
migrations/0133.sql
Normal file
103
migrations/0133.sql
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
DROP VIEW quantity_unit_conversions_resolved;
|
||||||
|
CREATE VIEW quantity_unit_conversions_resolved
|
||||||
|
AS
|
||||||
|
|
||||||
|
-- 1. Product "purchase to stock" conversion factor
|
||||||
|
SELECT
|
||||||
|
-1 AS id, -- Dummy, LessQL needs an id column
|
||||||
|
p.id AS product_id,
|
||||||
|
p.qu_id_purchase AS from_qu_id,
|
||||||
|
qu_from.name AS from_qu_name,
|
||||||
|
qu_from.name_plural AS from_qu_name_plural,
|
||||||
|
p.qu_id_stock AS to_qu_id,
|
||||||
|
qu_to.name AS to_qu_name,
|
||||||
|
qu_to.name_plural AS to_qu_name_plural,
|
||||||
|
p.qu_factor_purchase_to_stock AS factor
|
||||||
|
FROM products p
|
||||||
|
JOIN quantity_units qu_from
|
||||||
|
ON p.qu_id_purchase = qu_from.id
|
||||||
|
JOIN quantity_units qu_to
|
||||||
|
ON p.qu_id_stock = qu_to.id
|
||||||
|
UNION -- Inversed
|
||||||
|
SELECT
|
||||||
|
-1 AS id, -- Dummy, LessQL needs an id column
|
||||||
|
p.id AS product_id,
|
||||||
|
p.qu_id_stock AS from_qu_id,
|
||||||
|
qu_to.name AS from_qu_name,
|
||||||
|
qu_to.name_plural AS from_qu_name_plural,
|
||||||
|
p.qu_id_purchase AS to_qu_id,
|
||||||
|
qu_from.name AS to_qu_name,
|
||||||
|
qu_from.name_plural AS to_qu_name_plural,
|
||||||
|
1 / p.qu_factor_purchase_to_stock AS factor
|
||||||
|
FROM products p
|
||||||
|
JOIN quantity_units qu_from
|
||||||
|
ON p.qu_id_purchase = qu_from.id
|
||||||
|
JOIN quantity_units qu_to
|
||||||
|
ON p.qu_id_stock = qu_to.id
|
||||||
|
|
||||||
|
UNION
|
||||||
|
|
||||||
|
-- 2. Product specific QU overrides
|
||||||
|
SELECT
|
||||||
|
-1 AS id, -- Dummy, LessQL needs an id column
|
||||||
|
p.id AS product_id,
|
||||||
|
quc.from_qu_id AS from_qu_id,
|
||||||
|
qu_from.name AS from_qu_name,
|
||||||
|
qu_from.name_plural AS from_qu_name_plural,
|
||||||
|
quc.to_qu_id AS to_qu_id,
|
||||||
|
qu_to.name AS to_qu_name,
|
||||||
|
qu_to.name_plural AS to_qu_name_plural,
|
||||||
|
quc.factor AS factor
|
||||||
|
FROM products p
|
||||||
|
JOIN quantity_unit_conversions quc
|
||||||
|
ON p.id = quc.product_id
|
||||||
|
JOIN quantity_units qu_from
|
||||||
|
ON quc.from_qu_id = qu_from.id
|
||||||
|
JOIN quantity_units qu_to
|
||||||
|
ON quc.to_qu_id = qu_to.id
|
||||||
|
|
||||||
|
UNION
|
||||||
|
|
||||||
|
-- 3. Default (direct) QU conversion factors
|
||||||
|
SELECT
|
||||||
|
-1 AS id, -- Dummy, LessQL needs an id column
|
||||||
|
p.id AS product_id,
|
||||||
|
p.qu_id_stock AS from_qu_id,
|
||||||
|
qu_from.name AS from_qu_name,
|
||||||
|
qu_from.name_plural AS from_qu_name_plural,
|
||||||
|
quc.to_qu_id AS to_qu_id,
|
||||||
|
qu_to.name AS to_qu_name,
|
||||||
|
qu_to.name_plural AS to_qu_name_plural,
|
||||||
|
quc.factor AS factor
|
||||||
|
FROM products p
|
||||||
|
JOIN quantity_unit_conversions quc
|
||||||
|
ON p.qu_id_stock = quc.from_qu_id
|
||||||
|
AND quc.product_id IS NULL
|
||||||
|
JOIN quantity_units qu_from
|
||||||
|
ON quc.from_qu_id = qu_from.id
|
||||||
|
JOIN quantity_units qu_to
|
||||||
|
ON quc.to_qu_id = qu_to.id
|
||||||
|
|
||||||
|
UNION
|
||||||
|
|
||||||
|
-- 4. Default (indirect) QU conversion factors
|
||||||
|
SELECT
|
||||||
|
-1 AS id, -- Dummy, LessQL needs an id column
|
||||||
|
p.id AS product_id,
|
||||||
|
(SELECT from_qu_id FROM quantity_unit_conversions WHERE to_qu_id = quc.to_qu_id AND product_id = p.id) AS from_qu_id,
|
||||||
|
qu_from.name AS from_qu_name,
|
||||||
|
qu_from.name_plural AS from_qu_name_plural,
|
||||||
|
quc.from_qu_id AS to_qu_id,
|
||||||
|
qu_to.name AS to_qu_name,
|
||||||
|
qu_to.name_plural AS to_qu_name_plural,
|
||||||
|
quc.factor * (SELECT factor FROM quantity_unit_conversions WHERE to_qu_id = quc.to_qu_id AND product_id = p.id) AS factor
|
||||||
|
FROM products p
|
||||||
|
JOIN product_qu_relations pqr
|
||||||
|
ON p.id = pqr.product_id
|
||||||
|
JOIN quantity_unit_conversions quc
|
||||||
|
ON pqr.qu_id = quc.from_qu_id
|
||||||
|
AND quc.product_id IS NULL
|
||||||
|
JOIN quantity_units qu_from
|
||||||
|
ON (SELECT from_qu_id FROM quantity_unit_conversions WHERE to_qu_id = quc.to_qu_id AND product_id = p.id) = qu_from.id
|
||||||
|
JOIN quantity_units qu_to
|
||||||
|
ON quc.from_qu_id = qu_to.id;
|
@@ -21,7 +21,7 @@ Grocy.Components.ProductAmountPicker.Reload = function(productId, destinationQuI
|
|||||||
|
|
||||||
if (!$('#qu_id option[value="' + conversion.to_qu_id + '"]').length) // Don't add the destination QU multiple times
|
if (!$('#qu_id option[value="' + conversion.to_qu_id + '"]').length) // Don't add the destination QU multiple times
|
||||||
{
|
{
|
||||||
$("#qu_id").append('<option value="' + conversion.to_qu_id + '" data-qu-factor="' + factor + '">' + conversion.to_qu_name + '</option>');
|
$("#qu_id").append('<option value="' + conversion.to_qu_id + '" data-qu-factor="' + factor + '" data-qu-name-plural="' + conversion.to_qu_name_plural + '">' + conversion.to_qu_name + '</option>');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ Grocy.Components.ProductAmountPicker.AllowAnyQu = function(keepInitialQu = false
|
|||||||
$("#qu_id").find("option").remove().end();
|
$("#qu_id").find("option").remove().end();
|
||||||
Grocy.QuantityUnits.forEach(qu =>
|
Grocy.QuantityUnits.forEach(qu =>
|
||||||
{
|
{
|
||||||
$("#qu_id").append('<option value="' + qu.id + '" data-qu-factor="1">' + qu.name + '</option>');
|
$("#qu_id").append('<option value="' + qu.id + '" data-qu-factor="1" data-qu-name-plural="' + qu.name_plural + '>' + qu.name + '</option>');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (keepInitialQu)
|
if (keepInitialQu)
|
||||||
|
@@ -10,6 +10,7 @@ $('#save-shoppinglist-button').on('click', function(e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var jsonData = $('#shoppinglist-form').serializeJSON();
|
var jsonData = $('#shoppinglist-form').serializeJSON();
|
||||||
|
var displayAmount = parseFloat(jsonData.display_amount);
|
||||||
if (!jsonData.product_id)
|
if (!jsonData.product_id)
|
||||||
{
|
{
|
||||||
jsonData.amount = jsonData.display_amount;
|
jsonData.amount = jsonData.display_amount;
|
||||||
@@ -37,7 +38,7 @@ $('#save-shoppinglist-button').on('click', function(e)
|
|||||||
Grocy.Api.Get('stock/products/' + jsonData.product_id,
|
Grocy.Api.Get('stock/products/' + jsonData.product_id,
|
||||||
function(productDetails)
|
function(productDetails)
|
||||||
{
|
{
|
||||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", parseFloat(jsonData.product_amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(jsonData.product_amount, productDetails.default_quantity_unit_purchase.name, productDetails.default_quantity_unit_purchase.name_plural), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural")), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
||||||
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
||||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||||
},
|
},
|
||||||
@@ -74,7 +75,7 @@ $('#save-shoppinglist-button').on('click', function(e)
|
|||||||
Grocy.Api.Get('stock/products/' + jsonData.product_id,
|
Grocy.Api.Get('stock/products/' + jsonData.product_id,
|
||||||
function(productDetails)
|
function(productDetails)
|
||||||
{
|
{
|
||||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", parseFloat(jsonData.amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(jsonData.amount, productDetails.default_quantity_unit_purchase.name, productDetails.default_quantity_unit_purchase.name_plural), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural")), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
||||||
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
||||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||||
},
|
},
|
||||||
@@ -116,7 +117,7 @@ $('#save-shoppinglist-button').on('click', function(e)
|
|||||||
Grocy.Api.Get('stock/products/' + jsonData.product_id,
|
Grocy.Api.Get('stock/products/' + jsonData.product_id,
|
||||||
function(productDetails)
|
function(productDetails)
|
||||||
{
|
{
|
||||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", parseFloat(jsonData.amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(jsonData.amount, productDetails.default_quantity_unit_purchase.name, productDetails.default_quantity_unit_purchase.name_plural), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural")), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
||||||
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
||||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||||
},
|
},
|
||||||
|
@@ -214,7 +214,7 @@ class StockService extends BaseService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function AddProductToShoppingList($productId, $amount = 1, $note = null, $listId = 1)
|
public function AddProductToShoppingList($productId, $amount = 1, $quId = -1, $note = null, $listId = 1)
|
||||||
{
|
{
|
||||||
if (!$this->ShoppingListExists($listId))
|
if (!$this->ShoppingListExists($listId))
|
||||||
{
|
{
|
||||||
@@ -226,9 +226,15 @@ class StockService extends BaseService
|
|||||||
throw new \Exception('Product does not exist or is inactive');
|
throw new \Exception('Product does not exist or is inactive');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($quId == -1)
|
||||||
|
{
|
||||||
|
$quId = $this->getDatabase()->products($productId)->qu_id_purchase;
|
||||||
|
}
|
||||||
|
|
||||||
$alreadyExistingEntry = $this->getDatabase()->shopping_list()->where('product_id = :1 AND shopping_list_id = :2', $productId, $listId)->fetch();
|
$alreadyExistingEntry = $this->getDatabase()->shopping_list()->where('product_id = :1 AND shopping_list_id = :2', $productId, $listId)->fetch();
|
||||||
if ($alreadyExistingEntry)
|
if ($alreadyExistingEntry)
|
||||||
{ // Update
|
{
|
||||||
|
// Update
|
||||||
$alreadyExistingEntry->update([
|
$alreadyExistingEntry->update([
|
||||||
'amount' => ($alreadyExistingEntry->amount + $amount),
|
'amount' => ($alreadyExistingEntry->amount + $amount),
|
||||||
'shopping_list_id' => $listId,
|
'shopping_list_id' => $listId,
|
||||||
@@ -236,10 +242,12 @@ class StockService extends BaseService
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ // Insert
|
{
|
||||||
|
// Insert
|
||||||
$shoppinglistRow = $this->getDatabase()->shopping_list()->createRow([
|
$shoppinglistRow = $this->getDatabase()->shopping_list()->createRow([
|
||||||
'product_id' => $productId,
|
'product_id' => $productId,
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
|
'qu_id' => $quId,
|
||||||
'shopping_list_id' => $listId,
|
'shopping_list_id' => $listId,
|
||||||
'note' => $note
|
'note' => $note
|
||||||
]);
|
]);
|
||||||
|
Reference in New Issue
Block a user