diff --git a/.gitignore b/.gitignore
index 9e8bc37..5b9358c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,6 @@ data/config.php
data/users.db
/vendor/
.idea
+/.devDocker/
+/.run/Dev Docker.run.xml
+/data/logs/
diff --git a/api/index.php b/api/index.php
index e1e9e48..3be9d7c 100644
--- a/api/index.php
+++ b/api/index.php
@@ -37,7 +37,6 @@
$api->checkIfAuthorized();
$api->execute($requestedUrl);
-
class BBuddyApi {
private $routes = array();
@@ -115,7 +114,6 @@ function addRoute(ApiRoute $route): void {
}
private function initRoutes(): void {
-
$this->addRoute(new ApiRoute("/action/scan", function () {
$barcode = "";
if (isset($_GET["text"]))
@@ -124,22 +122,24 @@ private function initRoutes(): void {
$barcode = $_GET["add"];
if (isset($_POST["barcode"]))
$barcode = $_POST["barcode"];
- if ($barcode == "")
+ if ($barcode == "") {
return self::createResultArray(null, "No barcode supplied", 400);
- else {
+ } else {
$bestBefore = null;
$price = null;
if (isset($_POST["bestBeforeInDays"]) && $_POST["bestBeforeInDays"] != null) {
- if (is_numeric($_POST["bestBeforeInDays"]))
+ if (is_numeric($_POST["bestBeforeInDays"])) {
$bestBefore = $_POST["bestBeforeInDays"];
- else
+ } else {
return self::createResultArray(null, "Invalid parameter bestBeforeInDays: needs to be type int", 400);
+ }
}
if (isset($_POST["price"]) && $_POST["price"] != null) {
- if (is_numeric($_POST["price"]))
+ if (is_numeric($_POST["price"])) {
$price = $_POST["price"];
- else
+ } else {
return self::createResultArray(null, "Invalid parameter price: needs to be type float", 400);
+ }
}
$result = processNewBarcode(sanitizeString($barcode), $bestBefore, $price);
return self::createResultArray(array("result" => sanitizeString($result)));
@@ -159,10 +159,10 @@ private function initRoutes(): void {
else if (isset($_POST["state"]))
$state = $_POST["state"];
- //Also check if value is a valid range (STATE_CONSUME the lowest and STATE_CONSUME_ALL the highest value)
- if (!is_numeric($state) || $state < STATE_CONSUME || $state > STATE_CONSUME_ALL)
+ //Also check if value is a valid range (STATE_CONSUME the lowest and STATE_TXFR the highest value)
+ if (!is_numeric($state) || $state < STATE_CONSUME || $state > STATE_TXFR) {
return self::createResultArray(null, "Invalid state provided", 400);
- else {
+ } else {
DatabaseConnection::getInstance()->setTransactionState(intval($state));
return self::createResultArray();
}
@@ -178,7 +178,8 @@ private function initRoutes(): void {
"BARCODE_GS" => $config["BARCODE_GS"],
"BARCODE_Q" => $config["BARCODE_Q"],
"BARCODE_AS" => $config["BARCODE_AS"],
- "BARCODE_CA" => $config["BARCODE_CA"]
+ "BARCODE_CA" => $config["BARCODE_CA"],
+ "BARCODE_TXFR" => $config["BARCODE_TXFR"],
));
}));
diff --git a/barcodes.php b/barcodes.php
new file mode 100644
index 0000000..5f6e40b
--- /dev/null
+++ b/barcodes.php
@@ -0,0 +1,136 @@
+checkIfAuthenticated(true, true);
+
+// Get mode and validate it
+$mode = MODE_ACTION;
+if (isset($_GET)) {
+ if (isset($_GET["mode"])) {
+ $mode = $_GET["mode"];
+ }
+}
+if (!in_array($mode, [MODE_ACTION ,MODE_QUANTITY, MODE_LOCATION])) {
+ die("Invalid mode");
+}
+
+// Generate the page
+$webUi = new WebUiGenerator(MENU_GENERIC);
+
+$webUi->addBaseHeader(
+ null,
+ false,
+ true,
+ "\n");
+
+switch ($mode) {
+ case MODE_ACTION:
+ getHtmlActionTable($webUi);
+ break;
+ case MODE_QUANTITY:
+ getHtmlQuantityTable($webUi);
+ break;
+ case MODE_LOCATION:
+ getHtmlLocationTable($webUi);
+ break;
+}
+
+$webUi->printHtml();
+
+function getHtmlActionTable(WebUiGenerator $webUi): void
+{
+ $config = BBConfig::getInstance();
+
+ $actions = [
+ 1 => ['barcode' => $config['BARCODE_C'], 'name' => 'Consume'],
+ 2 => ['barcode' => $config['BARCODE_CS'], 'name' => 'Consume (spoiled)'],
+ 3 => ['barcode' => $config['BARCODE_CA'], 'name' => 'Consume All'],
+ 4 => ['barcode' => $config['BARCODE_P'], 'name' => 'Purchase'],
+ 5 => ['barcode' => $config['BARCODE_O'], 'name' => 'Open'],
+ 6 => ['barcode' => $config['BARCODE_GS'], 'name' => 'Inventory'],
+ 7 => ['barcode' => $config['BARCODE_AS'], 'name' => 'Add to Shopping List'],
+ ];
+
+
+ // Generate the HTML
+ $html = new UiEditor(true, null, "barcodes");
+ $html->addHtml("
");
+
+ foreach ($actions as $key => $action) {
+ $html->addDiv("
![\"$key\"]()
", null, "flex-settings-child");
+
+ }
+
+ $html->addHtml('
');
+ $webUi->addHtml($html->getHtml());
+
+ // Generate the JS
+ $webUi->addScript("generateActionBarcodes();");
+}
+
+function getHtmlLocationTable(WebUiGenerator $webUi): void
+{
+ $config = BBConfig::getInstance();
+ $locations = API::getLocations();
+
+ // Generate the HTML
+ $html = new UiEditor(true, null, "barcodes");
+ $html->addHtml("');
+ $webUi->addHtml($html->getHtml());
+
+ // Generate the JS
+ $webUi->addScript("generateLocationBarcodes();");
+}
+
+
+function getHtmlQuantityTable(WebUiGenerator $webUi): void
+{
+ $config = BBConfig::getInstance();
+
+ // Get quantity start and end
+ $startQty = isset($_GET['startQty']) ? intval($_GET['startQty']) : 1;
+ $endQty = isset($_GET['endQty']) ? intval($_GET['endQty']) : 10;
+
+ // Generate the HTML
+ $html = new UiEditor(true, null, "barcodes");
+ $html->addHtml("");
+
+ for ($i = $startQty; $i <= $endQty; $i++) {
+ $html->addDiv("
![\"$i\"]()
", null, "flex-settings-child");
+ }
+
+ $html->addHtml('
');
+ $webUi->addHtml($html->getHtml());
+
+ // Generate the JS
+ $webUi->addScript("generateQuantityBarcodes();");
+}
\ No newline at end of file
diff --git a/composer.lock b/composer.lock
index 357e042..7361ced 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,52 +1,47 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "bf9c4fdd657f9f4f1fed224955837374",
+ "content-hash": "dda097e883f30e7c8de82e0aa9929666",
"packages": [],
"packages-dev": [
{
"name": "amphp/amp",
- "version": "v2.5.2",
+ "version": "v2.6.5",
"source": {
"type": "git",
"url": "https://github.com/amphp/amp.git",
- "reference": "efca2b32a7580087adb8aabbff6be1dc1bb924a9"
+ "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/amp/zipball/efca2b32a7580087adb8aabbff6be1dc1bb924a9",
- "reference": "efca2b32a7580087adb8aabbff6be1dc1bb924a9",
+ "url": "https://api.github.com/repos/amphp/amp/zipball/d7dda98dae26e56f3f6fcfbf1c1f819c9a993207",
+ "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207",
"shasum": ""
},
"require": {
- "php": ">=7"
+ "php": ">=7.1"
},
"require-dev": {
"amphp/php-cs-fixer-config": "dev-master",
"amphp/phpunit-util": "^1",
"ext-json": "*",
"jetbrains/phpstorm-stubs": "^2019.3",
- "phpunit/phpunit": "^6.0.9 | ^7",
- "psalm/phar": "^3.11@dev",
- "react/promise": "^2"
+ "phpunit/phpunit": "^7 | ^8 | ^9",
+ "react/promise": "^2",
+ "vimeo/psalm": "^3.12"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.x-dev"
- }
- },
"autoload": {
- "psr-4": {
- "Amp\\": "lib"
- },
"files": [
"lib/functions.php",
"lib/Internal/functions.php"
- ]
+ ],
+ "psr-4": {
+ "Amp\\": "lib"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -71,7 +66,7 @@
}
],
"description": "A non-blocking concurrency framework for PHP applications.",
- "homepage": "http://amphp.org/amp",
+ "homepage": "https://amphp.org/amp",
"keywords": [
"async",
"asynchronous",
@@ -83,20 +78,31 @@
"non-blocking",
"promise"
],
- "time": "2021-01-10T17:06:37+00:00"
+ "support": {
+ "irc": "irc://irc.freenode.org/amphp",
+ "issues": "https://github.com/amphp/amp/issues",
+ "source": "https://github.com/amphp/amp/tree/v2.6.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-09-03T19:41:28+00:00"
},
{
"name": "amphp/byte-stream",
- "version": "v1.8.0",
+ "version": "v1.8.2",
"source": {
"type": "git",
"url": "https://github.com/amphp/byte-stream.git",
- "reference": "f0c20cf598a958ba2aa8c6e5a71c697d652c7088"
+ "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/byte-stream/zipball/f0c20cf598a958ba2aa8c6e5a71c697d652c7088",
- "reference": "f0c20cf598a958ba2aa8c6e5a71c697d652c7088",
+ "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc",
+ "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc",
"shasum": ""
},
"require": {
@@ -112,18 +118,13 @@
"psalm/phar": "^3.11.4"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.x-dev"
- }
- },
"autoload": {
- "psr-4": {
- "Amp\\ByteStream\\": "lib"
- },
"files": [
"lib/functions.php"
- ]
+ ],
+ "psr-4": {
+ "Amp\\ByteStream\\": "lib"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -140,7 +141,7 @@
}
],
"description": "A stream abstraction to make working with non-blocking I/O simple.",
- "homepage": "http://amphp.org/byte-stream",
+ "homepage": "https://amphp.org/byte-stream",
"keywords": [
"amp",
"amphp",
@@ -149,20 +150,30 @@
"non-blocking",
"stream"
],
- "time": "2020-06-29T18:35:05+00:00"
+ "support": {
+ "issues": "https://github.com/amphp/byte-stream/issues",
+ "source": "https://github.com/amphp/byte-stream/tree/v1.8.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-13T18:00:56+00:00"
},
{
"name": "composer/package-versions-deprecated",
- "version": "1.11.99.1",
+ "version": "1.11.99.5",
"source": {
"type": "git",
"url": "https://github.com/composer/package-versions-deprecated.git",
- "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6"
+ "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/7413f0b55a051e89485c5cb9f765fe24bb02a7b6",
- "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6",
+ "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d",
+ "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d",
"shasum": ""
},
"require": {
@@ -204,28 +215,117 @@
}
],
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
- "time": "2020-11-11T10:22:58+00:00"
+ "support": {
+ "issues": "https://github.com/composer/package-versions-deprecated/issues",
+ "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-01-17T14:14:24+00:00"
+ },
+ {
+ "name": "composer/pcre",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560",
+ "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.3",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "symfony/phpunit-bridge": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Pcre\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "PCRE wrapping library that offers type-safe preg_* replacements.",
+ "keywords": [
+ "PCRE",
+ "preg",
+ "regex",
+ "regular expression"
+ ],
+ "support": {
+ "issues": "https://github.com/composer/pcre/issues",
+ "source": "https://github.com/composer/pcre/tree/1.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-01-21T20:24:37+00:00"
},
{
"name": "composer/semver",
- "version": "3.2.4",
+ "version": "3.4.4",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
- "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464"
+ "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/semver/zipball/a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
- "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
+ "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95",
+ "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95",
"shasum": ""
},
"require": {
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
- "phpstan/phpstan": "^0.12.54",
- "symfony/phpunit-bridge": "^4.2 || ^5"
+ "phpstan/phpstan": "^1.11",
+ "symfony/phpunit-bridge": "^3 || ^7"
},
"type": "library",
"extra": {
@@ -266,28 +366,46 @@
"validation",
"versioning"
],
- "time": "2020-11-13T08:59:24+00:00"
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/tree/3.4.4"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-20T19:15:30+00:00"
},
{
"name": "composer/xdebug-handler",
- "version": "1.4.5",
+ "version": "2.0.5",
"source": {
"type": "git",
"url": "https://github.com/composer/xdebug-handler.git",
- "reference": "f28d44c286812c714741478d968104c5e604a1d4"
+ "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f28d44c286812c714741478d968104c5e604a1d4",
- "reference": "f28d44c286812c714741478d968104c5e604a1d4",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/9e36aeed4616366d2b690bdce11f71e9178c579a",
+ "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a",
"shasum": ""
},
"require": {
+ "composer/pcre": "^1",
"php": "^5.3.2 || ^7.0 || ^8.0",
- "psr/log": "^1.0"
+ "psr/log": "^1 || ^2 || ^3"
},
"require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8"
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0"
},
"type": "library",
"autoload": {
@@ -310,7 +428,26 @@
"Xdebug",
"performance"
],
- "time": "2020-11-13T08:04:11+00:00"
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues",
+ "source": "https://github.com/composer/xdebug-handler/tree/2.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-02-24T20:20:32+00:00"
},
{
"name": "dnoegel/php-xdg-base-dir",
@@ -343,24 +480,76 @@
"MIT"
],
"description": "implementation of xdg base directory specification for php",
+ "support": {
+ "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues",
+ "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1"
+ },
"time": "2019-12-04T15:06:13+00:00"
},
+ {
+ "name": "doctrine/deprecations",
+ "version": "1.1.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/deprecations.git",
+ "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
+ "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<=7.5 || >=14"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9 || ^12 || ^14",
+ "phpstan/phpstan": "1.4.10 || 2.1.30",
+ "phpstan/phpstan-phpunit": "^1.0 || ^2",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "suggest": {
+ "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Deprecations\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+ "homepage": "https://www.doctrine-project.org/",
+ "support": {
+ "issues": "https://github.com/doctrine/deprecations/issues",
+ "source": "https://github.com/doctrine/deprecations/tree/1.1.6"
+ },
+ "time": "2026-02-07T07:09:04+00:00"
+ },
{
"name": "felixfbecker/advanced-json-rpc",
- "version": "v3.2.0",
+ "version": "v3.2.1",
"source": {
"type": "git",
"url": "https://github.com/felixfbecker/php-advanced-json-rpc.git",
- "reference": "06f0b06043c7438959dbdeed8bb3f699a19be22e"
+ "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/06f0b06043c7438959dbdeed8bb3f699a19be22e",
- "reference": "06f0b06043c7438959dbdeed8bb3f699a19be22e",
+ "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447",
+ "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447",
"shasum": ""
},
"require": {
- "netresearch/jsonmapper": "^1.0 || ^2.0",
+ "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
"php": "^7.1 || ^8.0",
"phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0"
},
@@ -384,20 +573,24 @@
}
],
"description": "A more advanced JSONRPC implementation",
- "time": "2021-01-10T17:48:47+00:00"
+ "support": {
+ "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues",
+ "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1"
+ },
+ "time": "2021-06-11T22:34:44+00:00"
},
{
"name": "felixfbecker/language-server-protocol",
- "version": "v1.5.0",
+ "version": "v1.5.3",
"source": {
"type": "git",
"url": "https://github.com/felixfbecker/php-language-server-protocol.git",
- "reference": "85e83cacd2ed573238678c6875f8f0d7ec699541"
+ "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/85e83cacd2ed573238678c6875f8f0d7ec699541",
- "reference": "85e83cacd2ed573238678c6875f8f0d7ec699541",
+ "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9",
+ "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9",
"shasum": ""
},
"require": {
@@ -436,20 +629,24 @@
"php",
"server"
],
- "time": "2020-10-23T13:55:30+00:00"
+ "support": {
+ "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues",
+ "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3"
+ },
+ "time": "2024-04-30T00:40:11+00:00"
},
{
"name": "netresearch/jsonmapper",
- "version": "v2.1.0",
+ "version": "v4.5.0",
"source": {
"type": "git",
"url": "https://github.com/cweiske/jsonmapper.git",
- "reference": "e0f1e33a71587aca81be5cffbb9746510e1fe04e"
+ "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/e0f1e33a71587aca81be5cffbb9746510e1fe04e",
- "reference": "e0f1e33a71587aca81be5cffbb9746510e1fe04e",
+ "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8e76efb98ee8b6afc54687045e1b8dba55ac76e5",
+ "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5",
"shasum": ""
},
"require": {
@@ -457,10 +654,10 @@
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
- "php": ">=5.6"
+ "php": ">=7.1"
},
"require-dev": {
- "phpunit/phpunit": "~4.8.35 || ~5.7 || ~6.4 || ~7.0",
+ "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0",
"squizlabs/php_codesniffer": "~3.5"
},
"type": "library",
@@ -482,7 +679,12 @@
}
],
"description": "Map nested JSON structures onto PHP classes",
- "time": "2020-04-16T18:48:43+00:00"
+ "support": {
+ "email": "cweiske@cweiske.de",
+ "issues": "https://github.com/cweiske/jsonmapper/issues",
+ "source": "https://github.com/cweiske/jsonmapper/tree/v4.5.0"
+ },
+ "time": "2024-09-08T10:13:13+00:00"
},
{
"name": "nikic/php-parser",
@@ -534,6 +736,10 @@
"parser",
"php"
],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0"
+ },
"time": "2021-07-21T10:44:31+00:00"
},
{
@@ -583,6 +789,10 @@
"xml",
"xml conversion"
],
+ "support": {
+ "issues": "https://github.com/nullivex/lib-array2xml/issues",
+ "source": "https://github.com/nullivex/lib-array2xml/tree/master"
+ },
"time": "2019-03-29T20:06:56+00:00"
},
{
@@ -632,31 +842,43 @@
"reflection",
"static analysis"
],
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+ },
"time": "2020-06-27T09:03:43+00:00"
},
{
"name": "phpdocumentor/reflection-docblock",
- "version": "5.2.2",
+ "version": "5.6.6",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
- "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
+ "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
- "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8",
+ "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8",
"shasum": ""
},
"require": {
+ "doctrine/deprecations": "^1.1",
"ext-filter": "*",
- "php": "^7.2 || ^8.0",
+ "php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.2",
- "phpdocumentor/type-resolver": "^1.3",
- "webmozart/assert": "^1.9.1"
+ "phpdocumentor/type-resolver": "^1.7",
+ "phpstan/phpdoc-parser": "^1.7|^2.0",
+ "webmozart/assert": "^1.9.1 || ^2"
},
"require-dev": {
- "mockery/mockery": "~1.3.2"
+ "mockery/mockery": "~1.3.5 || ~1.6.0",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-webmozart-assert": "^1.2",
+ "phpunit/phpunit": "^9.5",
+ "psalm/phar": "^5.26"
},
"type": "library",
"extra": {
@@ -680,32 +902,45 @@
},
{
"name": "Jaap van Otterdijk",
- "email": "account@ijaap.nl"
+ "email": "opensource@ijaap.nl"
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
- "time": "2020-09-03T19:13:55+00:00"
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.6"
+ },
+ "time": "2025-12-22T21:13:58+00:00"
},
{
"name": "phpdocumentor/type-resolver",
- "version": "1.4.0",
+ "version": "1.12.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
- "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
+ "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
- "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195",
+ "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195",
"shasum": ""
},
"require": {
- "php": "^7.2 || ^8.0",
- "phpdocumentor/reflection-common": "^2.0"
+ "doctrine/deprecations": "^1.0",
+ "php": "^7.3 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.0",
+ "phpstan/phpdoc-parser": "^1.18|^2.0"
},
"require-dev": {
- "ext-tokenizer": "*"
+ "ext-tokenizer": "*",
+ "phpbench/phpbench": "^1.2",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpunit/phpunit": "^9.5",
+ "rector/rector": "^0.13.9",
+ "vimeo/psalm": "^4.25"
},
"type": "library",
"extra": {
@@ -729,29 +964,80 @@
}
],
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
- "time": "2020-09-17T18:55:26+00:00"
+ "support": {
+ "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0"
+ },
+ "time": "2025-11-21T15:09:14+00:00"
+ },
+ {
+ "name": "phpstan/phpdoc-parser",
+ "version": "2.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpdoc-parser.git",
+ "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a",
+ "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^2.0",
+ "nikic/php-parser": "^5.3.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^9.6",
+ "symfony/process": "^5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\PhpDocParser\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPDoc parser with support for nullable, intersection and generic types",
+ "support": {
+ "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2"
+ },
+ "time": "2026-01-25T14:56:51+00:00"
},
{
"name": "psr/container",
- "version": "1.0.0",
+ "version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
- "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
- "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "php": ">=7.4.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "2.0.x-dev"
}
},
"autoload": {
@@ -766,7 +1052,7 @@
"authors": [
{
"name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "homepage": "https://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
@@ -778,34 +1064,38 @@
"container-interop",
"psr"
],
- "time": "2017-02-14T16:28:37+00:00"
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
},
{
"name": "psr/log",
- "version": "1.1.3",
+ "version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
- "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
+ "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
- "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376",
+ "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.1.x-dev"
+ "dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
- "Psr\\Log\\": "Psr/Log/"
+ "Psr\\Log\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -815,7 +1105,7 @@
"authors": [
{
"name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
@@ -825,33 +1115,36 @@
"psr",
"psr-3"
],
- "time": "2020-03-23T09:12:05+00:00"
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/2.0.0"
+ },
+ "time": "2021-07-14T16:41:46+00:00"
},
{
"name": "sebastian/diff",
- "version": "3.0.3",
+ "version": "4.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211"
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211",
- "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc",
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^7.5 || ^8.0",
- "symfony/process": "^2 || ^3.3 || ^4"
+ "phpunit/phpunit": "^9.3",
+ "symfony/process": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.0-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -881,31 +1174,43 @@
"unidiff",
"unified diff"
],
- "time": "2020-11-30T07:59:04+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T06:30:58+00:00"
},
{
"name": "symfony/console",
- "version": "v5.2.1",
+ "version": "v5.4.47",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "47c02526c532fb381374dab26df05e7313978976"
+ "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/47c02526c532fb381374dab26df05e7313978976",
- "reference": "47c02526c532fb381374dab26df05e7313978976",
+ "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed",
+ "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
"symfony/polyfill-mbstring": "~1.0",
- "symfony/polyfill-php73": "^1.8",
- "symfony/polyfill-php80": "^1.15",
- "symfony/service-contracts": "^1.1|^2",
- "symfony/string": "^5.1"
+ "symfony/polyfill-php73": "^1.9",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/string": "^5.1|^6.0"
},
"conflict": {
+ "psr/log": ">=3",
"symfony/dependency-injection": "<4.4",
"symfony/dotenv": "<5.1",
"symfony/event-dispatcher": "<4.4",
@@ -913,16 +1218,16 @@
"symfony/process": "<4.4"
},
"provide": {
- "psr/log-implementation": "1.0"
+ "psr/log-implementation": "1.0|2.0"
},
"require-dev": {
- "psr/log": "~1.0",
- "symfony/config": "^4.4|^5.0",
- "symfony/dependency-injection": "^4.4|^5.0",
- "symfony/event-dispatcher": "^4.4|^5.0",
- "symfony/lock": "^4.4|^5.0",
- "symfony/process": "^4.4|^5.0",
- "symfony/var-dumper": "^4.4|^5.0"
+ "psr/log": "^1|^2",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/event-dispatcher": "^4.4|^5.0|^6.0",
+ "symfony/lock": "^4.4|^5.0|^6.0",
+ "symfony/process": "^4.4|^5.0|^6.0",
+ "symfony/var-dumper": "^4.4|^5.0|^6.0"
},
"suggest": {
"psr/log": "For using the console logger",
@@ -953,53 +1258,137 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Console Component",
+ "description": "Eases the creation of beautiful and testable command line interfaces",
"homepage": "https://symfony.com",
"keywords": [
"cli",
- "command line",
+ "command-line",
"console",
"terminal"
],
- "time": "2020-12-18T08:03:05+00:00"
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v5.4.47"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-06T11:30:55+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:21:43+00:00"
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.22.0",
+ "version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
- "reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.22-dev"
- },
"thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Ctype\\": ""
- },
"files": [
"bootstrap.php"
- ]
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1023,45 +1412,63 @@
"polyfill",
"portable"
],
- "time": "2021-01-07T16:49:33+00:00"
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
- "version": "v1.22.0",
+ "version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "267a9adeb8ecb8071040a740930e077cdfb987af"
+ "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/267a9adeb8ecb8071040a740930e077cdfb987af",
- "reference": "267a9adeb8ecb8071040a740930e077cdfb987af",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70",
+ "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.22-dev"
- },
"thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
- },
"files": [
"bootstrap.php"
- ]
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1087,45 +1494,63 @@
"portable",
"shim"
],
- "time": "2021-01-07T16:49:33+00:00"
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-27T09:58:17+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
- "version": "v1.22.0",
+ "version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "6e971c891537eb617a00bb07a43d182a6915faba"
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/6e971c891537eb617a00bb07a43d182a6915faba",
- "reference": "6e971c891537eb617a00bb07a43d182a6915faba",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.22-dev"
- },
"thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
- },
"files": [
"bootstrap.php"
],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
"classmap": [
"Resources/stubs"
]
@@ -1154,45 +1579,67 @@
"portable",
"shim"
],
- "time": "2021-01-07T17:09:11+00:00"
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.22.0",
+ "version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13"
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13",
- "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "ext-iconv": "*",
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.22-dev"
- },
"thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Mbstring\\": ""
- },
"files": [
"bootstrap.php"
- ]
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1217,42 +1664,60 @@
"portable",
"shim"
],
- "time": "2021-01-07T16:49:33+00:00"
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-12-23T08:48:59+00:00"
},
{
"name": "symfony/polyfill-php73",
- "version": "v1.22.0",
+ "version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
- "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2"
+ "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
- "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb",
+ "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.22-dev"
- },
"thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Php73\\": ""
- },
"files": [
"bootstrap.php"
],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php73\\": ""
+ },
"classmap": [
"Resources/stubs"
]
@@ -1279,42 +1744,60 @@
"portable",
"shim"
],
- "time": "2021-01-07T16:49:33+00:00"
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.22.0",
+ "version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
- "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.22-dev"
- },
"thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
}
},
"autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Php80\\": ""
- },
"files": [
"bootstrap.php"
],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
"classmap": [
"Resources/stubs"
]
@@ -1345,43 +1828,68 @@
"portable",
"shim"
],
- "time": "2021-01-07T16:49:33+00:00"
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-01-02T08:10:11+00:00"
},
{
"name": "symfony/service-contracts",
- "version": "v2.2.0",
+ "version": "v3.6.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1"
+ "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1",
- "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43",
+ "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "psr/container": "^1.0"
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
},
- "suggest": {
- "symfony/service-implementation": ""
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-master": "2.2-dev"
- },
"thanks": {
- "name": "symfony/contracts",
- "url": "https://github.com/symfony/contracts"
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\Service\\": ""
- }
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1407,44 +1915,67 @@
"interoperability",
"standards"
],
- "time": "2020-09-07T11:33:47+00:00"
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.6.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-15T11:30:57+00:00"
},
{
"name": "symfony/string",
- "version": "v5.2.1",
+ "version": "v6.4.30",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed"
+ "reference": "50590a057841fa6bf69d12eceffce3465b9e32cb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed",
- "reference": "5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed",
+ "url": "https://api.github.com/repos/symfony/string/zipball/50590a057841fa6bf69d12eceffce3465b9e32cb",
+ "reference": "50590a057841fa6bf69d12eceffce3465b9e32cb",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
+ "php": ">=8.1",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-intl-grapheme": "~1.0",
"symfony/polyfill-intl-normalizer": "~1.0",
- "symfony/polyfill-mbstring": "~1.0",
- "symfony/polyfill-php80": "~1.15"
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
},
"require-dev": {
- "symfony/error-handler": "^4.4|^5.0",
- "symfony/http-client": "^4.4|^5.0",
- "symfony/translation-contracts": "^1.1|^2",
- "symfony/var-exporter": "^4.4|^5.0"
+ "symfony/http-client": "^5.4|^6.0|^7.0",
+ "symfony/intl": "^6.2|^7.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^5.4|^6.0|^7.0"
},
"type": "library",
"autoload": {
- "psr-4": {
- "Symfony\\Component\\String\\": ""
- },
"files": [
"Resources/functions.php"
],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
"exclude-from-classmap": [
"/Tests/"
]
@@ -1463,7 +1994,7 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony String component",
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
"homepage": "https://symfony.com",
"keywords": [
"grapheme",
@@ -1473,7 +2004,28 @@
"utf-8",
"utf8"
],
- "time": "2020-12-05T07:33:16+00:00"
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v6.4.30"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-11-21T18:03:05+00:00"
},
{
"name": "vimeo/psalm",
@@ -1544,20 +2096,20 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.x-dev",
- "dev-3.x": "3.x-dev",
+ "dev-1.x": "1.x-dev",
"dev-2.x": "2.x-dev",
- "dev-1.x": "1.x-dev"
+ "dev-3.x": "3.x-dev",
+ "dev-master": "4.x-dev"
}
},
"autoload": {
- "psr-4": {
- "Psalm\\": "src/Psalm/"
- },
"files": [
"src/functions.php",
"src/spl_object_id.php"
- ]
+ ],
+ "psr-4": {
+ "Psalm\\": "src/Psalm/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1574,34 +2126,43 @@
"inspection",
"php"
],
+ "support": {
+ "issues": "https://github.com/vimeo/psalm/issues",
+ "source": "https://github.com/vimeo/psalm/tree/4.9.0"
+ },
"time": "2021-07-30T21:23:45+00:00"
},
{
"name": "webmozart/assert",
- "version": "1.9.1",
+ "version": "1.12.1",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
- "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
+ "reference": "9be6926d8b485f55b9229203f962b51ed377ba68"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
- "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68",
+ "reference": "9be6926d8b485f55b9229203f962b51ed377ba68",
"shasum": ""
},
"require": {
- "php": "^5.3.3 || ^7.0 || ^8.0",
- "symfony/polyfill-ctype": "^1.8"
- },
- "conflict": {
- "phpstan/phpstan": "<0.12.20",
- "vimeo/psalm": "<3.9.1"
+ "ext-ctype": "*",
+ "ext-date": "*",
+ "ext-filter": "*",
+ "php": "^7.2 || ^8.0"
},
- "require-dev": {
- "phpunit/phpunit": "^4.8.36 || ^7.5.13"
+ "suggest": {
+ "ext-intl": "",
+ "ext-simplexml": "",
+ "ext-spl": ""
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.10-dev"
+ }
+ },
"autoload": {
"psr-4": {
"Webmozart\\Assert\\": "src/"
@@ -1623,7 +2184,11 @@
"check",
"validate"
],
- "time": "2020-07-08T17:02:28+00:00"
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.12.1"
+ },
+ "time": "2025-10-29T15:56:20+00:00"
},
{
"name": "webmozart/path-util",
@@ -1669,16 +2234,24 @@
}
],
"description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.",
+ "support": {
+ "issues": "https://github.com/webmozart/path-util/issues",
+ "source": "https://github.com/webmozart/path-util/tree/2.3.0"
+ },
+ "abandoned": "symfony/filesystem",
"time": "2015-12-17T08:42:14+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "ext-redis": "*"
+ "ext-redis": "*",
+ "ext-sockets": "*",
+ "ext-curl": "*"
},
- "platform-dev": []
+ "platform-dev": {},
+ "plugin-api-version": "2.9.0"
}
diff --git a/config-dist.php b/config-dist.php
index a766b53..c9bc4aa 100644
--- a/config-dist.php
+++ b/config-dist.php
@@ -92,6 +92,7 @@
//"BARCODE_GS" => "BBUDDY-I",
//"BARCODE_Q" => "BBUDDY-Q-",
//"BARCODE_AS" => "BBUDDY-AS",
+ //"BARCODE_TXFR" => "BBUDDY-TXFR-",
//"REVERT_TIME" => "10",
//"REVERT_SINGLE" => "1",
//"MORE_VERBOSE" => "1",
diff --git a/incl/api.inc.php b/incl/api.inc.php
index 45b1c56..ff95fe1 100755
--- a/incl/api.inc.php
+++ b/incl/api.inc.php
@@ -28,7 +28,9 @@
const API_STOCK_PRODUCTS = 'stock/products';
const API_ALL_PRODUCTS = 'stock';
const API_SHOPPINGLIST = 'stock/shoppinglist/';
+const API_OBJECTS = 'objects';
const API_CHORES = 'objects/chores';
+const API_LOCATIONS = 'objects/locations';
const API_STOCK = 'stock/products';
const API_STOCK_BY_BARCODE = 'stock/products/by-barcode/';
const API_CHORE_EXECUTE = 'chores/';
@@ -42,18 +44,18 @@
const DISPLAY_DEBUG = false;
-
class GrocyProduct {
- public $id;
- public $name;
- public $barcodes = null;
- public $unit = null;
- public $stockAmount = "0";
- public $isTare;
- public $tareWeight;
- public $quFactor;
+ public int $id;
+ public ?string $name;
+ public ?array $barcodes = null;
+ public ?string $unit = null;
+ public int $stockAmount = 0;
+ public bool $isTare = false;
+ public ?string $tareWeight;
+ public float $quFactor;
public $defaultBestBeforeDays;
public $creationDate;
+ public GrocyLocation $location;
public static function parseProductInfoStock(array $infoArray): GrocyProduct {
checkIfNumeric($infoArray["product"]["id"]);
@@ -69,12 +71,20 @@ public static function parseProductInfoStock(array $infoArray): GrocyProduct {
$result->barcodes = $infoArray["product_barcodes"];
if (isset($infoArray["product"]["qu_conversion_factor_purchase_to_stock"]))
- $result->quFactor = sanitizeString($infoArray["product"]["qu_conversion_factor_purchase_to_stock"]);
+ $result->quFactor = floatval(sanitizeString($infoArray["product"]["qu_conversion_factor_purchase_to_stock"]));
else
- $result->quFactor = 1;
+ $result->quFactor = 1.0;
if (sanitizeString($infoArray["stock_amount"]) != null)
- $result->stockAmount = sanitizeString($infoArray["stock_amount"]);
+ $result->stockAmount = intval(sanitizeString($infoArray["stock_amount"]));
+
+ $result->location = new GrocyLocation();
+ if (isset($infoArray['location'])) {
+ $result->location = GrocyLocation::parseLocationObject($infoArray['location']);
+ } elseif (isset($infoArray['location_id'])) {
+ $result->location->id = $infoArray['location_id'];
+ }
+
return $result;
}
@@ -90,6 +100,83 @@ public static function parseProductInfoObjects(array $infoArray): GrocyProduct {
$result->quFactor = 1; //FIXME qu_conversion_factor_purchase_to_stock was removed, might break QU conversion
$result->defaultBestBeforeDays = $infoArray["default_best_before_days"];
$result->creationDate = $infoArray["row_created_timestamp"];
+
+ $result->location = new GrocyLocation();
+ if (isset($infoArray['location'])) {
+ $result->location = GrocyLocation::parseLocationObject($infoArray['location']);
+ } elseif (isset($infoArray['location_id'])) {
+ $result->location->id = $infoArray['location_id'];
+ }
+
+ return $result;
+ }
+}
+
+class GrocyLocation {
+ public ?int $id = null;
+ public string $name = '';
+ public ?string $description = null;
+ public bool $isFreezer = false;
+
+ public function toString(): string
+ {
+ return empty($this->name) ? $this->id : $this->name;
+ }
+
+ public static function parseLocationObject(array $locationArray): GrocyLocation {
+ $result = new GrocyLocation();
+
+ // If Grocy returns no location (empty array), just return defaults
+ if (empty($locationArray)) {
+ return $result;
+ }
+
+ if (array_key_exists('id', $locationArray) && $locationArray['id'] !== null && $locationArray['id'] !== '') {
+ checkIfNumeric((string) $locationArray['id']);
+ $result->id = (int) $locationArray['id'];
+ }
+
+ if (array_key_exists('name', $locationArray) && $locationArray['name'] !== null) {
+ $result->name = sanitizeString((string) $locationArray['name']) ?? '';
+ }
+
+ if (array_key_exists('description', $locationArray) && $locationArray['description'] !== null) {
+ $result->description = sanitizeString((string) $locationArray['description']) ?? '';
+ }
+
+ if (array_key_exists('is_freezer', $locationArray)) {
+ $result->isFreezer = ((string) $locationArray['is_freezer'] === '1');
+ }
+
+ return $result;
+ }
+
+ public static function parseProductLocationObject(array $locationArray): GrocyLocation {
+ $result = new GrocyLocation();
+
+ // If Grocy returns no location (empty array), just return defaults
+ if (empty($locationArray)) {
+ return $result;
+ }
+
+ // Grocy may returns an array of locations
+ if (!array_key_exists('id', $locationArray) && isset($locationArray[0]) && is_array($locationArray[0])) {
+ $locationArray = $locationArray[0];
+ }
+
+ if (array_key_exists('location_id', $locationArray) && $locationArray['location_id'] !== null && $locationArray['location_id'] !== '') {
+ checkIfNumeric((string) $locationArray['location_id']);
+ $result->id = (int) $locationArray['location_id'];
+ }
+
+ if (array_key_exists('location_name', $locationArray) && $locationArray['location_name'] !== null) {
+ $result->name = sanitizeString((string) $locationArray['location_name']) ?? '';
+ }
+
+ if (array_key_exists('location_is_freezer', $locationArray)) {
+ $result->isFreezer = ((string) $locationArray['location_is_freezer'] === '1');
+ }
+
return $result;
}
}
@@ -97,6 +184,9 @@ public static function parseProductInfoObjects(array $infoArray): GrocyProduct {
class ApiInternalErrorException extends Exception {
}
+class TransferException extends Exception {
+}
+
class API {
/**
@@ -670,6 +760,95 @@ public static function getProductLocations(int $productid): ?array {
}
+ /** Gets the main location of a product and amount of stock of a product */
+ public static function getProductLocationMain(int $productId): GrocyLocation {
+
+ $url = API_STOCK . "/" . $productId . "/locations?include_sub_products=true&query%5B%5D=product_id%3D" . $productId;
+
+ $result = null;
+ $curl = new CurlGenerator($url);
+ try {
+ $result = $curl->execute(true);
+ } catch (Exception $e) {
+ return new GrocyLocation();
+ }
+ return GrocyLocation::parseProductLocationObject($result);
+ }
+
+ /** Get location by location id */
+ public static function getLocation(int $locationId): GrocyLocation {
+
+ $url = API_LOCATIONS . "/" . $locationId;
+
+ $result = null;
+ $curl = new CurlGenerator($url);
+ try {
+ $result = $curl->execute(true);
+ } catch (Exception $e) {
+ return new GrocyLocation();
+ }
+ return GrocyLocation::parseLocationObject($result);
+ }
+
+ /**
+ * Get all locations
+ *
+ * @return GrocyLocation[]
+ */
+ public static function getLocations(): array {
+
+ $url = API_OBJECTS . "/locations";
+
+ $rtn = [];
+ $result = null;
+ $curl = new CurlGenerator($url);
+ try {
+ $result = $curl->execute(true);
+ } catch (Exception $e) {
+ return $rtn;
+ }
+
+ foreach ($result as $location) {
+ $rtn[] = GrocyLocation::parseLocationObject($location);
+ }
+ return $rtn;
+ }
+
+ /**
+ * Transfers a product from one location to another.
+ *
+ * @throws TransferException when the product could not be transferred
+ *
+ * @param int $productId
+ * @param int $sourceId
+ * @param int $destId
+ * @return void
+ */
+ public static function transferProduct(int $productId, int $sourceId, int $destId, int $amount = 1): void
+ {
+ if ($amount <= 0)
+ return;
+
+ $data = json_encode(array(
+ 'amount' => $amount,
+ 'location_id_from' => $sourceId,
+ 'location_id_to' => $destId
+ ));
+
+ $url = API_STOCK . "/" . $productId . "/transfer";
+
+ $curl = new CurlGenerator($url, METHOD_POST, $data);
+ try {
+ $curl->execute();
+ } catch (Exception $e) {
+ if ($e instanceof InvalidJsonResponseException && (str_contains($e->getMessage(), "ould not transfer") || str_contains($e->getMessage(), "mount to be transferred cannot"))) {
+ throw new TransferException($e->getMessage());
+ }
+
+ self::processError($e, "Could not transfer product");
+ }
+ }
+
/**
* Getting info of a Grocy chore
* @param int $choreId Chore ID.
@@ -765,6 +944,8 @@ public static function processError(Exception $e, string $errorMessage): void {
case 'ApiInternalErrorException':
self::logError("Could not process API call: " . $errorMessage);
break;
+ default:
+ self::logError("Unknown error: " . $errorMessage);
}
}
diff --git a/incl/configProcessing.inc.php b/incl/configProcessing.inc.php
index ee6152b..95a9d91 100644
--- a/incl/configProcessing.inc.php
+++ b/incl/configProcessing.inc.php
@@ -17,8 +17,8 @@
*/
-const BB_VERSION = "1818";
-const BB_VERSION_READABLE = "1.8.1.8";
+const BB_VERSION = "1819";
+const BB_VERSION_READABLE = "1.8.1.9";
const CONFIG_PATH = __DIR__ . '/../data/config.php';
const AUTHDB_PATH = __DIR__ . '/../data/users.db';
@@ -77,7 +77,7 @@ function checkForMissingConstants(): void {
"TRUSTED_PROXIES" => array(),
"SEARCH_ENGINE" => "https://google.com/search?q=",
"BASEURL" => "/",
- "DEFAULT_LOOKUP_LANGUAGE" => "en"
+ "DEFAULT_LOOKUP_LANGUAGE" => "en",
);
foreach ($defaultValues as $key => $value) {
if (!defined($key))
diff --git a/incl/curl.inc.php b/incl/curl.inc.php
index 4bc8976..89f3654 100644
--- a/incl/curl.inc.php
+++ b/incl/curl.inc.php
@@ -16,7 +16,6 @@
* @since File available since Release 1.6
*/
-
class InvalidServerResponseException extends Exception {
}
@@ -69,7 +68,7 @@ class CurlGenerator {
* @param array|null $headers
* @throws DbConnectionDuringEstablishException
*/
- function __construct(string $url, string $method = METHOD_GET,
+ public function __construct(string $url, string $method = METHOD_GET,
string $jasonData = null, array $loginOverride = null,
bool $noApiCall = false, array $ignoredResultCodes = null,
array $formData = null, string $userAgent = null,
@@ -146,7 +145,7 @@ function __construct(string $url, string $method = METHOD_GET,
* @throws NotFoundException
* @throws UnauthorizedException
*/
- function execute(bool $decode = false) {
+ public function execute(bool $decode = false) {
if (DISPLAY_DEBUG) {
$startTime = microtime(true);
DatabaseConnection::getInstance()->saveLog("Executing API call: " . $this->urlApi . "", false, false, true);
@@ -180,10 +179,11 @@ function execute(bool $decode = false) {
$totalTimeMs = round((microtime(true) - $startTime) * 1000);
DatabaseConnection::getInstance()->saveLog("Executing took " . $totalTimeMs . "ms", false, false, true);
}
- if ($decode)
+ if ($decode) {
return $jsonDecoded;
- else
+ } else {
return $curlResult;
+ }
}
diff --git a/incl/db.inc.php b/incl/db.inc.php
index c8500cb..8d44802 100755
--- a/incl/db.inc.php
+++ b/incl/db.inc.php
@@ -37,6 +37,8 @@
const STATE_GETSTOCK = 4;
const STATE_ADD_SL = 5;
const STATE_CONSUME_ALL = 6;
+const STATE_TXFR = 7;
+
const SECTION_KNOWN_BARCODES = "known";
const SECTION_UNKNOWN_BARCODES = "unknown";
@@ -62,12 +64,17 @@
*/
const DEFAULT_USE_REDIS = "0";
+
/**
* Thrown when a database connection is already being setup and a new connection is requested
* This happens most likely when calling getInstance() during the database upgrade
*/
class DbConnectionDuringEstablishException extends Exception {
+}
+class TransferDestination {
+ public ?int $id = null;
+ public ?string $name = null;
}
/**
@@ -85,6 +92,7 @@ class DatabaseConnection {
"BARCODE_GS" => "BBUDDY-I",
"BARCODE_Q" => "BBUDDY-Q-",
"BARCODE_AS" => "BBUDDY-AS",
+ "BARCODE_TXFR" => "BBUDDY-TXFR-",
"REVERT_TIME" => "10",
"REVERT_SINGLE" => "1",
"MORE_VERBOSE" => "1",
@@ -190,6 +198,7 @@ private function initDb(): void {
$this->db->exec("CREATE TABLE IF NOT EXISTS Quantities(id INTEGER PRIMARY KEY, barcode TEXT NOT NULL UNIQUE, quantity INTEGER NOT NULL, product TEXT)");
$this->db->exec("CREATE TABLE IF NOT EXISTS ApiKeys(id INTEGER PRIMARY KEY, key TEXT NOT NULL UNIQUE, lastused INTEGER NOT NULL)");
$this->db->exec("CREATE TABLE IF NOT EXISTS LookupProviderData(providerType INTEGER PRIMARY KEY, data TEXT NOT NULL)");
+ $this->db->exec("CREATE TABLE IF NOT EXISTS Transfer(id INTEGER PRIMARY KEY, dest_id INTEGER NULL, dest_name TEXT NULL)");
$this->insertDefaultValues();
$previousVersion = intval(BBConfig::getInstance($this)["version"]);
if ($previousVersion < BB_VERSION) {
@@ -239,7 +248,7 @@ private function checkPermissions(): void {
DbUpgrade::createDbDirectory();
DbUpgrade::checkAndMoveIfOldDbLocation();
if (!is_writable(dirname($CONFIG->DATABASE_PATH))) {
- showErrorNotWritable("DB Error Not_Writable");
+ showErrorNotWritable("DB Error: Not_Writable");
}
}
}
@@ -653,6 +662,37 @@ public function updateConfig(string $key, ?string $value): void {
BBConfig::getInstance()[$key] = $value;
}
+ public function getTransferDestination(): TransferDestination
+ {
+ $res = $this->db->query("SELECT dest_id, dest_name FROM Transfer WHERE id=1");
+ if ($res !== false && ($row = $res->fetchArray(SQLITE3_ASSOC))) {
+ $dest = new TransferDestination();
+ $dest->id = $row['dest_id'] !== null ? (int) $row['dest_id'] : null;
+ $dest->name = $row['dest_name'] ?? null;
+
+ return $dest;
+ }
+
+ return new TransferDestination();
+ }
+
+
+ public function setTransferDestination(?int $destId, ?string $destName = null): void
+ {
+ if (null === $destName) {
+ $this->db->exec("INSERT INTO Transfer(id, dest_id, dest_name) VALUES(1, $destId, NULL) ON CONFLICT(id) DO UPDATE SET dest_id=$destId, dest_name=NULL");
+
+ return;
+ }
+
+ $this->db->exec("INSERT INTO Transfer(id, dest_id, dest_name) VALUES(1, $destId, '$destName') ON CONFLICT(id) DO UPDATE SET dest_id=$destId, dest_name='$destName'");
+ }
+
+ public function setTransferDestinationName(?string $destName): void
+ {
+ $this->db->exec("INSERT INTO Transfer(id, dest_name) VALUES(1, '$destName') ON CONFLICT(id) DO UPDATE SET dest_name='$destName'");
+ }
+
public function getDatabaseReference(): SQLite3 {
return $this->db;
}
diff --git a/incl/js/JsBarcode.all.min.js b/incl/js/JsBarcode.all.min.js
new file mode 100644
index 0000000..80d1092
--- /dev/null
+++ b/incl/js/JsBarcode.all.min.js
@@ -0,0 +1,2 @@
+/*! JsBarcode v3.12.3 | (c) Johan Lindell | MIT license */
+!function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=16)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});e.default=function t(e,n){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.data=e,this.text=n.text||e,this.options=n}},function(t,e,n){"use strict";var r;function o(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}Object.defineProperty(e,"__esModule",{value:!0});var i=e.SET_A=0,a=e.SET_B=1,u=e.SET_C=2,f=(e.SHIFT=98,e.START_A=103),c=e.START_B=104,s=e.START_C=105;e.MODULO=103,e.STOP=106,e.FNC1=207,e.SET_BY_CODE=(o(r={},f,i),o(r,c,a),o(r,s,u),r),e.SWAP={101:i,100:a,99:u},e.A_START_CHAR=String.fromCharCode(208),e.B_START_CHAR=String.fromCharCode(209),e.C_START_CHAR=String.fromCharCode(210),e.A_CHARS="[\0-_Γ-Γ]",e.B_CHARS="[ -Γ-Γ]",e.C_CHARS="(Γ*[0-9]{2}Γ*)",e.BARS=[11011001100,11001101100,11001100110,10010011e3,10010001100,10001001100,10011001e3,10011000100,10001100100,11001001e3,11001000100,11000100100,10110011100,10011011100,10011001110,10111001100,10011101100,10011100110,11001110010,11001011100,11001001110,11011100100,11001110100,11101101110,11101001100,11100101100,11100100110,11101100100,11100110100,11100110010,11011011e3,11011000110,11000110110,10100011e3,10001011e3,10001000110,10110001e3,10001101e3,10001100010,11010001e3,11000101e3,11000100010,10110111e3,10110001110,10001101110,10111011e3,10111000110,10001110110,11101110110,11010001110,11000101110,11011101e3,11011100010,11011101110,11101011e3,11101000110,11100010110,11101101e3,11101100010,11100011010,11101111010,11001000010,11110001010,1010011e4,10100001100,1001011e4,10010000110,10000101100,10000100110,1011001e4,10110000100,1001101e4,10011000010,10000110100,10000110010,11000010010,1100101e4,11110111010,11000010100,10001111010,10100111100,10010111100,10010011110,10111100100,10011110100,10011110010,11110100100,11110010100,11110010010,11011011110,11011110110,11110110110,10101111e3,10100011110,10001011110,10111101e3,10111100010,11110101e3,11110100010,10111011110,10111101110,11101011110,11110101110,11010000100,1101001e4,11010011100,1100011101011]},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});e.SIDE_BIN="101",e.MIDDLE_BIN="01010",e.BINARIES={L:["0001101","0011001","0010011","0111101","0100011","0110001","0101111","0111011","0110111","0001011"],G:["0100111","0110011","0011011","0100001","0011101","0111001","0000101","0010001","0001001","0010111"],R:["1110010","1100110","1101100","1000010","1011100","1001110","1010000","1000100","1001000","1110100"],O:["0001101","0011001","0010011","0111101","0100011","0110001","0101111","0111011","0110111","0001011"],E:["0100111","0110011","0011011","0100001","0011101","0111001","0000101","0010001","0001001","0010111"]},e.EAN2_STRUCTURE=["LL","LG","GL","GG"],e.EAN5_STRUCTURE=["GGLLL","GLGLL","GLLGL","GLLLG","LGGLL","LLGGL","LLLGG","LGLGL","LGLLG","LLGLG"],e.EAN13_STRUCTURE=["LLLLLL","LLGLGG","LLGGLG","LLGGGL","LGLLGG","LGGLLG","LGGGLL","LGLGLG","LGLGGL","LGGLGL"]},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2);e.default=function(t,e,n){var o=t.split("").map((function(t,n){return r.BINARIES[e[n]]})).map((function(e,n){return e?e[t[n]]:""}));if(n){var i=t.length-1;o=o.map((function(t,e){return e=200){i=t.shift()-105;var a=u.SWAP[i];void 0!==a?o=e.next(t,n+1,a):(r!==u.SET_A&&r!==u.SET_B||i!==u.SHIFT||(t[0]=r===u.SET_A?t[0]>95?t[0]-96:t[0]:t[0]<32?t[0]+96:t[0]),o=e.next(t,n+1,r))}else i=e.correctIndex(t,r),o=e.next(t,n+1,r);var f=i*n;return{result:e.getBar(i)+o.result,checksum:f+o.checksum}}}]),e}(a.default);e.default=f},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.mod10=function(t){for(var e=0,n=0;n10*n.width?10*n.width:n.fontSize,r.guardHeight=n.height+r.fontSize/2+n.textMargin,r}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),r(e,[{key:"encode",value:function(){return this.options.flat?this.encodeFlat():this.encodeGuarded()}},{key:"leftText",value:function(t,e){return this.text.substr(t,e)}},{key:"leftEncode",value:function(t,e){return(0,i.default)(t,e)}},{key:"rightText",value:function(t,e){return this.text.substr(t,e)}},{key:"rightEncode",value:function(t,e){return(0,i.default)(t,e)}},{key:"encodeGuarded",value:function(){var t={fontSize:this.fontSize},e={height:this.guardHeight};return[{data:o.SIDE_BIN,options:e},{data:this.leftEncode(),text:this.leftText(),options:t},{data:o.MIDDLE_BIN,options:e},{data:this.rightEncode(),text:this.rightText(),options:t},{data:o.SIDE_BIN,options:e}]}},{key:"encodeFlat",value:function(){return{data:[o.SIDE_BIN,this.leftEncode(),o.MIDDLE_BIN,this.rightEncode(),o.SIDE_BIN].join(""),text:this.text}}}]),e}(a(n(0)).default);e.default=u},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){for(var n=0;n10*n.width?r.fontSize=10*n.width:r.fontSize=n.fontSize,r.guardHeight=n.height+r.fontSize/2+n.textMargin,r}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),r(e,[{key:"valid",value:function(){return-1!==this.data.search(/^[0-9]{12}$/)&&this.data[11]==u(this.data)}},{key:"encode",value:function(){return this.options.flat?this.flatEncoding():this.guardedEncoding()}},{key:"flatEncoding",value:function(){var t="";return t+="101",t+=(0,o.default)(this.data.substr(0,6),"LLLLLL"),t+="01010",t+=(0,o.default)(this.data.substr(6,6),"RRRRRR"),{data:t+="101",text:this.text}}},{key:"guardedEncoding",value:function(){var t=[];return this.displayValue&&t.push({data:"00000000",text:this.text.substr(0,1),options:{textAlign:"left",fontSize:this.fontSize}}),t.push({data:"101"+(0,o.default)(this.data[0],"L"),options:{height:this.guardHeight}}),t.push({data:(0,o.default)(this.data.substr(1,5),"LLLLL"),text:this.text.substr(1,5),options:{fontSize:this.fontSize}}),t.push({data:"01010",options:{height:this.guardHeight}}),t.push({data:(0,o.default)(this.data.substr(6,5),"RRRRR"),text:this.text.substr(6,5),options:{fontSize:this.fontSize}}),t.push({data:(0,o.default)(this.data[11],"R")+"101",options:{height:this.guardHeight}}),this.displayValue&&t.push({data:"00000000",text:this.text.substr(11,1),options:{textAlign:"right",fontSize:this.fontSize}}),t}}]),e}(i(n(0)).default);function u(t){var e,n=0;for(e=1;e<11;e+=2)n+=parseInt(t[e]);for(e=0;e<11;e+=2)n+=3*parseInt(t[e]);return(10-n%10)%10}e.default=a},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r,o=function(){function t(t,e){for(var n=0;n0?e.fontSize+e.textMargin:0)+e.marginTop+e.marginBottom}function u(t,e,n){if(n.displayValue&&ee&&(e=t[n].height);return e},e.getEncodingHeight=a,e.getBarcodePadding=u,e.calculateEncodingAttributes=function(t,e,n){for(var r=0;r=i(t);return e+String.fromCharCode(r?206:205)+u(t,r)}e.default=function(t){var e=void 0;if(a(t).length>=2)e=r.C_START_CHAR+f(t);else{var n=o(t)>i(t);e=(n?r.A_START_CHAR:r.B_START_CHAR)+u(t,n)}return e.replace(/[\xCD\xCE]([^])[\xCD\xCE]/,(function(t,e){return String.fromCharCode(203)+e}))}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r,o=function(){function t(t,e){for(var n=0;n10*n.width?r.fontSize=10*n.width:r.fontSize=n.fontSize,r.guardHeight=n.height+r.fontSize/2+n.textMargin,r}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),r(e,[{key:"valid",value:function(){return this.isValid}},{key:"encode",value:function(){return this.options.flat?this.flatEncoding():this.guardedEncoding()}},{key:"flatEncoding",value:function(){var t="";return t+="101",t+=this.encodeMiddleDigits(),{data:t+="010101",text:this.text}}},{key:"guardedEncoding",value:function(){var t=[];return this.displayValue&&t.push({data:"00000000",text:this.text[0],options:{textAlign:"left",fontSize:this.fontSize}}),t.push({data:"101",options:{height:this.guardHeight}}),t.push({data:this.encodeMiddleDigits(),text:this.text.substring(1,7),options:{fontSize:this.fontSize}}),t.push({data:"010101",options:{height:this.guardHeight}}),this.displayValue&&t.push({data:"00000000",text:this.text[7],options:{textAlign:"right",fontSize:this.fontSize}}),t}},{key:"encodeMiddleDigits",value:function(){var t=this.upcA[0],e=this.upcA[this.upcA.length-1],n=s[parseInt(e)][parseInt(t)];return(0,o.default)(this.middleDigits,n)}}]),e}(i.default);function p(t,e){for(var n=parseInt(t[t.length-1]),r=c[n],o="",i=0,u=0;u=3&&this.number<=131070}}]),e}(((r=i)&&r.__esModule?r:{default:r}).default);e.pharmacode=a},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.codabar=void 0;var r,o=function(){function t(t,e){for(var n=0;n":["(%)","I"],"?":["(%)","J"],"@":["(%)","V"],"[":["(%)","K"],"\\":["(%)","L"],"]":["(%)","M"],"^":["(%)","N"],_:["(%)","O"],"`":["(%)","W"],a:["(+)","A"],b:["(+)","B"],c:["(+)","C"],d:["(+)","D"],e:["(+)","E"],f:["(+)","F"],g:["(+)","G"],h:["(+)","H"],i:["(+)","I"],j:["(+)","J"],k:["(+)","K"],l:["(+)","L"],m:["(+)","M"],n:["(+)","N"],o:["(+)","O"],p:["(+)","P"],q:["(+)","Q"],r:["(+)","R"],s:["(+)","S"],t:["(+)","T"],u:["(+)","U"],v:["(+)","V"],w:["(+)","W"],x:["(+)","X"],y:["(+)","Y"],z:["(+)","Z"],"{":["(%)","P"],"|":["(%)","Q"],"}":["(%)","R"],"~":["(%)","S"],"":["(%)","T"]}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r,o=function(){function t(t,e){for(var n=0;n0?(n=0,o.textAlign="left"):"right"==t.textAlign?(n=e.width-1,o.textAlign="right"):(n=e.width/2,o.textAlign="center"),o.fillText(e.text,n,r))}},{key:"moveCanvasDrawing",value:function(t){this.canvas.getContext("2d").translate(t.width,0)}},{key:"restoreCanvas",value:function(){this.canvas.getContext("2d").restore()}}]),t}();e.default=f},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r,o=function(){function t(t,e){for(var n=0;n0&&(this.drawRect(a-e.width*i,r,e.width*i,e.height,t),i=0);i>0&&this.drawRect(a-e.width*(i-1),r,e.width*i,e.height,t)}},{key:"drawSVGText",value:function(t,e,n){var r,o,i=this.document.createElementNS(f,"text");e.displayValue&&(i.setAttribute("font-family",e.font),i.setAttribute("font-size",e.fontSize),e.fontOptions.includes("bold")&&i.setAttribute("font-weight","bold"),e.fontOptions.includes("italic")&&i.setAttribute("font-style","italic"),o="top"==e.textPosition?e.fontSize-e.textMargin:e.height+e.textMargin+e.fontSize,"left"==e.textAlign||n.barcodePadding>0?(r=0,i.setAttribute("text-anchor","start")):"right"==e.textAlign?(r=n.width-1,i.setAttribute("text-anchor","end")):(r=n.width/2,i.setAttribute("text-anchor","middle")),i.setAttribute("x",r),i.setAttribute("y",o),i.appendChild(this.document.createTextNode(n.text)),t.appendChild(i))}},{key:"setSvgAttributes",value:function(t,e){var n=this.svg;n.setAttribute("width",t+"px"),n.setAttribute("height",e+"px"),n.setAttribute("x","0px"),n.setAttribute("y","0px"),n.setAttribute("viewBox","0 0 "+t+" "+e),n.setAttribute("xmlns",f),n.setAttribute("version","1.1")}},{key:"createGroup",value:function(t,e,n){var r=this.document.createElementNS(f,"g");return r.setAttribute("transform","translate("+t+", "+e+")"),n.appendChild(r),r}},{key:"setGroupOptions",value:function(t,e){t.setAttribute("fill",e.lineColor)}},{key:"drawRect",value:function(t,e,n,r,o){var i=this.document.createElementNS(f,"rect");return i.setAttribute("x",t),i.setAttribute("y",e),i.setAttribute("width",n),i.setAttribute("height",r),o.appendChild(i),i}}]),t}();e.default=c},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){for(var n=0;n {
+ const elId = "#action-" + key;
+ JsBarcode(elId, action.barcode, {format: "CODE128", text: action.name});
+ });
+}
+
+function downloadBarcode(elId) {
+ // Get the image element
+ const img = document.getElementById(elId);
+ const barcodeName = img.dataset.name;
+
+ // Ask the user for confirmation before downloading
+ const filename = getBarcodeFilename(barcodeName);
+ if (!window.confirm(`Download "${filename}"?`)) {
+ return;
+ }
+
+ const imageDataUrl = img.src;
+
+ // Create a temporary anchor element
+ const link = document.createElement('a');
+ link.href = imageDataUrl;
+ link.download = filename;
+
+ // Append the link to the body (necessary for Firefox)
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+}
+
+function getBarcodeFilename(name) {
+ return "barcode_" + name.replace(/[^a-zA-Z0-9]/g, '') + ".png";
+}
\ No newline at end of file
diff --git a/incl/js/scripts_settings.js b/incl/js/scripts_settings.js
new file mode 100644
index 0000000..c518e8d
--- /dev/null
+++ b/incl/js/scripts_settings.js
@@ -0,0 +1,36 @@
+function showActionBarcodes() {
+ window.open('/barcodes.php?mode=act', '_blank');
+}
+
+function showTransferBarcodes() {
+ window.open('/barcodes.php?mode=loc', '_blank');
+}
+
+function showQuantityBarcodes() {
+ const startRaw = window.prompt("Starting quantity:", "1");
+ if (startRaw === null) return; // user cancelled
+
+ const endRaw = window.prompt("Ending quantity:", "10");
+ if (endRaw === null) return; // user cancelled
+
+ const startQty = parseInt(String(startRaw).trim(), 10);
+ const endQty = parseInt(String(endRaw).trim(), 10);
+
+ if (!Number.isFinite(startQty) || !Number.isFinite(endQty)) {
+ window.alert("Please enter valid whole numbers for both quantities.");
+ return;
+ }
+
+ if (startQty <= 0 || endQty <= 0) {
+ window.alert("Quantities must be greater than 0.");
+ return;
+ }
+
+ if (endQty < startQty) {
+ window.alert("Ending quantity must be greater than or equal to starting quantity.");
+ return;
+ }
+
+ const url = `/barcodes.php?mode=qty&startQty=${encodeURIComponent(startQty)}&endQty=${encodeURIComponent(endQty)}`;
+ window.open(url, "_blank");
+}
\ No newline at end of file
diff --git a/incl/modules/dbUpgrade.php b/incl/modules/dbUpgrade.php
index ccb3cca..0b123fd 100644
--- a/incl/modules/dbUpgrade.php
+++ b/incl/modules/dbUpgrade.php
@@ -16,6 +16,7 @@
require_once __DIR__ . "/../db.inc.php";
+
class DbUpgrade {
const LEGACY_DATABASE_PATH = __DIR__ . '/../barcodebuddy.db';
@@ -148,6 +149,11 @@ public function upgradeBarcodeBuddy(int $previousVersion): void {
$this->databaseConnection->updateConfig("LOOKUP_ORDER", $config["LOOKUP_ORDER"] . ",9");
}
}
+ if ($previousVersion < 1819) {
+ $config = BBConfig::getInstance();
+ $this->databaseConnection->updateConfig("BARCODE_TXFR", strtoupper($config["BARCODE_TXFR"]));
+ $this->db->exec("CREATE TABLE IF NOT EXISTS Transfer(id INTEGER PRIMARY KEY, dest_id INTEGER NULL, dest_name TEXT NULL)");
+ }
RedisConnection::updateCache();
}
diff --git a/incl/processing.inc.php b/incl/processing.inc.php
index 393e174..b20d2d2 100755
--- a/incl/processing.inc.php
+++ b/incl/processing.inc.php
@@ -22,6 +22,7 @@
require_once __DIR__ . "/lookupProviders/BarcodeLookup.class.php";
require_once __DIR__ . "/modules/choreManager.php";
+
/**
*
* Function that is called when a barcode is passed on
@@ -64,6 +65,29 @@ function processNewBarcode(string $barcodeInput, ?string $bestBeforeInDays = nul
$db->setTransactionState(STATE_ADD_SL);
return createLogModeChange(STATE_ADD_SL);
}
+ if (stringStartsWith($barcode, $config["BARCODE_TXFR"])) {
+ $destId = intval(str_replace($config["BARCODE_TXFR"], "", $barcode));
+ $txfrDest = $db->getTransferDestination();
+
+ if ($txfrDest->id !== $destId) {
+ $location = API::getLocation($destId);
+ if (null !== $location->id) {
+ $txfrDest->id = $location->id;
+ $txfrDest->name = $location->name;
+ $db->setTransferDestination($destId, $location->name);
+ } else {
+ $log = new LogOutput("Invalid destination location: ".$destId, EVENT_TYPE_ERROR, null, true);
+ return $log
+ ->setVerbose()
+ ->setWebsocketResultCode(WS_RESULT_TXFR_INVALID)
+ ->createLog()
+ ;
+ }
+ }
+
+ $db->setTransactionState(STATE_TXFR);
+ return createLogModeChange(STATE_TXFR, " to " . $txfrDest->name);
+ }
if (stringStartsWith($barcode, $config["BARCODE_Q"])) {
$quantity = str_replace($config["BARCODE_Q"], "", $barcode);
$quantity = checkIfFloat($quantity);
@@ -102,7 +126,7 @@ function processNewBarcode(string $barcodeInput, ?string $bestBeforeInDays = nul
}
-function createLogModeChange(int $state): string {
+function createLogModeChange(int $state, ?string $extra = null): string {
$text = "Set state to ";
switch ($state) {
case STATE_CONSUME:
@@ -126,9 +150,16 @@ function createLogModeChange(int $state): string {
case STATE_CONSUME_ALL:
$text .= "Consume all";
break;
+ case STATE_TXFR:
+ $text .= "Transfer";
+ break;
default:
throw new Exception("Invalid state");
}
+ if ($extra != null) {
+ $text .= $extra;
+ }
+
$log = new LogOutput($text, EVENT_TYPE_MODE_CHANGE);
return $log->setVerbose()->createLog();
}
@@ -156,12 +187,14 @@ function createLogModeChange(int $state): string {
const EVENT_TYPE_ACTION_REQUIRED = 17;
const EVENT_TYPE_CONSUME_ALL_PRODUCT = 18;
const EVENT_TYPE_NO_STOCK = 19;
+const EVENT_TYPE_TRANSFER_PRODUCT = 20;
const WS_RESULT_PRODUCT_FOUND = 0;
const WS_RESULT_PRODUCT_LOOKED_UP = 1;
const WS_RESULT_PRODUCT_UNKNOWN = 2;
const WS_RESULT_MODE_CHANGE = 4;
+const WS_RESULT_TXFR_INVALID = 8;
const WS_RESULT_ERROR = 'E';
@@ -195,10 +228,20 @@ function processChoreBarcode(string $barcode) {
function processUnknownBarcode(string $barcode, bool $websocketEnabled, LockGenerator &$fileLock, ?string $bestBeforeInDays, ?string $price): string {
$db = DatabaseConnection::getInstance();
$amount = 1;
+
if ($db->getTransactionState() == STATE_PURCHASE) {
$amount = QuantityManager::getStoredQuantityForBarcode($barcode);
}
- if ($db->isUnknownBarcodeAlreadyStored($barcode)) {
+
+ if ($db->getTransactionState() == STATE_TXFR) {
+ $log = new LogOutput("Cannot transfer unknown barcode, ignoring.", EVENT_TYPE_TRANSFER_PRODUCT, $barcode);
+ $output = $log
+ ->insertBarcodeInWebsocketText()
+ ->setSendWebsocket($websocketEnabled)
+ ->setWebsocketResultCode(WS_RESULT_PRODUCT_UNKNOWN)
+ ->createLog();
+ }
+ elseif ($db->isUnknownBarcodeAlreadyStored($barcode)) {
//Unknown barcode already in local database
$db->addQuantityToUnknownBarcode($barcode, $amount);
$log = new LogOutput("Unknown product already scanned. Increasing quantity.", EVENT_TYPE_ADD_NEW_BARCODE, $barcode);
@@ -207,18 +250,19 @@ function processUnknownBarcode(string $barcode, bool $websocketEnabled, LockGene
->setSendWebsocket($websocketEnabled)
->setWebsocketResultCode(WS_RESULT_PRODUCT_LOOKED_UP)
->createLog();
- } else {
- $productname = null;
+ }
+ else {
+ $productName = null;
if (is_numeric($barcode)) {
- $productname = BarcodeLookup::lookup($barcode);
+ $productName = BarcodeLookup::lookup($barcode);
}
- if ($productname != null) {
- $db->insertUnrecognizedBarcode($barcode, $amount, $bestBeforeInDays, $price, $productname);
- $log = new LogOutput("Unknown barcode looked up, found name: " . $productname["name"], EVENT_TYPE_ADD_NEW_BARCODE, $barcode);
+ if ($productName != null) {
+ $db->insertUnrecognizedBarcode($barcode, $amount, $bestBeforeInDays, $price, $productName);
+ $log = new LogOutput("Unknown barcode looked up, found name: " . $productName["name"], EVENT_TYPE_ADD_NEW_BARCODE, $barcode);
$output = $log
->insertBarcodeInWebsocketText()
->setSendWebsocket($websocketEnabled)
- ->setCustomWebsocketText($productname["name"])
+ ->setCustomWebsocketText($productName["name"])
->setWebsocketResultCode(WS_RESULT_PRODUCT_LOOKED_UP)
->createLog();
} else {
@@ -250,7 +294,8 @@ function stateToString(int $state): string {
STATE_PURCHASE => "Purchase",
STATE_OPEN => "Open",
STATE_GETSTOCK => "Inventory",
- STATE_ADD_SL => "Add to shoppinglist"
+ STATE_ADD_SL => "Add to shoppinglist",
+ STATE_TXFR => "Transfer",
);
return $allowedModes[$state];
}
@@ -311,6 +356,9 @@ function processModeChangeGetParameter(string $modeParameter): void {
case "shoppinglist":
$db->setTransactionState(STATE_ADD_SL);
break;
+ case "transfer":
+ $db->setTransactionState(STATE_TXFR);
+ break;
}
}
@@ -485,6 +533,47 @@ function processKnownBarcode(GrocyProduct $productInfo, string $barcode, bool $w
$log = "Added to shopping list: " . $amount . " " . $productInfo->unit . " of " . $productInfo->name;
API::addToShoppinglist($productInfo->id, 1);
return (new LogOutput($log, EVENT_TYPE_ADD_TO_SHOPPINGLIST))->createLog();
+ case STATE_TXFR:
+
+ // Get transfer destination and update if necessary
+ $transferDest = $db->getTransferDestination();
+ if (empty($transferDest->name)) {
+ $location = API::getLocation($transferDest->id);
+ if (null !== $location->id) {
+ $transferDest->name = $location->name;
+ $db->setTransferDestinationName($transferDest->name);
+ }
+ }
+
+ // Validate transfer destination
+ if ($transferDest->id == $productInfo->location->id) {
+ return (new LogOutput("Transfer destination is same as current location, skipping", EVENT_TYPE_TRANSFER_PRODUCT))
+ ->setWebsocketResultCode(WS_RESULT_PRODUCT_UNKNOWN)
+ ->createLog()
+ ;
+ }
+
+ // Transfer product
+ try {
+ API::transferProduct($productInfo->id, $productInfo->location->id, $transferDest->id);
+ } catch (\Throwable $e) {
+ return (new LogOutput("Transfer failed: " . $e->getMessage(), EVENT_TYPE_TRANSFER_PRODUCT))
+ ->setWebsocketResultCode(WS_RESULT_TXFR_INVALID)
+ ->setVerbose()
+ ->createLog();
+ }
+
+ $output = (new LogOutput(
+ "Transferred product: " . $productInfo->name . " from ".$productInfo->location->toString()." to ".$transferDest->name,
+ EVENT_TYPE_TRANSFER_PRODUCT
+ ))->setWebsocketResultCode(WS_RESULT_PRODUCT_FOUND)->createLog();
+
+ $fileLock->removeLock();
+ if ($config["REVERT_SINGLE"]) {
+ $db->saveLog("Reverting back to Consume", true);
+ $db->setTransactionState(STATE_CONSUME);
+ }
+ return $output;
default:
throw new Exception("Unknown state");
}
@@ -493,16 +582,16 @@ function processKnownBarcode(GrocyProduct $productInfo, string $barcode, bool $w
/**
* Function for generating the