diff --git a/controllers/StockApiController.php b/controllers/StockApiController.php index 04f9e04f..beecdafe 100644 --- a/controllers/StockApiController.php +++ b/controllers/StockApiController.php @@ -165,4 +165,17 @@ class StockApiController extends BaseApiController return $this->VoidApiActionResponse($response, false, 400, $ex->getMessage()); } } + + public function UndoBooking(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) + { + try + { + $this->ApiResponse($this->StockService->UndoBooking($args['stockLogId'])); + return $this->ApiResponse(array('success' => true)); + } + catch (\Exception $ex) + { + return $this->VoidApiActionResponse($response, false, 400, $ex->getMessage()); + } + } } diff --git a/grocy.openapi.json b/grocy.openapi.json index 133fa726..61097667 100644 --- a/grocy.openapi.json +++ b/grocy.openapi.json @@ -1288,6 +1288,47 @@ } } }, + "/stock/undo-booking/{stockLogId}": { + "get": { + "description": "Undoes a booking", + "tags": [ + "Stock" + ], + "parameters": [ + { + "in": "path", + "name": "stockLogId", + "required": true, + "description": "A valid stock log id", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A VoidApiActionResponse object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoidApiActionResponse" + } + } + } + }, + "400": { + "description": "A VoidApiActionResponse object (possible errors are: Not existing booking)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExampleVoidApiActionResponse" + } + } + } + } + } + } + }, "/recipes/add-not-fulfilled-products-to-shopping-list/{recipeId}": { "get": { "description": "Adds all missing products for the given recipe to the shopping list", diff --git a/migrations/0044.sql b/migrations/0044.sql new file mode 100644 index 00000000..6f2aac90 --- /dev/null +++ b/migrations/0044.sql @@ -0,0 +1,5 @@ +ALTER TABLE stock_log +ADD undone TINYINT NOT NULL DEFAULT 0 CHECK(undone IN (0, 1)); + +ALTER TABLE stock_log +ADD undone_timestamp DATETIME; diff --git a/routes.php b/routes.php index 3014f735..02964c67 100644 --- a/routes.php +++ b/routes.php @@ -114,6 +114,7 @@ $app->group('/api', function() $this->get('/stock/add-missing-products-to-shoppinglist', '\Grocy\Controllers\StockApiController:AddMissingProductsToShoppingList'); $this->get('/stock/clear-shopping-list', '\Grocy\Controllers\StockApiController:ClearShoppingList'); $this->get('/stock/external-barcode-lookup/{barcode}', '\Grocy\Controllers\StockApiController:ExternalBarcodeLookup'); + $this->get('/stock/undo-booking/{stockLogId}', '\Grocy\Controllers\StockApiController:UndoBooking'); // Recipes $this->get('/recipes/add-not-fulfilled-products-to-shopping-list/{recipeId}', '\Grocy\Controllers\RecipesApiController:AddNotFulfilledProductsToShoppingList'); diff --git a/services/StockService.php b/services/StockService.php index 9cf0c95d..1f421100 100644 --- a/services/StockService.php +++ b/services/StockService.php @@ -305,4 +305,52 @@ class StockService extends BaseService return $pluginOutput; } + + public function UndoBooking($stockLogId) + { + //TODO: This is not possible when the given booking has subsequent bookings + + $logRow = $this->Database->stock_log()->where('id = :1', $stockLogId)->fetch(); + + if ($logRow == null) + { + throw new \Exception('Booking does not exist'); + } + + if ($logRow->transaction_type === self::TRANSACTION_TYPE_PURCHASE) + { + // Remove corresponding stock entry + $stockRows = $this->Database->stock()->where('stock_id', $logRow->stock_id); + $stockRows->delete(); + + // Update log entry + $logRow->update(array( + 'undone' => 1, + 'undone_timestamp' => date('Y-m-d H:i:s') + )); + } + elseif ($logRow->transaction_type === self::TRANSACTION_TYPE_CONSUME || $logRow->transaction_type === self::TRANSACTION_TYPE_INVENTORY_CORRECTION) + { + // Add corresponding amount back to stock + $stockRow = $this->Database->stock()->createRow(array( + 'product_id' => $logRow->product_id, + 'amount' => $logRow->amount * -1, + 'best_before_date' => $logRow->best_before_date, + 'purchased_date' => $logRow->purchased_date, + 'stock_id' => $logRow->stock_id, + 'price' => $logRow->price + )); + $stockRow->save(); + + // Update log entry + $logRow->update(array( + 'undone' => 1, + 'undone_timestamp' => date('Y-m-d H:i:s') + )); + } + else + { + throw new \Exception('This booking cannot be undone'); + } + } }