From ca9b8d068a614303a94852d5e04b693c10fc84b1 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Fri, 20 Sep 2019 10:45:58 +0200 Subject: [PATCH] Reuse existing routes for the `by-barcode` API routes and complete them (references #331) --- changelog/52_UNRELEASED_2019-xx-xx.md | 24 +++-- controllers/StockApiController.php | 110 ++++++-------------- grocy.openapi.json | 140 +++++++++++++++++++++++++- routes.php | 2 + 4 files changed, 183 insertions(+), 93 deletions(-) diff --git a/changelog/52_UNRELEASED_2019-xx-xx.md b/changelog/52_UNRELEASED_2019-xx-xx.md index 0b2d643e..5d200d3e 100644 --- a/changelog/52_UNRELEASED_2019-xx-xx.md +++ b/changelog/52_UNRELEASED_2019-xx-xx.md @@ -62,17 +62,19 @@ - It's now possible to test plural forms of quantity units (button on the quantity unit edit page, only visible if the current language requires more than 2 plural forms) ### API improvements & non-breaking changes - - New endpoint `/stock/shoppinglist/add-product` to add a product to a shopping list (thanks @Forceu) - - New endpoint `/stock/shoppinglist/remove-product` to remove a product from a shopping list (thanks @Forceu) - - New endpoint `/chores/executions/calculate-next-assignments` to (re)calculate next user assignments for a single or all chores - - New endpoint `/stock/products/by-barcode/{barcode}/add` to add a product to stock by its barcode - - New endpoint `/stock/products/by-barcode/{barcode}/consume` to remove a product to stock by its barcode - - New endpoint `/objects/{entity}/search/{searchString}` to search for objects by name (contains search) - - Endpoint `GET /files/{group}/{fileName}` can now also downscale pictures (see API documentation on [/api](https://demo-en.grocy.info/api)) - - When adding a product (through `stock/product/{productId}/add` or `stock/product/{productId}/inventory`) with omitted best before date and if the given product has "Default best before days" set, the best before date is calculated based on that (so far always today was used which is still the case when no date is supplied and also the product has no "Default best before days set) (thanks @Forceu) - - Field `stock_amount` of endpoint `/stock/products/{productId}` now returns `0` instead of `null` when the given product is not in stock (thanks @Forceu) - - Fixed that `/system/db-changed-time` always returned the current time (more or less) due to that that time is the database file modification time and the database is effectively changed on each request because of session information tracking - which now explicitly does not change the database file modification time, so this should work again to determine if any data changes happened - - It's now also possible to provide the API key via a query parameter (same name as the header, so `GROCY-API-KEY`) +- New endpoint `/objects/{entity}/search/{searchString}` to search for objects by name (contains search) +- New endpoint `/stock/shoppinglist/add-product` to add a product to a shopping list (thanks @Forceu) +- New endpoint `/stock/shoppinglist/remove-product` to remove a product from a shopping list (thanks @Forceu) +- New endpoint `/chores/executions/calculate-next-assignments` to (re)calculate next user assignments for a single or all chores +- New endpoint `/stock/products/by-barcode/{barcode}/add` to add a product to stock by its barcode +- New endpoint `/stock/products/by-barcode/{barcode}/consume` to remove a product to stock by its barcode +- New endpoint `/stock/products/by-barcode/{barcode}/inventory` to inventory a product by its barcode +- New endpoint `/stock/products/by-barcode/{barcode}/open` to mark a product as opened by its barcode +- Endpoint `GET /files/{group}/{fileName}` can now also downscale pictures (see API documentation on [/api](https://demo-en.grocy.info/api)) +- When adding a product (through `stock/product/{productId}/add` or `stock/product/{productId}/inventory`) with omitted best before date and if the given product has "Default best before days" set, the best before date is calculated based on that (so far always today was used which is still the case when no date is supplied and also the product has no "Default best before days set) (thanks @Forceu) +- Field `stock_amount` of endpoint `/stock/products/{productId}` now returns `0` instead of `null` when the given product is not in stock (thanks @Forceu) +- Fixed that `/system/db-changed-time` always returned the current time (more or less) due to that that time is the database file modification time and the database is effectively changed on each request because of session information tracking - which now explicitly does not change the database file modification time, so this should work again to determine if any data changes happened +- It's now also possible to provide the API key via a query parameter (same name as the header, so `GROCY-API-KEY`) #### Say thanks Because there were some questions about that in the past: If grocy is useful for you, [say thanks](https://grocy.info/#say-thanks)! diff --git a/controllers/StockApiController.php b/controllers/StockApiController.php index 9f5225fd..518fa701 100644 --- a/controllers/StockApiController.php +++ b/controllers/StockApiController.php @@ -102,48 +102,10 @@ class StockApiController extends BaseApiController public function AddProductByBarcode(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) { - $requestBody = $request->getParsedBody(); - try { - if ($requestBody === null) - { - throw new \Exception('Request body could not be parsed (probably invalid JSON format or missing/wrong Content-Type header)'); - } - - if (!array_key_exists('amount', $requestBody)) - { - throw new \Exception('An amount is required'); - } - - $bestBeforeDate = null; - if (array_key_exists('best_before_date', $requestBody) && IsIsoDate($requestBody['best_before_date'])) - { - $bestBeforeDate = $requestBody['best_before_date']; - } - - $price = null; - if (array_key_exists('price', $requestBody) && is_numeric($requestBody['price'])) - { - $price = $requestBody['price']; - } - - $locationId = null; - if (array_key_exists('location_id', $requestBody) && is_numeric($requestBody['location_id'])) - { - $locationId = $requestBody['location_id']; - } - - $transactionType = StockService::TRANSACTION_TYPE_PURCHASE; - if (array_key_exists('transaction_type', $requestBody) && !empty($requestBody['transactiontype'])) - { - $transactionType = $requestBody['transactiontype']; - } - - $productId = $this->StockService->GetProductIdFromBarcode($args['barcode']); - - $bookingId = $this->StockService->AddProduct($productId, $requestBody['amount'], $bestBeforeDate, $transactionType, date('Y-m-d'), $price, $locationId); - return $this->ApiResponse($this->Database->stock_log($bookingId)); + $args['productId'] = $this->StockService->GetProductIdFromBarcode($args['barcode']); + return $this->AddProduct($request, $response, $args); } catch (\Exception $ex) { @@ -202,48 +164,10 @@ class StockApiController extends BaseApiController public function ConsumeProductByBarcode(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) { - $requestBody = $request->getParsedBody(); - try { - if ($requestBody === null) - { - throw new \Exception('Request body could not be parsed (probably invalid JSON format or missing/wrong Content-Type header)'); - } - - if (!array_key_exists('amount', $requestBody)) - { - throw new \Exception('An amount is required'); - } - - $spoiled = false; - if (array_key_exists('spoiled', $requestBody)) - { - $spoiled = $requestBody['spoiled']; - } - - $transactionType = StockService::TRANSACTION_TYPE_CONSUME; - if (array_key_exists('transaction_type', $requestBody) && !empty($requestBody['transactiontype'])) - { - $transactionType = $requestBody['transactiontype']; - } - - $specificStockEntryId = 'default'; - if (array_key_exists('stock_entry_id', $requestBody) && !empty($requestBody['stock_entry_id'])) - { - $specificStockEntryId = $requestBody['stock_entry_id']; - } - - $recipeId = null; - if (array_key_exists('recipe_id', $requestBody) && is_numeric($requestBody['recipe_id'])) - { - $recipeId = $requestBody['recipe_id']; - } - - $productId = $this->StockService->GetProductIdFromBarcode($args['barcode']); - - $bookingId = $this->StockService->ConsumeProduct($productId, $requestBody['amount'], $spoiled, $transactionType, $specificStockEntryId, $recipeId); - return $this->ApiResponse($this->Database->stock_log($bookingId)); + $args['productId'] = $this->StockService->GetProductIdFromBarcode($args['barcode']); + return $this->ConsumeProduct($request, $response, $args); } catch (\Exception $ex) { @@ -294,6 +218,19 @@ class StockApiController extends BaseApiController } } + public function InventoryProductByBarcode(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) + { + try + { + $args['productId'] = $this->StockService->GetProductIdFromBarcode($args['barcode']); + return $this->InventoryProduct($request, $response, $args); + } + catch (\Exception $ex) + { + return $this->GenericErrorResponse($response, $ex->getMessage()); + } + } + public function OpenProduct(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) { $requestBody = $request->getParsedBody(); @@ -325,6 +262,19 @@ class StockApiController extends BaseApiController } } + public function OpenProductByBarcode(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) + { + try + { + $args['productId'] = $this->StockService->GetProductIdFromBarcode($args['barcode']); + return $this->OpenProduct($request, $response, $args); + } + catch (\Exception $ex) + { + return $this->GenericErrorResponse($response, $ex->getMessage()); + } + } + public function CurrentStock(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) { return $this->ApiResponse($this->StockService->GetCurrentStock()); diff --git a/grocy.openapi.json b/grocy.openapi.json index 818dff85..d8038e6c 100644 --- a/grocy.openapi.json +++ b/grocy.openapi.json @@ -1555,7 +1555,7 @@ }, "/stock/products/by-barcode/{barcode}/add": { "post": { - "summary": "Adds the given amount of the given product by its barcode to stock", + "summary": "Adds the given amount of the by its barcode given product to stock", "tags": [ "Stock" ], @@ -1637,7 +1637,7 @@ }, "/stock/products/by-barcode/{barcode}/consume": { "post": { - "summary": "Removes the given amount of the given product by its barcode from stock", + "summary": "Removes the given amount of the by its barcode given product from stock", "tags": [ "Stock" ], @@ -1713,6 +1713,142 @@ } } }, + "/stock/products/by-barcode/{barcode}/inventory": { + "post": { + "summary": "Inventories the by its barcode given product (adds/removes based on the given new amount)", + "tags": [ + "Stock" + ], + "parameters": [ + { + "in": "path", + "name": "barcode", + "required": true, + "description": "Barcode", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "new_amount": { + "type": "integer", + "description": "The new current amount for the given product - please note that when tare weight handling for the product is enabled, this needs to be the amount including the container weight (gross), the amount to be posted will be automatically calculated based on what is in stock and the defined tare weight" + }, + "best_before_date": { + "type": "string", + "format": "date", + "description": "The best before date which applies to added products" + }, + "location_id": { + "type": "number", + "format": "integer", + "description": "If omitted, the default location of the product is used (only applies to added products)" + }, + "price": { + "type": "number", + "format": "number", + "description": "If omitted, the last price of the product is used (only applies to added products)" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The operation was successful", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StockLogEntry" + } + } + } + }, + "400": { + "description": "The operation was not successful (possible errors are: Not existing product)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponse" + } + } + } + } + } + } + }, + "/stock/products/by-barcode/{barcode}/open": { + "post": { + "summary": "Marks the given amount of the by its barcode given product as opened", + "tags": [ + "Stock" + ], + "parameters": [ + { + "in": "path", + "name": "barcode", + "required": true, + "description": "Barcode", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "amount": { + "type": "number", + "description": "The amount to mark as opened" + }, + "stock_entry_id": { + "type": "string", + "description": "A specific stock entry id to open, if used, the amount has to be 1" + } + }, + "example": { + "amount": 1 + } + } + } + } + }, + "responses": { + "200": { + "description": "The operation was successful", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StockLogEntry" + } + } + } + }, + "400": { + "description": "The operation was not successful (possible errors are: Not existing product, given amount > current unopened stock amount)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponse" + } + } + } + } + } + } + }, "/stock/shoppinglist/add-missing-products": { "post": { "summary": "Adds currently missing products (below defined min. stock amount) to the given shopping list", diff --git a/routes.php b/routes.php index 90077de0..cb5a6672 100644 --- a/routes.php +++ b/routes.php @@ -168,6 +168,8 @@ $app->group('/api', function() $this->get('/stock/products/by-barcode/{barcode}', '\Grocy\Controllers\StockApiController:ProductDetailsByBarcode'); $this->post('/stock/products/by-barcode/{barcode}/add', '\Grocy\Controllers\StockApiController:AddProductByBarcode'); $this->post('/stock/products/by-barcode/{barcode}/consume', '\Grocy\Controllers\StockApiController:ConsumeProductByBarcode'); + $this->post('/stock/products/by-barcode/{barcode}/inventory', '\Grocy\Controllers\StockApiController:InventoryProductByBarcode'); + $this->post('/stock/products/by-barcode/{barcode}/open', '\Grocy\Controllers\StockApiController:OpenProductByBarcode'); $this->post('/stock/bookings/{bookingId}/undo', '\Grocy\Controllers\StockApiController:UndoBooking'); $this->get('/stock/barcodes/external-lookup', '\Grocy\Controllers\StockApiController:ExternalBarcodeLookup'); }