diff --git a/controllers/StockApiController.php b/controllers/StockApiController.php index e2975ca0..6205a49d 100644 --- a/controllers/StockApiController.php +++ b/controllers/StockApiController.php @@ -66,13 +66,19 @@ class StockApiController extends BaseApiController $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']; } - $bookingId = $this->StockService->AddProduct($args['productId'], $requestBody['amount'], $bestBeforeDate, $transactionType, date('Y-m-d'), $price); + $bookingId = $this->StockService->AddProduct($args['productId'], $requestBody['amount'], $bestBeforeDate, $transactionType, date('Y-m-d'), $price, $locationId); return $this->ApiResponse(array('booking_id' => $bookingId)); } catch (\Exception $ex) diff --git a/controllers/StockController.php b/controllers/StockController.php index 84d303aa..7c283320 100644 --- a/controllers/StockController.php +++ b/controllers/StockController.php @@ -22,6 +22,7 @@ class StockController extends BaseController 'quantityunits' => $this->Database->quantity_units()->orderBy('name'), 'locations' => $this->Database->locations()->orderBy('name'), 'currentStock' => $this->StockService->GetCurrentStock(), + 'currentStockLocations' => $this->StockService->GetCurrentStockLocations(), 'missingProducts' => $this->StockService->GetMissingProducts(), 'nextXDays' => 5, 'productGroups' => $this->Database->product_groups()->orderBy('name') @@ -31,7 +32,8 @@ class StockController extends BaseController public function Purchase(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) { return $this->AppContainer->view->render($response, 'purchase', [ - 'products' => $this->Database->products()->orderBy('name') + 'products' => $this->Database->products()->orderBy('name'), + 'locations' => $this->Database->locations()->orderBy('name') ]); } diff --git a/grocy.openapi.json b/grocy.openapi.json index 606e5c56..0ca90fc3 100644 --- a/grocy.openapi.json +++ b/grocy.openapi.json @@ -1021,7 +1021,8 @@ "type": "object", "properties": { "amount": { - "type": "double" + "type": "number", + "format": "double" }, "best_before_date": { "type": "string", @@ -1035,6 +1036,11 @@ "type": "number", "format": "double", "description": "The price per purchase quantity unit in configured currency" + }, + "location_id": { + "type": "number", + "format": "integer", + "description": "If omitted, the default location of the product is used" } }, "example": { diff --git a/migrations/0051.sql b/migrations/0051.sql new file mode 100644 index 00000000..c2584543 --- /dev/null +++ b/migrations/0051.sql @@ -0,0 +1,15 @@ +ALTER TABLE stock +ADD location_id INTEGER; + +ALTER TABLE stock_log +ADD location_id INTEGER; + +CREATE VIEW stock_current_locations +AS +SELECT + s.product_id, + IFNULL(s.location_id, p.location_id) AS location_id +FROM stock s +JOIN products p + ON s.product_id = p.id +GROUP BY s.product_id, IFNULL(s.location_id, p.location_id); diff --git a/public/viewjs/purchase.js b/public/viewjs/purchase.js index 5c2f0e3e..8d30d36c 100644 --- a/public/viewjs/purchase.js +++ b/public/viewjs/purchase.js @@ -20,6 +20,7 @@ jsonData.amount = amount; jsonData.best_before_date = Grocy.Components.DateTimePicker.GetValue(); jsonData.price = price; + jsonData.location_id = jsonForm.location_id; Grocy.Api.Post('stock/products/' + jsonForm.product_id + '/add', jsonData, function(result) @@ -65,6 +66,7 @@ toastr.success(successMessage); $('#amount').val(0); $('#price').val(''); + $('#location_id').val(''); Grocy.Components.DateTimePicker.Clear(); Grocy.Components.ProductPicker.SetValue(''); Grocy.Components.ProductPicker.GetInputElement().focus(); @@ -99,6 +101,7 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e) { $('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name); $('#price').val(productDetails.last_price); + $('#location_id').val(productDetails.product.location_id); if (productDetails.product.allow_partial_units_in_stock == 1) { diff --git a/services/StockService.php b/services/StockService.php index c4aa5294..da73ad81 100644 --- a/services/StockService.php +++ b/services/StockService.php @@ -11,18 +11,24 @@ class StockService extends BaseService public function GetCurrentStock($includeNotInStockButMissingProducts = false) { - $sql = 'SELECT * from stock_current'; + $sql = 'SELECT * FROM stock_current'; if ($includeNotInStockButMissingProducts) { - $sql = 'SELECT * from stock_current WHERE best_before_date IS NOT NULL'; + $sql = 'SELECT * FROM stock_current WHERE best_before_date IS NOT NULL'; } return $this->DatabaseService->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); } + public function GetCurrentStockLocations() + { + $sql = 'SELECT * FROM stock_current_locations'; + return $this->DatabaseService->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); + } + public function GetMissingProducts() { - $sql = 'SELECT * from stock_missing_products'; + $sql = 'SELECT * FROM stock_missing_products'; return $this->DatabaseService->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); } @@ -109,7 +115,7 @@ class StockService extends BaseService } } - public function AddProduct(int $productId, float $amount, string $bestBeforeDate, $transactionType, $purchasedDate, $price) + public function AddProduct(int $productId, float $amount, string $bestBeforeDate, $transactionType, $purchasedDate, $price, $locationId = null) { if (!$this->ProductExists($productId)) { @@ -127,7 +133,8 @@ class StockService extends BaseService 'purchased_date' => $purchasedDate, 'stock_id' => $stockId, 'transaction_type' => $transactionType, - 'price' => $price + 'price' => $price, + 'location_id' => $locationId )); $logRow->save(); @@ -139,7 +146,8 @@ class StockService extends BaseService 'best_before_date' => $bestBeforeDate, 'purchased_date' => $purchasedDate, 'stock_id' => $stockId, - 'price' => $price + 'price' => $price, + 'location_id' => $locationId )); $stockRow->save(); diff --git a/views/purchase.blade.php b/views/purchase.blade.php index e5d209ce..e12d1a3f 100644 --- a/views/purchase.blade.php +++ b/views/purchase.blade.php @@ -49,6 +49,17 @@ 'isRequired' => false )) +
+ + +
{{ $L('A location is required') }}
+
+ diff --git a/views/stockoverview.blade.php b/views/stockoverview.blade.php index 1ba746b0..b95025d5 100644 --- a/views/stockoverview.blade.php +++ b/views/stockoverview.blade.php @@ -119,7 +119,9 @@ - {{ FindObjectInArrayByPropertyValue($locations, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->location_id)->name }} + @foreach(FindAllObjectsInArrayByPropertyValue($currentStockLocations, 'product_id', $currentStockEntry->product_id) as $locationsForProduct) + {{ FindObjectInArrayByPropertyValue($locations, 'id', $locationsForProduct->location_id)->name }} + @endforeach @if($currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $currentStockEntry->amount > 0) expired @elseif($currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime("+$nextXDays days")) && $currentStockEntry->amount > 0) expiring @elseif (FindObjectInArrayByPropertyValue($missingProducts, 'id', $currentStockEntry->product_id) !== null) belowminstockamount @endif