diff --git a/composer.json b/composer.json
index 33b0a597..8f3f6abd 100644
--- a/composer.json
+++ b/composer.json
@@ -12,7 +12,8 @@
"erusev/parsedown": "^1.7",
"gumlet/php-image-resize": "^1.9",
"ezyang/htmlpurifier": "^4.13",
- "jucksearm/php-barcode": "^1.0"
+ "jucksearm/php-barcode": "^1.0",
+ "guzzlehttp/guzzle": "^7.0"
},
"autoload": {
"psr-4": {
diff --git a/composer.lock b/composer.lock
index 9480cbc3..dbb53b2c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "2f15993a36a55cfc2018969a144c73b0",
+ "content-hash": "b8b8e77618038c44c21edac2c31c4b67",
"packages": [
{
"name": "doctrine/inflector",
@@ -508,6 +508,239 @@
},
"time": "2019-01-01T13:53:00+00:00"
},
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "7.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "7008573787b430c1c1f650e3722d9bba59967628"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628",
+ "reference": "7008573787b430c1c1f650e3722d9bba59967628",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^1.4",
+ "guzzlehttp/psr7": "^1.7 || ^2.0",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4.1",
+ "ext-curl": "*",
+ "php-http/client-integration-tests": "^3.0",
+ "phpunit/phpunit": "^8.5.5 || ^9.3.5",
+ "psr/log": "^1.1"
+ },
+ "suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "psr-18",
+ "psr-7",
+ "rest",
+ "web service"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/alexeyshockov",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/gmponos",
+ "type": "github"
+ }
+ ],
+ "time": "2021-03-23T11:33:13+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d",
+ "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^4.4 || ^5.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/1.4.1"
+ },
+ "time": "2021-03-07T09:25:29+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "1.8.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "dc960a912984efb74d0a90222870c72c87f10c91"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91",
+ "reference": "dc960a912984efb74d0a90222870c72c87f10c91",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "psr/http-message": "~1.0",
+ "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "ext-zlib": "*",
+ "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Schultze",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/1.8.2"
+ },
+ "time": "2021-04-26T09:17:50+00:00"
+ },
{
"name": "illuminate/container",
"version": "v5.8.36",
@@ -1412,6 +1645,58 @@
},
"time": "2017-02-14T16:28:37+00:00"
},
+ {
+ "name": "psr/http-client",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client/tree/master"
+ },
+ "time": "2020-06-29T06:28:15+00:00"
+ },
{
"name": "psr/http-factory",
"version": "1.0.1",
diff --git a/config-dist.php b/config-dist.php
index bc8f003e..37411082 100644
--- a/config-dist.php
+++ b/config-dist.php
@@ -159,6 +159,21 @@ DefaultUserSetting('quagga2_patchsize', 'medium');
DefaultUserSetting('quagga2_frequency', 10);
DefaultUserSetting('quagga2_debug', true);
+// Label Printer Settings
+// This is the URI that grocy will POST to when asked to print a label.
+Setting('LABEL_PRINTER_WEBHOOK', '');
+// This setting decides whether the webhook will be called server- or clientside.
+// If the machine grocy runs on has a network connection to the host
+// the webhook receiver is on, this is probably a good idea.
+// If, for example, grocy runs in the cloud and your printer daemon
+// runs locally to you, set this to false to let your browser call
+// the webhook instead.
+Setting('LABEL_PRINTER_RUN_SERVER', true);
+// Additional Parameters supplied to the webhook.
+Setting('LABEL_PRINTER_PARAMS', ['font_family' => 'Source Sans Pro (Regular)']);
+// Use JSON or normal POST request variables?
+Setting('LABEL_PRINTER_HOOK_JSON', false);
+
// Feature flags
// grocy was initially about "stock management for your household", many other things
// came and still come by, because they are useful - here you can disable the parts
@@ -172,6 +187,7 @@ Setting('FEATURE_FLAG_TASKS', true);
Setting('FEATURE_FLAG_BATTERIES', true);
Setting('FEATURE_FLAG_EQUIPMENT', true);
Setting('FEATURE_FLAG_CALENDAR', true);
+Setting('FEATURE_FLAG_LABELPRINTER', true);
// Sub feature flags
Setting('FEATURE_FLAG_STOCK_PRICE_TRACKING', true);
diff --git a/controllers/StockApiController.php b/controllers/StockApiController.php
index 542631ba..10735f21 100644
--- a/controllers/StockApiController.php
+++ b/controllers/StockApiController.php
@@ -4,6 +4,7 @@ namespace Grocy\Controllers;
use Grocy\Controllers\Users\User;
use Grocy\Services\StockService;
+use Grocy\Helpers\WebhookRunner;
class StockApiController extends BaseApiController
{
@@ -138,8 +139,14 @@ class StockApiController extends BaseApiController
{
$transactionType = $requestBody['transactiontype'];
}
+ $runPrinterWebhook = false;
+ if (array_key_exists('print_stock_label', $requestBody) && boolval($requestBody['print_stock_label']))
+ {
+ $runPrinterWebhook = true;
+ }
+
+ $transactionId = $this->getStockService()->AddProduct($args['productId'], $requestBody['amount'], $bestBeforeDate, $transactionType, $purchasedDate, $price, $locationId, $shoppingLocationId, $unusedTransactionId, $runPrinterWebhook);
- $transactionId = $this->getStockService()->AddProduct($args['productId'], $requestBody['amount'], $bestBeforeDate, $transactionType, $purchasedDate, $price, $locationId, $shoppingLocationId);
$args['transactionId'] = $transactionId;
return $this->StockTransactions($request, $response, $args);
}
diff --git a/helpers/WebhookRunner.php b/helpers/WebhookRunner.php
new file mode 100644
index 00000000..a7a1aa25
--- /dev/null
+++ b/helpers/WebhookRunner.php
@@ -0,0 +1,48 @@
+client = new Client(['timeout' => 2.0]);
+ }
+
+ public function run($url, $args, $json = false)
+ {
+ $reqArgs = [];
+ if ($json)
+ {
+ $reqArgs = ['json' => $args];
+ }
+ else
+ {
+ $reqArgs = ['form_params' => $args];
+ }
+ try
+ {
+ file_put_contents('php://stderr', 'Running Webhook: ' . $url . "\n" . print_r($reqArgs, true));
+
+ $this->client->request('POST', $url, $reqArgs);
+ }
+ catch (RequestException $e)
+ {
+ file_put_contents('php://stderr', 'Webhook failed: ' . $url . "\n" . $e->getMessage());
+ }
+ }
+
+ public function runAll($urls, $args)
+ {
+ foreach ($urls as $url)
+ {
+ $this->run($url, $args);
+ }
+ }
+}
diff --git a/localization/de/strings.po b/localization/de/strings.po
index 35cd5934..c103006c 100644
--- a/localization/de/strings.po
+++ b/localization/de/strings.po
@@ -2394,4 +2394,7 @@ msgid "Open print label in new Window"
msgstr "Druck-Label in neuem Fenster öffnen"
msgid "DD"
-msgstr "MHD"
\ No newline at end of file
+msgstr "MHD"
+
+msgid "Print stock label"
+msgstr "Etikett drucken"
\ No newline at end of file
diff --git a/localization/strings.pot b/localization/strings.pot
index a49d2312..21be827b 100644
--- a/localization/strings.pot
+++ b/localization/strings.pot
@@ -2106,3 +2106,6 @@ msgstr ""
msgid "DD"
msgstr ""
+
+msgid "Print stock label"
+msgstr ""
diff --git a/public/js/grocy.js b/public/js/grocy.js
index 71e76528..4e956250 100644
--- a/public/js/grocy.js
+++ b/public/js/grocy.js
@@ -476,6 +476,16 @@ Grocy.FrontendHelpers.DeleteUserSetting = function(settingsKey, reloadPageOnSucc
);
}
+Grocy.FrontendHelpers.RunWebhook = function(webhook, data)
+{
+ Object.assign(data, webhook.extra_data);
+
+ $.post(webhook.hook, data).fail(function(req, status, errorThrown)
+ {
+ Grocy.FrontendHelpers.ShowGenericError(__t("Unable to connect to webhook.", { "status": status, "errorThrown": errorThrown }));
+ });
+}
+
$(document).on("keyup paste change", "input, textarea", function()
{
$(this).closest("form").addClass("is-dirty");
diff --git a/public/viewjs/purchase.js b/public/viewjs/purchase.js
index ba47ca7d..760a1cd8 100644
--- a/public/viewjs/purchase.js
+++ b/public/viewjs/purchase.js
@@ -23,6 +23,7 @@ $('#save-purchase-button').on('click', function(e)
{
var jsonData = {};
jsonData.amount = jsonForm.amount;
+ jsonData.print_stock_label = jsonForm.print_stock_label
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
{
@@ -116,6 +117,21 @@ $('#save-purchase-button').on('click', function(e)
}
var successMessage = __t('Added %1$s of %2$s to stock', amountMessage + " " + __n(amountMessage, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + __t("Undo") + '';
+ if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABELPRINTER)
+ {
+ if (Grocy.Webhooks.labelprinter !== undefined)
+ {
+ var post_data = {};
+ post_data.product = productDetails.product.name;
+ post_data.grocycode = 'grcy:p:' + jsonForm.product_id + ":" + result[0].stock_id
+ if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
+ {
+ post_data.duedate = __t('DD') + ': ' + result[0].best_before_date
+ }
+ Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, post_data);
+ }
+ }
+
if (GetUriParam("embedded") !== undefined)
{
window.parent.postMessage(WindowMessageBag("ProductChanged", jsonForm.product_id), Grocy.BaseUrl);
diff --git a/services/StockService.php b/services/StockService.php
index d7efedce..23c321fb 100644
--- a/services/StockService.php
+++ b/services/StockService.php
@@ -1,7 +1,9 @@
amount < $amountToAdd)
- {
+ {
$alreadyExistingEntry->update([
'amount' => $amountToAdd,
'shopping_list_id' => $listId
@@ -103,7 +105,7 @@ class StockService extends BaseService
}
}
- public function AddProduct(int $productId, float $amount, $bestBeforeDate, $transactionType, $purchasedDate, $price, $locationId = null, $shoppingLocationId = null, &$transactionId = null)
+ public function AddProduct(int $productId, float $amount, $bestBeforeDate, $transactionType, $purchasedDate, $price, $locationId = null, $shoppingLocationId = null, &$transactionId = null, $runWebhook = false, &$webhookData = null)
{
if (!$this->ProductExists($productId))
{
@@ -178,6 +180,17 @@ class StockService extends BaseService
]);
$stockRow->save();
+ if (GROCY_FEATURE_FLAG_LABELPRINTER && GROCY_LABEL_PRINTER_RUN_SERVER && $runWebhook)
+ {
+ $webhookData = array_merge([
+ 'product' => $productDetails->product->name,
+ 'grocycode' => (string)(new Grocycode(Grocycode::PRODUCT, $productId, [$stockId])),
+ 'duedate' => $this->getLocalizationService()->__t('DD') . ': ' . $bestBeforeDate,
+ ], GROCY_LABEL_PRINTER_PARAMS);
+
+ (new WebhookRunner())->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON);
+ }
+
return $transactionId;
}
else
@@ -452,7 +465,7 @@ class StockService extends BaseService
if ($pluginOutput !== null)
{ // Lookup was successful
if ($addFoundProduct === true)
- {
+ {
// Add product to database and include new product id in output
$newRow = $this->getDatabase()->products()->createRow($pluginOutput);
$newRow->save();
diff --git a/views/layout/default.blade.php b/views/layout/default.blade.php
index fceaaa00..7684cbcc 100644
--- a/views/layout/default.blade.php
+++ b/views/layout/default.blade.php
@@ -95,6 +95,14 @@
Grocy.CalendarShowWeekNumbers = {{ BoolToString(GROCY_CALENDAR_SHOW_WEEK_OF_YEAR) }};
Grocy.GettextPo = {!! $GettextPo !!};
Grocy.FeatureFlags = {!! json_encode($featureFlags) !!};
+ Grocy.Webhooks = {
+ @if(GROCY_FEATURE_FLAG_LABELPRINTER && !GROCY_LABEL_PRINTER_RUN_SERVER)
+ "labelprinter" : {
+ "hook" : "{{ GROCY_LABEL_PRINTER_WEBHOOK}}",
+ "extra_data" : {!! json_encode(GROCY_LABEL_PRINTER_PARAMS) !!}
+ }
+ @endif
+ };
@if (GROCY_AUTHENTICATED)
Grocy.UserSettings = {!! json_encode($userSettings) !!};
diff --git a/views/purchase.blade.php b/views/purchase.blade.php
index 679fa3e2..ccd3bed0 100644
--- a/views/purchase.blade.php
+++ b/views/purchase.blade.php
@@ -10,9 +10,9 @@
@section('content')