mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 17:45:39 +00:00
Consume any subproduct when consuming a recipe ingredient which is not in stock (fixes #446)
This commit is contained in:
parent
dceed6759a
commit
e84c7063d3
@ -41,6 +41,7 @@
|
||||
- Fixed that when `FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS` was set to `false`, the shopping list appeared empty after some actions
|
||||
|
||||
### Recipe improvements
|
||||
- When consuming a recipe and if an ingredient is not in stock, but that product has any subproduct which is in stock, this gets now consumed (before consuming was not possible in that case)
|
||||
- When adding or editing a recipe ingredient, a dialog is now used instead of switching between pages (thanks @kriddles)
|
||||
|
||||
### Meal plan improvements/fixes
|
||||
@ -60,9 +61,10 @@
|
||||
|
||||
### API improvements/fixes
|
||||
- The endpoint `/stock` now includes also the product object itself (new field/property `product`) (thanks @gsacre)
|
||||
- The endpoint `/stock/products/{productId}/entries` can now include stock entries of child products (if the given product is a parent product and in addition to the ones of the given product) - new query parameter `include_sub_products` (defaults to `false` so no changed behavior when not supplied)
|
||||
- New endpoints for the new stock transfer & stock entry edit capabilities
|
||||
- Fixed that the route `/stock/barcodes/external-lookup/{barcode}` did not work, because the `barcode` argument was expected as a route argument but the route was missing it (thanks @Mikhail5555 and @beetle442002)
|
||||
- Fixed the response type description of the `/stock/volatile` endpoint
|
||||
- New endpoints for the new stock transfer & stock entry edit capabilities
|
||||
|
||||
### General & other improvements/fixes
|
||||
- It's now possible to keep the screen on always or when a "fullscreen-card" (e. g. used for recipes) is displayed
|
||||
|
@ -567,7 +567,13 @@ class StockApiController extends BaseApiController
|
||||
|
||||
public function ProductStockEntries(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
return $this->ApiResponse($this->StockService->GetProductStockEntries($args['productId']));
|
||||
$allowSubproductSubstitution = false;
|
||||
if (isset($request->getQueryParams()['include_sub_products']) && boolval($request->getQueryParams()['include_sub_products']))
|
||||
{
|
||||
$allowSubproductSubstitution = true;
|
||||
}
|
||||
|
||||
return $this->ApiResponse($this->StockService->GetProductStockEntries($args['productId'], false, $allowSubproductSubstitution));
|
||||
}
|
||||
|
||||
public function ProductStockLocations(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
|
@ -707,8 +707,7 @@
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
,
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "best_fit_width",
|
||||
@ -1351,6 +1350,15 @@
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "include_sub_products",
|
||||
"required": false,
|
||||
"description": "If sub products should be included (if the given product is a parent product and in addition to the ones of the given product)",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
@ -82,7 +82,7 @@ function SumArrayValue($array, $propertyName)
|
||||
$sum = 0;
|
||||
foreach($array as $object)
|
||||
{
|
||||
$sum += $object->{$propertyName};
|
||||
$sum += floatval($object->{$propertyName});
|
||||
}
|
||||
|
||||
return $sum;
|
||||
|
@ -67,12 +67,13 @@ class RecipesService extends BaseService
|
||||
throw new \Exception('Recipe does not exist');
|
||||
}
|
||||
|
||||
$transactionId = uniqid();
|
||||
$recipePositions = $this->Database->recipes_pos_resolved()->where('recipe_id', $recipeId)->fetchAll();
|
||||
foreach ($recipePositions as $recipePosition)
|
||||
{
|
||||
if ($recipePosition->only_check_single_unit_in_stock == 0)
|
||||
{
|
||||
$this->StockService->ConsumeProduct($recipePosition->product_id, $recipePosition->recipe_amount, false, StockService::TRANSACTION_TYPE_CONSUME, 'default', $recipeId);
|
||||
$this->StockService->ConsumeProduct($recipePosition->product_id, $recipePosition->recipe_amount, false, StockService::TRANSACTION_TYPE_CONSUME, 'default', $recipeId, null, $transactionId, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,24 +184,29 @@ class StockService extends BaseService
|
||||
return $this->Database->stock()->where('id', $entryId)->fetch();
|
||||
}
|
||||
|
||||
public function GetProductStockEntries($productId, $excludeOpened = false)
|
||||
public function GetProductStockEntries($productId, $excludeOpened = false, $allowSubproductSubstitution = false)
|
||||
{
|
||||
// In order of next use:
|
||||
// First expiring first, then first in first out
|
||||
|
||||
if ($excludeOpened)
|
||||
$sqlWhereProductId = 'product_id = :1';
|
||||
if ($allowSubproductSubstitution)
|
||||
{
|
||||
return $this->Database->stock()->where('product_id = :1 AND open = 0', $productId)->orderBy('best_before_date', 'ASC')->orderBy('purchased_date', 'ASC')->fetchAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->Database->stock()->where('product_id', $productId)->orderBy('best_before_date', 'ASC')->orderBy('purchased_date', 'ASC')->fetchAll();
|
||||
}
|
||||
$sqlWhereProductId = '(product_id IN (SELECT sub_product_id FROM products_resolved WHERE parent_product_id = :1) OR product_id = :1)';
|
||||
}
|
||||
|
||||
public function GetProductStockEntriesForLocation($productId, $locationId, $excludeOpened = false)
|
||||
$sqlOpenAndWhere = 'AND open IN (0, 1)';
|
||||
if ($excludeOpened)
|
||||
{
|
||||
$stockEntries = $this->GetProductStockEntries($productId, $excludeOpened);
|
||||
$sqlOpenAndWhere = 'AND open = 0';
|
||||
}
|
||||
|
||||
return $this->Database->stock()->where($sqlWhereProductId . ' ' . $sqlOpenAndWhere, $productId)->orderBy('best_before_date', 'ASC')->orderBy('purchased_date', 'ASC')->fetchAll();
|
||||
}
|
||||
|
||||
public function GetProductStockEntriesForLocation($productId, $locationId, $excludeOpened = false, $allowSubproductSubstitution = false)
|
||||
{
|
||||
$stockEntries = $this->GetProductStockEntries($productId, $excludeOpened, $allowSubproductSubstitution);
|
||||
return FindAllObjectsInArrayByPropertyValue($stockEntries, 'location_id', $locationId);
|
||||
}
|
||||
|
||||
@ -286,14 +291,14 @@ class StockService extends BaseService
|
||||
}
|
||||
}
|
||||
|
||||
public function ConsumeProduct(int $productId, float $amount, bool $spoiled, $transactionType, $specificStockEntryId = 'default', $recipeId = null, $locationId = null, &$transactionId = null)
|
||||
public function ConsumeProduct(int $productId, float $amount, bool $spoiled, $transactionType, $specificStockEntryId = 'default', $recipeId = null, $locationId = null, &$transactionId = null, $allowSubproductSubstitution = false)
|
||||
{
|
||||
if (!$this->ProductExists($productId))
|
||||
{
|
||||
throw new \Exception('Product does not exist');
|
||||
}
|
||||
|
||||
if ($locationId !== null & !$this->LocationExists($locationId))
|
||||
if ($locationId !== null && !$this->LocationExists($locationId))
|
||||
{
|
||||
throw new \Exception('Location does not exist');
|
||||
}
|
||||
@ -316,15 +321,14 @@ class StockService extends BaseService
|
||||
{
|
||||
if ($locationId === null) // Consume from any location
|
||||
{
|
||||
$productStockAmount = $this->Database->stock()->where('product_id', $productId)->sum('amount');
|
||||
$potentialStockEntries = $this->GetProductStockEntries($productId);
|
||||
$potentialStockEntries = $this->GetProductStockEntries($productId, false, $allowSubproductSubstitution);
|
||||
}
|
||||
else // Consume only from the supplied location
|
||||
{
|
||||
$productStockAmount = $this->Database->stock()->where('product_id = :1 AND location_id = :2', $productId, $locationId)->sum('amount');
|
||||
$potentialStockEntries = $this->GetProductStockEntriesForLocation($productId, $locationId);
|
||||
$potentialStockEntries = $this->GetProductStockEntriesForLocation($productId, $locationId, false, $allowSubproductSubstitution);
|
||||
}
|
||||
|
||||
$productStockAmount = SumArrayValue($potentialStockEntries, 'amount');
|
||||
if ($amount > $productStockAmount)
|
||||
{
|
||||
throw new \Exception('Amount to be consumed cannot be > current stock amount (if supplied, at the desired location)');
|
||||
|
Loading…
x
Reference in New Issue
Block a user