diff --git a/controllers/StockController.php b/controllers/StockController.php index 3cbcd174..f704c84e 100644 --- a/controllers/StockController.php +++ b/controllers/StockController.php @@ -16,7 +16,7 @@ class StockController extends BaseController $nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['stock_expring_soon_days']; return $this->renderPage($response, 'stockoverview', [ - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'quantityunits' => $this->getDatabase()->quantity_units()->orderBy('name'), 'locations' => $this->getDatabase()->locations()->orderBy('name'), 'currentStock' => $this->getStockService()->GetCurrentStock(true), @@ -36,7 +36,7 @@ class StockController extends BaseController $nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['stock_expring_soon_days']; return $this->renderPage($response, 'stockentries', [ - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'quantityunits' => $this->getDatabase()->quantity_units()->orderBy('name'), 'locations' => $this->getDatabase()->locations()->orderBy('name'), 'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name'), @@ -54,7 +54,7 @@ class StockController extends BaseController $productBarcodes = $this->getDatabaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); return $this->renderPage($response, 'purchase', [ - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'barcodes' => $productBarcodes, 'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name'), 'locations' => $this->getDatabase()->locations()->orderBy('name') @@ -67,7 +67,7 @@ class StockController extends BaseController $productBarcodes = $this->getDatabaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); return $this->renderPage($response, 'consume', [ - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'barcodes' => $productBarcodes, 'recipes' => $this->getDatabase()->recipes()->orderBy('name'), 'locations' => $this->getDatabase()->locations()->orderBy('name') @@ -80,7 +80,7 @@ class StockController extends BaseController $productBarcodes = $this->getDatabaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); return $this->renderPage($response, 'transfer', [ - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'barcodes' => $productBarcodes, 'recipes' => $this->getDatabase()->recipes()->orderBy('name'), 'locations' => $this->getDatabase()->locations()->orderBy('name') @@ -93,7 +93,7 @@ class StockController extends BaseController $productBarcodes = $this->getDatabaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ); return $this->renderPage($response, 'inventory', [ - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'barcodes' => $productBarcodes, 'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name'), 'locations' => $this->getDatabase()->locations()->orderBy('name') @@ -104,7 +104,7 @@ class StockController extends BaseController { return $this->renderPage($response, 'stockentryform', [ 'stockEntry' => $this->getDatabase()->stock()->where('id', $args['entryId'])->fetch(), - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name'), 'locations' => $this->getDatabase()->locations()->orderBy('name') ]); @@ -120,7 +120,7 @@ class StockController extends BaseController return $this->renderPage($response, 'shoppinglist', [ 'listItems' => $this->getDatabase()->shopping_list()->where('shopping_list_id = :1', $listId), - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'quantityunits' => $this->getDatabase()->quantity_units()->orderBy('name'), 'missingProducts' => $this->getStockService()->GetMissingProducts(), 'productGroups' => $this->getDatabase()->product_groups()->orderBy('name'), @@ -174,7 +174,7 @@ class StockController extends BaseController { return $this->renderPage($response, 'productgroups', [ 'productGroups' => $this->getDatabase()->product_groups()->orderBy('name'), - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'userfields' => $this->getUserfieldsService()->GetFields('product_groups'), 'userfieldValues' => $this->getUserfieldsService()->GetAllValues('product_groups') ]); @@ -200,7 +200,7 @@ class StockController extends BaseController 'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name'), 'productgroups' => $this->getDatabase()->product_groups()->orderBy('name'), 'userfields' => $this->getUserfieldsService()->GetFields('products'), - 'products' => $this->getDatabase()->products()->where('parent_product_id IS NULL')->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('parent_product_id IS NULL and active = 1')->orderBy('name'), 'isSubProductOfOthers' => false, 'mode' => 'create' ]); @@ -217,7 +217,7 @@ class StockController extends BaseController 'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name'), 'productgroups' => $this->getDatabase()->product_groups()->orderBy('name'), 'userfields' => $this->getUserfieldsService()->GetFields('products'), - 'products' => $this->getDatabase()->products()->where('id != :1 AND parent_product_id IS NULL', $product->id)->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('id != :1 AND parent_product_id IS NULL and active = 1', $product->id)->orderBy('name'), 'isSubProductOfOthers' => $this->getDatabase()->products()->where('parent_product_id = :1', $product->id)->count() !== 0, 'mode' => 'edit', 'quConversions' => $this->getDatabase()->quantity_unit_conversions() @@ -314,7 +314,7 @@ class StockController extends BaseController if ($args['itemId'] == 'new') { return $this->renderPage($response, 'shoppinglistitemform', [ - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'shoppingLists' => $this->getDatabase()->shopping_lists()->orderBy('name'), 'mode' => 'create' ]); @@ -323,7 +323,7 @@ class StockController extends BaseController { return $this->renderPage($response, 'shoppinglistitemform', [ 'listItem' => $this->getDatabase()->shopping_list($args['itemId']), - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'shoppingLists' => $this->getDatabase()->shopping_lists()->orderBy('name'), 'mode' => 'edit' ]); @@ -357,7 +357,7 @@ class StockController extends BaseController return $this->renderPage($response, 'stockjournal', [ 'stockLog' => $this->getDatabase()->stock_log()->orderBy('row_created_timestamp', 'DESC'), 'locations' => $this->getDatabase()->locations()->orderBy('name'), - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'quantityunits' => $this->getDatabase()->quantity_units()->orderBy('name') ]); } @@ -365,7 +365,7 @@ class StockController extends BaseController public function LocationContentSheet(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { return $this->renderPage($response, 'locationcontentsheet', [ - 'products' => $this->getDatabase()->products()->orderBy('name'), + 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'quantityunits' => $this->getDatabase()->quantity_units()->orderBy('name'), 'locations' => $this->getDatabase()->locations()->orderBy('name'), 'currentStockLocationContent' => $this->getStockService()->GetCurrentStockLocationContent() diff --git a/migrations/0103.sql b/migrations/0103.sql index 6e3efd1a..c98885dc 100644 --- a/migrations/0103.sql +++ b/migrations/0103.sql @@ -35,6 +35,7 @@ CREATE TABLE products ( name TEXT NOT NULL UNIQUE, description TEXT, product_group_id INTEGER, + active TINYINT NOT NULL DEFAULT 1, location_id INTEGER NOT NULL, shopping_location_id INTEGER, qu_id_purchase INTEGER NOT NULL, @@ -75,7 +76,7 @@ SELECT IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = s.product_id AND location_id = s.location_id AND open = 1), 0) AS amount_opened FROM stock s JOIN products p - ON s.product_id = p.id + ON s.product_id = p.id and p.active = 1 GROUP BY IFNULL(s.location_id, p.location_id), s.product_id; DROP VIEW stock_current; @@ -95,9 +96,9 @@ FROM products_resolved pr JOIN stock s ON pr.sub_product_id = s.product_id JOIN products p_parent - ON pr.parent_product_id = p_parent.id + ON pr.parent_product_id = p_parent.id and p_parent.active = 1 JOIN products p_sub - ON pr.sub_product_id = p_sub.id + ON pr.sub_product_id = p_sub.id and p_sub.active = 1 LEFT JOIN quantity_unit_conversions_resolved qucr ON pr.sub_product_id = qucr.product_id AND p_sub.qu_id_stock = qucr.from_qu_id @@ -124,3 +125,21 @@ JOIN stock s WHERE pr.parent_product_id != pr.sub_product_id GROUP BY pr.sub_product_id HAVING SUM(s.amount) > 0; + +DROP VIEW products_resolved; +CREATE VIEW products_resolved AS +SELECT + p.parent_product_id parent_product_id, + p.id as sub_product_id +FROM products p + WHERE p.parent_product_id IS NOT NULL + and p.active = 1 + +UNION + +SELECT + p.id parent_product_id, + p.id as sub_product_id +FROM products p + WHERE p.parent_product_id IS NULL + AND p.active = 1; diff --git a/migrations/0104.sql b/migrations/0104.sql index 6352549e..17a3260c 100644 --- a/migrations/0104.sql +++ b/migrations/0104.sql @@ -77,7 +77,8 @@ SELECT rp.note, rp.variable_amount AS recipe_variable_amount, rp.only_check_single_unit_in_stock, - rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) * IFNULL(p.calories, 0) AS calories + rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) * IFNULL(p.calories, 0) AS calories, + p.active AS product_active FROM recipes r JOIN recipes_nestings_resolved rnr ON r.id = rnr.recipe_id @@ -125,7 +126,8 @@ SELECT rp.note, rp.variable_amount AS recipe_variable_amount, rp.only_check_single_unit_in_stock, - rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) * IFNULL(p.calories, 0) AS calories + rp.amount * (r.desired_servings*1.0 / r.base_servings*1.0) * (rnr.includes_servings*1.0 / CASE WHEN rnr.recipe_id != rnr.includes_recipe_id THEN rnrr.base_servings*1.0 ELSE 1 END) * IFNULL(p.calories, 0) AS calories, + p.active AS product_active FROM recipes r JOIN recipes_nestings_resolved rnr ON r.id = rnr.recipe_id diff --git a/public/viewjs/products.js b/public/viewjs/products.js index 2a040a7d..ace04d3d 100644 --- a/public/viewjs/products.js +++ b/public/viewjs/products.js @@ -49,7 +49,7 @@ $(document).on('click', '.product-delete-button', function (e) if (stockAmount.toString() == "0") { bootbox.confirm({ - message: __t('Are you sure to delete product "%s"?', objectName), + message: __t('Are you sure you want to deactivate this product "%s"?', objectName), closeButton: false, buttons: { confirm: { @@ -65,7 +65,9 @@ $(document).on('click', '.product-delete-button', function (e) { if (result === true) { - Grocy.Api.Delete('objects/products/' + objectId, {}, + jsonData = {}; + jsonData.active = 0; + Grocy.Api.Put('objects/products/' + objectId, jsonData, function (result) { window.location.href = U('/products'); @@ -82,8 +84,8 @@ $(document).on('click', '.product-delete-button', function (e) else { bootbox.alert({ - title: __t('Delete not possible'), - message: __t('This product cannot be deleted because it is in stock, please remove the stock amount first.') + '

' + __t('Stock amount') + ': ' + stockAmount + ' ' + __n(stockAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), + title: __t('Deactivation not possible'), + message: __t('This product cannot be deactivated because it is in stock, please remove the stock amount first.') + '

' + __t('Stock amount') + ': ' + stockAmount + ' ' + __n(stockAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), closeButton: false }); } diff --git a/services/StockService.php b/services/StockService.php index 2f12c198..2abfae88 100644 --- a/services/StockService.php +++ b/services/StockService.php @@ -96,7 +96,7 @@ class StockService extends BaseService { if (!$this->ProductExists($productId)) { - throw new \Exception('Product does not exist'); + throw new \Exception('Product does not exist or is inactive'); } $stockCurrentRow = FindObjectinArrayByPropertyValue($this->GetCurrentStock(), 'product_id', $productId); @@ -168,7 +168,7 @@ class StockService extends BaseService { if (!$this->ProductExists($productId)) { - throw new \Exception('Product does not exist'); + throw new \Exception('Product does not exist or is inactive'); } $returnData = array(); @@ -220,7 +220,7 @@ class StockService extends BaseService { if (!$this->ProductExists($productId)) { - throw new \Exception('Product does not exist'); + throw new \Exception('Product does not exist or is inactive'); } // Tare weight handling @@ -305,7 +305,7 @@ class StockService extends BaseService { if (!$this->ProductExists($productId)) { - throw new \Exception('Product does not exist'); + throw new \Exception('Product does not exist or is inactive'); } if ($locationId !== null && !$this->LocationExists($locationId)) @@ -424,7 +424,7 @@ class StockService extends BaseService { if (!$this->ProductExists($productId)) { - throw new \Exception('Product does not exist'); + throw new \Exception('Product does not exist or is inactive'); } if (!$this->LocationExists($locationIdFrom)) @@ -681,7 +681,7 @@ class StockService extends BaseService { if (!$this->ProductExists($productId)) { - throw new \Exception('Product does not exist'); + throw new \Exception('Product does not exist or is inactive'); } $productDetails = (object)$this->GetProductDetails($productId); @@ -737,7 +737,7 @@ class StockService extends BaseService { if (!$this->ProductExists($productId)) { - throw new \Exception('Product does not exist'); + throw new \Exception('Product does not exist or is inactive'); } $productStockAmountUnopened = $this->getDatabase()->stock()->where('product_id = :1 AND open = 0', $productId)->sum('amount'); @@ -917,7 +917,7 @@ class StockService extends BaseService if (!$this->ProductExists($productId)) { - throw new \Exception('Product does not exist'); + throw new \Exception('Product does not exist or is inactive'); } $alreadyExistingEntry = $this->getDatabase()->shopping_list()->where('product_id = :1 AND shopping_list_id = :2', $productId, $listId)->fetch(); @@ -943,7 +943,7 @@ class StockService extends BaseService private function ProductExists($productId) { - $productRow = $this->getDatabase()->products()->where('id = :1', $productId)->fetch(); + $productRow = $this->getDatabase()->products()->where('id = :1 and active = 1', $productId)->fetch(); return $productRow !== null; } diff --git a/views/productform.blade.php b/views/productform.blade.php index 5851f704..6af4090c 100644 --- a/views/productform.blade.php +++ b/views/productform.blade.php @@ -46,6 +46,14 @@
{{ $__t('A name is required') }}
+
+
+ + active == 1) checked @endif class="form-check-input" type="checkbox" id="active" name="active" value="1"> + +
+
+ @php $prefillById = ''; if($mode=='edit') { $prefillById = $product->parent_product_id; } @endphp @php $hint = ''; diff --git a/views/products.blade.php b/views/products.blade.php index 42477567..94505c64 100644 --- a/views/products.blade.php +++ b/views/products.blade.php @@ -81,12 +81,12 @@ - + - {{ $product->name }}@if(!empty($product->picture_file_name)) @endif + @if($product->active == 0) (deactivated) @endif {{ $product->name }}@if(!empty($product->picture_file_name)) @endif {{ FindObjectInArrayByPropertyValue($locations, 'id', $product->location_id)->name }} diff --git a/views/recipes.blade.php b/views/recipes.blade.php index 918039c1..1408f87b 100644 --- a/views/recipes.blade.php +++ b/views/recipes.blade.php @@ -310,6 +310,9 @@
{{ $selectedRecipePosition->product_group }}
@endif
  • + @if($selectedRecipePosition->product_active == 0) +
    {{ $__t('Deactivated Product') }}
    + @endif @php $product = FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id); $productQuConversions = FindAllObjectsInArrayByPropertyValue($quantityUnitConversionsResolved, 'product_id', $product->id);