From ff6918fcab83ae2bc952d02bb2592765b6e19ccd Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Sun, 15 Feb 2026 21:31:06 +0100 Subject: [PATCH] Release 2.49 (#5820) * bump packages * add column summarization per customer (#5808) * clarify database requirements * added helper methods to fetch original expected time Co-authored-by: GregorB54321 <34287148+GregorB54321@users.noreply.github.com> --- README.md | 2 +- composer.lock | 241 ++++++++++-------- phpstan.neon | 1 + src/Constants.php | 4 +- src/Entity/WorkingTime.php | 34 +++ templates/reporting/report_by_user.html.twig | 3 + .../reporting/report_by_user_data.html.twig | 32 ++- .../reporting/report_by_user_year.html.twig | 1 + .../report_by_user_year_export.html.twig | 1 + tests/Entity/WorkingTimeTest.php | 19 ++ 10 files changed, 221 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index 3a8f5bab..aecc8eda 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ and so much more. ### Requirements - PHP 8.1.3 minimum with support for 8.2, 8.3, 8.4, 8.5 -- MariaDB or MySQL +- MariaDB / MySQL in their oldest maintained LTS release, currently MariaDB >= [10.6](https://endoflife.date/mariadb) or MySQL >= [8.4](https://endoflife.date/mysql) - A webserver and subdomain (subdirectory is not supported) - PHP extensions: `gd`, `intl`, `json`, `mbstring`, `pdo`, `tokenizer`, `xml`, `xsl`, `zip` diff --git a/composer.lock b/composer.lock index 1a95994f..8fe8a3a2 100644 --- a/composer.lock +++ b/composer.lock @@ -726,29 +726,29 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.5", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", + "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", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -768,9 +768,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { "name": "doctrine/doctrine-bundle", @@ -1308,16 +1308,16 @@ }, { "name": "doctrine/migrations", - "version": "3.9.5", + "version": "3.9.6", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "1b823afbc40f932dae8272574faee53f2755eac5" + "reference": "ffd8355cdd8505fc650d9604f058bf62aedd80a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/1b823afbc40f932dae8272574faee53f2755eac5", - "reference": "1b823afbc40f932dae8272574faee53f2755eac5", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/ffd8355cdd8505fc650d9604f058bf62aedd80a1", + "reference": "ffd8355cdd8505fc650d9604f058bf62aedd80a1", "shasum": "" }, "require": { @@ -1391,7 +1391,7 @@ ], "support": { "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.9.5" + "source": "https://github.com/doctrine/migrations/tree/3.9.6" }, "funding": [ { @@ -1407,7 +1407,7 @@ "type": "tidelift" } ], - "time": "2025-11-20T11:15:36+00:00" + "time": "2026-02-11T06:46:11+00:00" }, { "name": "doctrine/orm", @@ -1608,16 +1608,16 @@ }, { "name": "doctrine/sql-formatter", - "version": "1.5.3", + "version": "1.5.4", "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "a8af23a8e9d622505baa2997465782cbe8bb7fc7" + "reference": "9563949f5cd3bd12a17d12fb980528bc141c5806" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/a8af23a8e9d622505baa2997465782cbe8bb7fc7", - "reference": "a8af23a8e9d622505baa2997465782cbe8bb7fc7", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/9563949f5cd3bd12a17d12fb980528bc141c5806", + "reference": "9563949f5cd3bd12a17d12fb980528bc141c5806", "shasum": "" }, "require": { @@ -1657,9 +1657,9 @@ ], "support": { "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.5.3" + "source": "https://github.com/doctrine/sql-formatter/tree/1.5.4" }, - "time": "2025-10-26T09:35:14+00:00" + "time": "2026-02-08T16:21:46+00:00" }, { "name": "easybill/zugferd-php", @@ -1919,29 +1919,29 @@ }, { "name": "friendsofsymfony/rest-bundle", - "version": "3.8.0", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/FriendsOfSymfony/FOSRestBundle.git", - "reference": "d24736896518bae817bf0de8a6b682cb6535044b" + "reference": "0a0d597a79734a8bd3a90cf6fa58de82940fc873" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfSymfony/FOSRestBundle/zipball/d24736896518bae817bf0de8a6b682cb6535044b", - "reference": "d24736896518bae817bf0de8a6b682cb6535044b", + "url": "https://api.github.com/repos/FriendsOfSymfony/FOSRestBundle/zipball/0a0d597a79734a8bd3a90cf6fa58de82940fc873", + "reference": "0a0d597a79734a8bd3a90cf6fa58de82940fc873", "shasum": "" }, "require": { "php": "^7.4|^8.0", - "symfony/config": "^5.4|^6.4|^7.0", - "symfony/dependency-injection": "^5.4|^6.4|^7.0", + "symfony/config": "^5.4|^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^5.4|^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.1|^3.0", - "symfony/event-dispatcher": "^5.4|^6.4|^7.0", - "symfony/framework-bundle": "^5.4|^6.4|^7.0", - "symfony/http-foundation": "^5.4|^6.4|^7.0", - "symfony/http-kernel": "^5.4|^6.4|^7.0", - "symfony/routing": "^5.4|^6.4|^7.0", - "symfony/security-core": "^5.4|^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^5.4|^6.4|^7.0|^8.0", + "symfony/http-foundation": "^5.4|^6.4|^7.0|^8.0", + "symfony/http-kernel": "^5.4|^6.4|^7.0|^8.0", + "symfony/routing": "^5.4|^6.4|^7.0|^8.0", + "symfony/security-core": "^5.4|^6.4|^7.0|^8.0", "willdurand/jsonp-callback-validator": "^1.0|^2.0", "willdurand/negotiation": "^2.0|^3.0" }, @@ -1959,19 +1959,19 @@ "psr/http-message": "^1.0", "psr/log": "^1.0|^2.0|^3.0", "sensio/framework-extra-bundle": "^6.1", - "symfony/asset": "^5.4|^6.4|^7.0", - "symfony/browser-kit": "^5.4|^6.4|^7.0", - "symfony/css-selector": "^5.4|^6.4|^7.0", - "symfony/expression-language": "^5.4|^6.4|^7.0", - "symfony/form": "^5.4|^6.4|^7.0", - "symfony/mime": "^5.4|^6.4|^7.0", - "symfony/phpunit-bridge": "^7.0.1", - "symfony/security-bundle": "^5.4|^6.4|^7.0", - "symfony/serializer": "^5.4|^6.4|^7.0", - "symfony/twig-bundle": "^5.4|^6.4|^7.0", - "symfony/validator": "^5.4|^6.4|^7.0", - "symfony/web-profiler-bundle": "^5.4|^6.4|^7.0", - "symfony/yaml": "^5.4|^6.4|^7.0" + "symfony/asset": "^5.4|^6.4|^7.0|^8.0", + "symfony/browser-kit": "^5.4|^6.4|^7.0|^8.0", + "symfony/css-selector": "^5.4|^6.4|^7.0|^8.0", + "symfony/expression-language": "^5.4|^6.4|^7.0|^8.0", + "symfony/form": "^5.4|^6.4|^7.0|^8.0", + "symfony/mime": "^5.4|^6.4|^7.0|^8.0", + "symfony/phpunit-bridge": "^7.0.1|^8.0", + "symfony/security-bundle": "^5.4|^6.4|^7.0|^8.0", + "symfony/serializer": "^5.4|^6.4|^7.0|^8.0", + "symfony/twig-bundle": "^5.4|^6.4|^7.0|^8.0", + "symfony/validator": "^5.4|^6.4|^7.0|^8.0", + "symfony/web-profiler-bundle": "^5.4|^6.4|^7.0|^8.0", + "symfony/yaml": "^5.4|^6.4|^7.0|^8.0" }, "suggest": { "jms/serializer-bundle": "Add support for advanced serialization capabilities, recommended", @@ -2019,9 +2019,9 @@ ], "support": { "issues": "https://github.com/FriendsOfSymfony/FOSRestBundle/issues", - "source": "https://github.com/FriendsOfSymfony/FOSRestBundle/tree/3.8.0" + "source": "https://github.com/FriendsOfSymfony/FOSRestBundle/tree/3.9.0" }, - "time": "2024-11-04T08:41:36+00:00" + "time": "2026-02-10T02:36:12+00:00" }, { "name": "gedmo/doctrine-extensions", @@ -2722,16 +2722,16 @@ }, { "name": "kevinpapst/tabler-bundle", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/kevinpapst/TablerBundle.git", - "reference": "b608ff05a7ba77b8fc32540bca086f6d18c2761e" + "reference": "d0f4130d7cba33015bbd798bfe529eeb4f19ac3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kevinpapst/TablerBundle/zipball/b608ff05a7ba77b8fc32540bca086f6d18c2761e", - "reference": "b608ff05a7ba77b8fc32540bca086f6d18c2761e", + "url": "https://api.github.com/repos/kevinpapst/TablerBundle/zipball/d0f4130d7cba33015bbd798bfe529eeb4f19ac3f", + "reference": "d0f4130d7cba33015bbd798bfe529eeb4f19ac3f", "shasum": "" }, "require": { @@ -2781,7 +2781,7 @@ "description": "Admin/Backend theme bundle for Symfony based on Tabler.io", "support": { "issues": "https://github.com/kevinpapst/TablerBundle/issues", - "source": "https://github.com/kevinpapst/TablerBundle/tree/2.1.0" + "source": "https://github.com/kevinpapst/TablerBundle/tree/2.2.0" }, "funding": [ { @@ -2793,7 +2793,7 @@ "type": "github" } ], - "time": "2026-01-19T09:05:10+00:00" + "time": "2026-02-15T08:43:44+00:00" }, { "name": "league/csv", @@ -10801,16 +10801,16 @@ }, { "name": "zircote/swagger-php", - "version": "5.8.0", + "version": "5.8.2", "source": { "type": "git", "url": "https://github.com/zircote/swagger-php.git", - "reference": "9cf5d1a0c159894026708c9e837e69140c2d3922" + "reference": "18ee6592518238a5a6c9905aae4926d5bbc65754" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/9cf5d1a0c159894026708c9e837e69140c2d3922", - "reference": "9cf5d1a0c159894026708c9e837e69140c2d3922", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/18ee6592518238a5a6c9905aae4926d5bbc65754", + "reference": "18ee6592518238a5a6c9905aae4926d5bbc65754", "shasum": "" }, "require": { @@ -10883,7 +10883,7 @@ ], "support": { "issues": "https://github.com/zircote/swagger-php/issues", - "source": "https://github.com/zircote/swagger-php/tree/5.8.0" + "source": "https://github.com/zircote/swagger-php/tree/5.8.2" }, "funding": [ { @@ -10891,7 +10891,7 @@ "type": "github" } ], - "time": "2026-01-28T01:27:48+00:00" + "time": "2026-02-10T20:10:15+00:00" } ], "packages-dev": [ @@ -11437,16 +11437,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.93.1", + "version": "v3.94.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a" + "reference": "883b20fb38c7866de9844ab6d0a205c423bde2d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b3546ab487c0762c39f308dc1ec0ea2c461fc21a", - "reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/883b20fb38c7866de9844ab6d0a205c423bde2d4", + "reference": "883b20fb38c7866de9844ab6d0a205c423bde2d4", "shasum": "" }, "require": { @@ -11463,7 +11463,7 @@ "react/event-loop": "^1.5", "react/socket": "^1.16", "react/stream": "^1.4", - "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", + "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0", "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", @@ -11477,18 +11477,18 @@ "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.7", - "infection/infection": "^0.32", - "justinrainbow/json-schema": "^6.6", + "facile-it/paraunit": "^1.3.1 || ^2.7.1", + "infection/infection": "^0.32.3", + "justinrainbow/json-schema": "^6.6.4", "keradus/cli-executor": "^2.3", "mikey179/vfsstream": "^1.6.12", - "php-coveralls/php-coveralls": "^2.9", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.48", + "php-coveralls/php-coveralls": "^2.9.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.7", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.7", + "phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.51", "symfony/polyfill-php85": "^1.33", - "symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0", - "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0" + "symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.4", + "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.1" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -11529,7 +11529,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.93.1" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.94.0" }, "funding": [ { @@ -11537,7 +11537,7 @@ "type": "github" } ], - "time": "2026-01-28T23:50:50+00:00" + "time": "2026-02-11T16:44:33+00:00" }, { "name": "masterminds/html5", @@ -11726,11 +11726,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.37", + "version": "2.1.39", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/28cd424c5ea984128c95cfa7ea658808e8954e49", - "reference": "28cd424c5ea984128c95cfa7ea658808e8954e49", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", + "reference": "c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", "shasum": "" }, "require": { @@ -11775,25 +11775,25 @@ "type": "github" } ], - "time": "2026-01-24T08:21:55+00:00" + "time": "2026-02-11T14:48:56+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-deprecation-rules.git", - "reference": "468e02c9176891cc901143da118f09dc9505fc2f" + "reference": "6b5571001a7f04fa0422254c30a0017ec2f2cacc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/468e02c9176891cc901143da118f09dc9505fc2f", - "reference": "468e02c9176891cc901143da118f09dc9505fc2f", + "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/6b5571001a7f04fa0422254c30a0017ec2f2cacc", + "reference": "6b5571001a7f04fa0422254c30a0017ec2f2cacc", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.15" + "phpstan/phpstan": "^2.1.39" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", @@ -11818,24 +11818,27 @@ "MIT" ], "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", - "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.3" + "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.4" }, - "time": "2025-05-14T10:56:57+00:00" + "time": "2026-02-09T13:21:14+00:00" }, { "name": "phpstan/phpstan-doctrine", - "version": "2.0.14", + "version": "2.0.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "70cd3e82fef49171163ff682a89cfe793d88581c" + "reference": "f4ff6084a26d91174b3f0b047589af293a893104" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/70cd3e82fef49171163ff682a89cfe793d88581c", - "reference": "70cd3e82fef49171163ff682a89cfe793d88581c", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/f4ff6084a26d91174b3f0b047589af293a893104", + "reference": "f4ff6084a26d91174b3f0b047589af293a893104", "shasum": "" }, "require": { @@ -11891,24 +11894,27 @@ "MIT" ], "description": "Doctrine extensions for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.14" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.16" }, - "time": "2026-01-25T14:56:09+00:00" + "time": "2026-02-11T08:54:45+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "2.0.12", + "version": "2.0.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "e4c5a22bf43d3d2bd5a780ad261a622ff62c49a4" + "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/e4c5a22bf43d3d2bd5a780ad261a622ff62c49a4", - "reference": "e4c5a22bf43d3d2bd5a780ad261a622ff62c49a4", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6ab598e1bc106e6827fd346ae4a12b4a5d634c32", + "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32", "shasum": "" }, "require": { @@ -11944,29 +11950,32 @@ "MIT" ], "description": "PHPUnit extensions and rules for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.12" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.16" }, - "time": "2026-01-22T13:40:00+00:00" + "time": "2026-02-14T09:05:21+00:00" }, { "name": "phpstan/phpstan-strict-rules", - "version": "2.0.8", + "version": "2.0.10", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "1ed9e626a37f7067b594422411539aa807190573" + "reference": "1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1ed9e626a37f7067b594422411539aa807190573", - "reference": "1ed9e626a37f7067b594422411539aa807190573", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f", + "reference": "1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.29" + "phpstan/phpstan": "^2.1.39" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", @@ -11992,24 +12001,27 @@ "MIT" ], "description": "Extra strict and opinionated rules for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.8" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.10" }, - "time": "2026-01-27T08:10:25+00:00" + "time": "2026-02-11T14:17:32+00:00" }, { "name": "phpstan/phpstan-symfony", - "version": "2.0.12", + "version": "2.0.14", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "a46dd92eaf15146cd932d897a272e59cd4108ce2" + "reference": "678136545a552a33b07f1a59a013f76df286cc34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/a46dd92eaf15146cd932d897a272e59cd4108ce2", - "reference": "a46dd92eaf15146cd932d897a272e59cd4108ce2", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/678136545a552a33b07f1a59a013f76df286cc34", + "reference": "678136545a552a33b07f1a59a013f76df286cc34", "shasum": "" }, "require": { @@ -12063,11 +12075,14 @@ } ], "description": "Symfony Framework extensions and rules for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.12" + "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.14" }, - "time": "2026-01-23T09:04:33+00:00" + "time": "2026-02-11T12:27:30+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/phpstan.neon b/phpstan.neon index 1cb71827..78bcdf60 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -27,6 +27,7 @@ parameters: numericOperandsInArithmeticOperators: true switchConditionsMatchingType: true noVariableVariables: false + reportNonIntStringArrayKey: false paths: - src tmpDir: %rootDir%/../../../var/cache/phpstan diff --git a/src/Constants.php b/src/Constants.php index 080b0d94..6001eef4 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -17,11 +17,11 @@ final class Constants /** * The current release version */ - public const VERSION = '2.48.0'; + public const VERSION = '2.49.0'; /** * The current release: major * 10000 + minor * 100 + patch */ - public const VERSION_ID = 24800; + public const VERSION_ID = 24900; /** * The software name */ diff --git a/src/Entity/WorkingTime.php b/src/Entity/WorkingTime.php index 00718b90..9d5d4ddb 100644 --- a/src/Entity/WorkingTime.php +++ b/src/Entity/WorkingTime.php @@ -36,6 +36,7 @@ class WorkingTime #[ORM\Column(name: 'expected', type: Types::INTEGER, nullable: false)] #[Assert\NotNull] private int $expectedTime = 0; + private ?int $originalExpectedTime = null; #[ORM\Column(name: 'actual', type: Types::INTEGER, nullable: false)] #[Assert\NotNull] private int $actualTime = 0; @@ -75,6 +76,39 @@ class WorkingTime public function setExpectedTime(int $expectedTime): void { $this->expectedTime = $expectedTime; + $this->storeOriginalExpectedTime(); + } + + public function storeOriginalExpectedTime(): void + { + if ($this->originalExpectedTime === null) { + $this->originalExpectedTime = $this->expectedTime; + } + } + + public function getOriginalExpectedTime(): int + { + $this->storeOriginalExpectedTime(); + + return $this->originalExpectedTime ?? 0; + } + + public function halveExpectedTime(): int + { + $this->storeOriginalExpectedTime(); + + $reduceBy = ($this->getOriginalExpectedTime() / 2); + + $this->expectedTime = $this->expectedTime - $reduceBy; + + return $reduceBy; + } + + public function emptyExpectedTime(): void + { + $this->storeOriginalExpectedTime(); + + $this->expectedTime = 0; } public function getActualTime(): int diff --git a/templates/reporting/report_by_user.html.twig b/templates/reporting/report_by_user.html.twig index 097bf157..432f8691 100644 --- a/templates/reporting/report_by_user.html.twig +++ b/templates/reporting/report_by_user.html.twig @@ -17,5 +17,8 @@ {% block column_classes_total -%} {% if column is weekend %} weekend{% endif %} {%- endblock %} + {% block column_classes_customer_total -%} + {% if column is weekend %} weekend{% endif %} + {%- endblock %} {% endembed %} {% endblock %} diff --git a/templates/reporting/report_by_user_data.html.twig b/templates/reporting/report_by_user_data.html.twig index df601f40..193bfe15 100644 --- a/templates/reporting/report_by_user_data.html.twig +++ b/templates/reporting/report_by_user_data.html.twig @@ -46,6 +46,23 @@ {% elseif data.customer != totalCurrency %} {% set totalCurrency = null %} {% endif %} + {% set customerTotalsDuration = {} %} + {% set customerTotalsInternalRate = {} %} + {% set customerTotalsRate = {} %} + {% for column in period.dateTimes %} + {% set dateKey = column|report_date %} + {% set customerTotalsDuration = customerTotalsDuration|merge({(dateKey): 0}) %} + {% set customerTotalsInternalRate = customerTotalsInternalRate|merge({(dateKey): 0}) %} + {% set customerTotalsRate = customerTotalsRate|merge({(dateKey): 0}) %} + {% endfor %} + {% for project in data.projects %} + {% for column in project.data.data %} + {% set dateKey = column.date|report_date %} + {% set customerTotalsDuration = customerTotalsDuration|merge({(dateKey): (customerTotalsDuration[dateKey] + column.duration)}) %} + {% set customerTotalsInternalRate = customerTotalsInternalRate|merge({(dateKey): (customerTotalsInternalRate[dateKey] + column.internalRate)}) %} + {% set customerTotalsRate = customerTotalsRate|merge({(dateKey): (customerTotalsRate[dateKey] + column.rate)}) %} + {% endfor %} + {% endfor %} {{ widgets.label_customer(data.customer) }} @@ -59,7 +76,20 @@ {% endif %} {% if with_links %}{% endif %} - + {% for column in period.dateTimes %} + {% set dateKey = column|report_date %} + + {% if customerTotalsRate[dateKey] > 0 or customerTotalsInternalRate[dateKey] > 0 or customerTotalsDuration[dateKey] > 0 %} + {% if dataType == 'rate' %} + {{ customerTotalsRate[dateKey]|money(currency) }} + {% elseif dataType == 'internalRate' %} + {{ customerTotalsInternalRate[dateKey]|money(currency) }} + {% else %} + {{ customerTotalsDuration[dateKey]|duration(decimal) }} + {% endif %} + {% endif %} + + {% endfor %} {% for project in data.projects %} {% set absoluteDuration = absoluteDuration + project.duration %} diff --git a/templates/reporting/report_by_user_year.html.twig b/templates/reporting/report_by_user_year.html.twig index 992f188e..8e3c29ea 100644 --- a/templates/reporting/report_by_user_year.html.twig +++ b/templates/reporting/report_by_user_year.html.twig @@ -14,5 +14,6 @@ {% block column_classes_project %}{% endblock %} {% block column_classes_activity %}{% endblock %} {% block column_classes_total %}{% endblock %} + {% block column_classes_customer_total %}{% endblock %} {% endembed %} {% endblock %} diff --git a/templates/reporting/report_by_user_year_export.html.twig b/templates/reporting/report_by_user_year_export.html.twig index 8347bd7f..f3ca7bc1 100644 --- a/templates/reporting/report_by_user_year_export.html.twig +++ b/templates/reporting/report_by_user_year_export.html.twig @@ -5,4 +5,5 @@ {% block column_classes_project %}{% endblock %} {% block column_classes_activity %}{% endblock %} {% block column_classes_total %}{% endblock %} + {% block column_classes_customer_total %}{% endblock %} {% endembed %} diff --git a/tests/Entity/WorkingTimeTest.php b/tests/Entity/WorkingTimeTest.php index 50963506..1850d2b5 100644 --- a/tests/Entity/WorkingTimeTest.php +++ b/tests/Entity/WorkingTimeTest.php @@ -50,4 +50,23 @@ class WorkingTimeTest extends TestCase self::assertSame($approvedBy, $sut->getApprovedBy()); self::assertTrue($sut->isApproved()); } + + public function testExpectedTime(): void + { + $user = new User(); + $user->setUsername('bar'); + $date = new \DateTimeImmutable(); + $sut = new WorkingTime($user, $date); + $sut->setExpectedTime(222222); + + self::assertEquals(222222, $sut->getExpectedTime()); + self::assertEquals(222222, $sut->getOriginalExpectedTime()); + $reducedBy = $sut->halveExpectedTime(); + self::assertEquals(111111, $reducedBy); + self::assertEquals(111111, $sut->getExpectedTime()); + self::assertEquals(222222, $sut->getOriginalExpectedTime()); + $sut->emptyExpectedTime(); + self::assertEquals(0, $sut->getExpectedTime()); + self::assertEquals(222222, $sut->getOriginalExpectedTime()); + } }