From a711bbd8f6a693d6df38f41aa2c27a1fe398e6ab Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Thu, 9 Dec 2021 18:32:59 +0100 Subject: [PATCH] Print stock entry labels also on inventory when adding products (closes #1713) --- changelog/66_UNRELEASED_xxxx-xx-xx.md | 1 + controllers/StockApiController.php | 8 +++- grocy.openapi.json | 5 +++ public/viewjs/inventory.js | 61 ++++++++++++++++++++++++++- services/StockService.php | 4 +- views/inventory.blade.php | 20 +++++++++ 6 files changed, 94 insertions(+), 5 deletions(-) diff --git a/changelog/66_UNRELEASED_xxxx-xx-xx.md b/changelog/66_UNRELEASED_xxxx-xx-xx.md index fe2e3eb6..bbd323e7 100644 --- a/changelog/66_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/66_UNRELEASED_xxxx-xx-xx.md @@ -1,3 +1,4 @@ +- Stock entry labels get now also printed on inventory (only when adding products, same option "Stock entry label" like on the purchase page) - Optimized relative time display (also fixed a phrasing problem for some languages, e.g. Hungarian) (thanks @Tallyrald) - When using LDAP authentication, the configured `LDAP_UID_ATTR` is now used to compare if the user already exists instead of the username entered on the login page (that prevents creating multiple users if you entere the username in different notations) (thanks @FloSet) - Fixed that stock entry labels on purchase were printed, even when "No label" was selected (was only a problem when running label printer WebHooks server side) diff --git a/controllers/StockApiController.php b/controllers/StockApiController.php index 013b1032..e216f32f 100644 --- a/controllers/StockApiController.php +++ b/controllers/StockApiController.php @@ -495,7 +495,13 @@ class StockApiController extends BaseApiController $shoppingLocationId = $requestBody['shopping_location_id']; } - $transactionId = $this->getStockService()->InventoryProduct($args['productId'], $requestBody['new_amount'], $bestBeforeDate, $locationId, $price, $shoppingLocationId, $purchasedDate); + $stockLabelType = 0; + if (array_key_exists('stock_label_type', $requestBody) && is_numeric($requestBody['stock_label_type'])) + { + $stockLabelType = intval($requestBody['stock_label_type']); + } + + $transactionId = $this->getStockService()->InventoryProduct($args['productId'], $requestBody['new_amount'], $bestBeforeDate, $locationId, $price, $shoppingLocationId, $purchasedDate, $stockLabelType); $args['transactionId'] = $transactionId; return $this->StockTransactions($request, $response, $args); } diff --git a/grocy.openapi.json b/grocy.openapi.json index 0c86be19..6a6a8c26 100644 --- a/grocy.openapi.json +++ b/grocy.openapi.json @@ -2180,6 +2180,11 @@ "type": "number", "format": "number", "description": "If omitted, the last price of the product is used (only applies to added products)" + }, + "stock_label_type": { + "type": "number", + "format": "integer", + "description": "`1` = No label, `2` = Single label, `3` = Label per unit (only applies to added products)" } } } diff --git a/public/viewjs/inventory.js b/public/viewjs/inventory.js index a2509295..38e7a6da 100644 --- a/public/viewjs/inventory.js +++ b/public/viewjs/inventory.js @@ -22,6 +22,7 @@ var jsonData = {}; jsonData.new_amount = jsonForm.amount; jsonData.best_before_date = Grocy.Components.DateTimePicker.GetValue(); + jsonData.stock_label_type = jsonForm.stock_label_type; if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) { jsonData.shopping_location_id = Grocy.Components.ShoppingLocationPicker.GetValue(); @@ -67,6 +68,49 @@ ); } + if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER && parseFloat($("#display_amount").attr("data-estimated-booking-amount")) > 0) + { + if (Grocy.Webhooks.labelprinter !== undefined) + { + if (jsonForm.stock_label_type == 1) // Single label + { + var webhookData = {}; + webhookData.product = productDetails.product.name; + webhookData.grocycode = 'grcy:p:' + jsonForm.product_id + ":" + result[0].stock_id; + if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) + { + webhookData.due_date = __t('DD') + ': ' + result[0].best_before_date; + } + + Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, webhookData); + } + else if (jsonForm.stock_label_type == 2) // Label per unit + { + Grocy.Api.Get('stock/transactions/' + result[0].transaction_id, + function(stockEntries) + { + stockEntries.forEach(stockEntry => + { + var webhookData = {}; + webhookData.product = productDetails.product.name; + webhookData.grocycode = 'grcy:p:' + jsonForm.product_id + ":" + stockEntry.stock_id; + if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) + { + webhookData.due_date = __t('DD') + ': ' + result[0].best_before_date; + } + + Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, webhookData); + }); + }, + function(xhr) + { + console.error(xhr); + } + ); + } + } + } + Grocy.Api.Get('stock/products/' + jsonForm.product_id, function(result) { @@ -100,6 +144,12 @@ } Grocy.Components.ProductPicker.GetInputElement().focus(); Grocy.Components.ProductCard.Refresh(jsonForm.product_id); + + if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER) + { + $("#stock_label_type").val(0); + } + Grocy.FrontendHelpers.ValidateForm('inventory-form'); } }, @@ -182,6 +232,11 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e) } } + if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER) + { + $("#stock_label_type").val(productDetails.product.default_stock_label_type); + } + if (document.getElementById("product_id").getAttribute("barcode") != "null") { Grocy.Api.Get('objects/product_barcodes?query[]=barcode=' + document.getElementById("product_id").getAttribute("barcode"), @@ -303,7 +358,7 @@ Grocy.Components.DateTimePicker.GetInputElement().on('keypress', function(e) $('#display_amount').on('keyup', function(e) { var productId = Grocy.Components.ProductPicker.GetValue(); - var newAmount = parseInt($('#amount').val()); + var newAmount = parseFloat($('#amount').val()); if (productId) { @@ -318,7 +373,9 @@ $('#display_amount').on('keyup', function(e) containerWeight = parseFloat(productDetails.product.tare_weight); } - var estimatedBookingAmount = Math.abs(newAmount - productStockAmount - containerWeight); + var estimatedBookingAmount = (newAmount - productStockAmount - containerWeight).toFixed(Grocy.UserSettings.stock_decimal_places_amounts); + $("#display_amount").attr("data-estimated-booking-amount", estimatedBookingAmount); + estimatedBookingAmount = Math.abs(estimatedBookingAmount); $('#inventory-change-info').removeClass('d-none'); if (productDetails.product.enable_tare_weight_handling == 1 && newAmount < containerWeight) diff --git a/services/StockService.php b/services/StockService.php index 99348050..bfc74aed 100644 --- a/services/StockService.php +++ b/services/StockService.php @@ -855,7 +855,7 @@ class StockService extends BaseService return $this->getDatabase()->stock()->where('id', $entryId)->fetch(); } - public function InventoryProduct(int $productId, float $newAmount, $bestBeforeDate, $locationId = null, $price = null, $shoppingLocationId = null, $purchasedDate = null) + public function InventoryProduct(int $productId, float $newAmount, $bestBeforeDate, $locationId = null, $price = null, $shoppingLocationId = null, $purchasedDate = null, $stockLabelType = 0) { if (!$this->ProductExists($productId)) { @@ -902,7 +902,7 @@ class StockService extends BaseService $bookingAmount = $newAmount; } - return $this->AddProduct($productId, $bookingAmount, $bestBeforeDate, self::TRANSACTION_TYPE_INVENTORY_CORRECTION, $purchasedDate, $price, $locationId, $shoppingLocationId); + return $this->AddProduct($productId, $bookingAmount, $bestBeforeDate, self::TRANSACTION_TYPE_INVENTORY_CORRECTION, $purchasedDate, $price, $locationId, $shoppingLocationId, $unusedTransactionId, $stockLabelType); } elseif ($newAmount < $productDetails->stock_amount + $containerWeight) { diff --git a/views/inventory.blade.php b/views/inventory.blade.php index 82d1ba38..f9cf8d9f 100644 --- a/views/inventory.blade.php +++ b/views/inventory.blade.php @@ -112,6 +112,26 @@ )) @endif + @if(GROCY_FEATURE_FLAG_LABEL_PRINTER) +
+ + +
{{ $__t('A quantity unit is required') }}
+
+ @endif +