Release 2.9.0 (#4526)
* added fix to work around bc break in new phpword version * fix phpoffice deprecations * mark unused option as deprecated * support for DateTimeInterface and DateTimeImmutable where possible * use TRUSTED_PROXIES setting - fixes #4533 * re-enable Kimai test in docker test build script (#4541) * bump dependencies
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# Test PHP/Kimai
|
||||
if [ -z "$DATABASE_URL" ]; then
|
||||
DATABASE_URL="mysql://kimai:kimai@127.0.0.1:3306/kimai?charset=utf8mb4&serverVersion=5.7.40"
|
||||
fi
|
||||
|
||||
/opt/kimai/bin/console kimai:version
|
||||
if [ $? != 0 ]; then
|
||||
echo "PHP/Kimai not responding"
|
||||
|
||||
5
.github/workflows/docker.yaml
vendored
5
.github/workflows/docker.yaml
vendored
@@ -49,9 +49,8 @@ jobs:
|
||||
kimai/kimai2:${{ matrix.server }}-${{ matrix.type }}
|
||||
kimai/kimai2:${{ matrix.server }}-${{ env.kimai_version }}-${{ matrix.type }}
|
||||
push: true
|
||||
# see https://github.com/kimai/kimai/issues/4503
|
||||
# - name: Test Lite
|
||||
# run: docker run --rm --entrypoint /assets/test-lite.sh kimai/kimai2:${{ matrix.server }}-${{ matrix.type }}
|
||||
- name: Test Lite
|
||||
run: docker run --rm --entrypoint /assets/test-lite.sh kimai/kimai2:${{ matrix.server }}-${{ matrix.type }}
|
||||
|
||||
tag:
|
||||
needs: build
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"onelogin/php-saml": "^4.0",
|
||||
"pagerfanta/pagerfanta": "^3.0",
|
||||
"phpoffice/phpspreadsheet": "^1.16",
|
||||
"phpoffice/phpword": "1.1.*",
|
||||
"phpoffice/phpword": "^1.0",
|
||||
"psr/container": "^2.0",
|
||||
"psr/log": "^3.0",
|
||||
"scheb/2fa-backup-code": "^6.2",
|
||||
|
||||
250
composer.lock
generated
250
composer.lock
generated
@@ -4,35 +4,34 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "7b0ed62d4f9b4d06a6748c700fd04238",
|
||||
"content-hash": "a307b9a36c22711439283a3e5bc3249c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "azuyalabs/yasumi",
|
||||
"version": "2.6.0",
|
||||
"version": "2.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/azuyalabs/yasumi.git",
|
||||
"reference": "a73f198d796100c237adf94f3fca1d15eb82b22b"
|
||||
"reference": "37d1215d4f4012d3185bb9990c76ca17a4ff1c30"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/azuyalabs/yasumi/zipball/a73f198d796100c237adf94f3fca1d15eb82b22b",
|
||||
"reference": "a73f198d796100c237adf94f3fca1d15eb82b22b",
|
||||
"url": "https://api.github.com/repos/azuyalabs/yasumi/zipball/37d1215d4f4012d3185bb9990c76ca17a4ff1c30",
|
||||
"reference": "37d1215d4f4012d3185bb9990c76ca17a4ff1c30",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": ">=7.4"
|
||||
"php": ">=8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-intl": "*",
|
||||
"friendsofphp/php-cs-fixer": "^2.19 || ^3.16",
|
||||
"infection/infection": "^0.17 || ^0.26",
|
||||
"friendsofphp/php-cs-fixer": "^2.19 || ^3.40",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"phan/phan": "^5.4",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^8.5 || ^9.6",
|
||||
"vimeo/psalm": "^5.9"
|
||||
"vimeo/psalm": "^5.16"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-calendar": "For calculating the date of Easter"
|
||||
@@ -78,7 +77,7 @@
|
||||
"type": "other"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-26T15:35:45+00:00"
|
||||
"time": "2024-01-07T14:12:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@@ -2519,68 +2518,6 @@
|
||||
],
|
||||
"time": "2023-12-01T15:39:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laminas/laminas-escaper",
|
||||
"version": "2.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laminas/laminas-escaper.git",
|
||||
"reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/af459883f4018d0f8a0c69c7a209daef3bf973ba",
|
||||
"reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0"
|
||||
},
|
||||
"conflict": {
|
||||
"zendframework/zend-escaper": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"infection/infection": "^0.27.0",
|
||||
"laminas/laminas-coding-standard": "~2.5.0",
|
||||
"maglnet/composer-require-checker": "^3.8.0",
|
||||
"phpunit/phpunit": "^9.6.7",
|
||||
"psalm/plugin-phpunit": "^0.18.4",
|
||||
"vimeo/psalm": "^5.9"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laminas\\Escaper\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs",
|
||||
"homepage": "https://laminas.dev",
|
||||
"keywords": [
|
||||
"escaper",
|
||||
"laminas"
|
||||
],
|
||||
"support": {
|
||||
"chat": "https://laminas.dev/chat",
|
||||
"docs": "https://docs.laminas.dev/laminas-escaper/",
|
||||
"forum": "https://discourse.laminas.dev",
|
||||
"issues": "https://github.com/laminas/laminas-escaper/issues",
|
||||
"rss": "https://github.com/laminas/laminas-escaper/releases.atom",
|
||||
"source": "https://github.com/laminas/laminas-escaper"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://funding.communitybridge.org/projects/laminas-project",
|
||||
"type": "community_bridge"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-10T08:35:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/csv",
|
||||
"version": "9.14.0",
|
||||
@@ -3246,16 +3183,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nelmio/api-doc-bundle",
|
||||
"version": "v4.15.1",
|
||||
"version": "v4.16.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nelmio/NelmioApiDocBundle.git",
|
||||
"reference": "bef8b654ed410d16bf1086e2319ab215e5f36193"
|
||||
"reference": "31da761b6c9d275fb3bbee87c4c6888b17aec4ad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/bef8b654ed410d16bf1086e2319ab215e5f36193",
|
||||
"reference": "bef8b654ed410d16bf1086e2319ab215e5f36193",
|
||||
"url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/31da761b6c9d275fb3bbee87c4c6888b17aec4ad",
|
||||
"reference": "31da761b6c9d275fb3bbee87c4c6888b17aec4ad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3352,9 +3289,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nelmio/NelmioApiDocBundle/issues",
|
||||
"source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v4.15.1"
|
||||
"source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v4.16.2"
|
||||
},
|
||||
"time": "2024-01-02T14:56:33+00:00"
|
||||
"time": "2024-01-06T21:33:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nelmio/cors-bundle",
|
||||
@@ -3862,6 +3799,59 @@
|
||||
},
|
||||
"time": "2023-08-12T11:01:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/math",
|
||||
"version": "0.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPOffice/Math.git",
|
||||
"reference": "f0f8cad98624459c540cdd61d2a174d834471773"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPOffice/Math/zipball/f0f8cad98624459c540cdd61d2a174d834471773",
|
||||
"reference": "f0f8cad98624459c540cdd61d2a174d834471773",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-xml": "*",
|
||||
"php": "^7.1|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^0.12.88 || ^1.0.0",
|
||||
"phpunit/phpunit": "^7.0 || ^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpOffice\\Math\\": "src/Math/",
|
||||
"Tests\\PhpOffice\\Math\\": "tests/Math/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Progi1984",
|
||||
"homepage": "https://lefevre.dev"
|
||||
}
|
||||
],
|
||||
"description": "Math - Manipulate Math Formula",
|
||||
"homepage": "https://phpoffice.github.io/Math/",
|
||||
"keywords": [
|
||||
"MathML",
|
||||
"officemathml",
|
||||
"php"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPOffice/Math/issues",
|
||||
"source": "https://github.com/PHPOffice/Math/tree/0.1.0"
|
||||
},
|
||||
"time": "2023-09-25T12:08:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpspreadsheet",
|
||||
"version": "1.29.0",
|
||||
@@ -3969,24 +3959,24 @@
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpword",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPOffice/PHPWord.git",
|
||||
"reference": "90a55955e6a772bb4cd9b1ef6a7e88c8976c2561"
|
||||
"reference": "e76b701ef538cb749641514fcbc31a68078550fa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PHPWord/zipball/90a55955e6a772bb4cd9b1ef6a7e88c8976c2561",
|
||||
"reference": "90a55955e6a772bb4cd9b1ef6a7e88c8976c2561",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PHPWord/zipball/e76b701ef538cb749641514fcbc31a68078550fa",
|
||||
"reference": "e76b701ef538cb749641514fcbc31a68078550fa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-json": "*",
|
||||
"ext-xml": "*",
|
||||
"laminas/laminas-escaper": ">=2.6",
|
||||
"php": "^7.1|^8.0"
|
||||
"php": "^7.1|^8.0",
|
||||
"phpoffice/math": "^0.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"dompdf/dompdf": "^2.0",
|
||||
@@ -3996,8 +3986,9 @@
|
||||
"friendsofphp/php-cs-fixer": "^3.3",
|
||||
"mpdf/mpdf": "^8.1",
|
||||
"phpmd/phpmd": "^2.13",
|
||||
"phpstan/phpstan-phpunit": "@stable",
|
||||
"phpunit/phpunit": ">=7.0",
|
||||
"symfony/process": "^4.4",
|
||||
"symfony/process": "^4.4 || ^5.0",
|
||||
"tecnickcom/tcpdf": "^6.5"
|
||||
},
|
||||
"suggest": {
|
||||
@@ -4043,7 +4034,7 @@
|
||||
}
|
||||
],
|
||||
"description": "PHPWord - A pure PHP library for reading and writing word processing documents (OOXML, ODF, RTF, HTML, PDF)",
|
||||
"homepage": "https://phpword.readthedocs.io/",
|
||||
"homepage": "https://phpoffice.github.io/PHPWord/",
|
||||
"keywords": [
|
||||
"ISO IEC 29500",
|
||||
"OOXML",
|
||||
@@ -4071,22 +4062,22 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPOffice/PHPWord/issues",
|
||||
"source": "https://github.com/PHPOffice/PHPWord/tree/1.1.0"
|
||||
"source": "https://github.com/PHPOffice/PHPWord/tree/1.2.0"
|
||||
},
|
||||
"time": "2023-05-30T07:59:14+00:00"
|
||||
"time": "2023-11-30T11:22:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
"version": "1.24.5",
|
||||
"version": "1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||
"reference": "fedf211ff14ec8381c9bf5714e33a7a552dd1acc"
|
||||
"reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fedf211ff14ec8381c9bf5714e33a7a552dd1acc",
|
||||
"reference": "fedf211ff14ec8381c9bf5714e33a7a552dd1acc",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bd84b629c8de41aa2ae82c067c955e06f1b00240",
|
||||
"reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4118,9 +4109,9 @@
|
||||
"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/1.24.5"
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.25.0"
|
||||
},
|
||||
"time": "2023-12-16T09:33:33+00:00"
|
||||
"time": "2024-01-04T17:06:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/cache",
|
||||
@@ -10340,16 +10331,16 @@
|
||||
},
|
||||
{
|
||||
"name": "zircote/swagger-php",
|
||||
"version": "4.8.2",
|
||||
"version": "4.8.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zircote/swagger-php.git",
|
||||
"reference": "a70a5dc5db26183c86f589ff3d76fb11e141fc58"
|
||||
"reference": "598958d8a83cfbd44ba36388b2f9ed69e8b86ed4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/a70a5dc5db26183c86f589ff3d76fb11e141fc58",
|
||||
"reference": "a70a5dc5db26183c86f589ff3d76fb11e141fc58",
|
||||
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/598958d8a83cfbd44ba36388b2f9ed69e8b86ed4",
|
||||
"reference": "598958d8a83cfbd44ba36388b2f9ed69e8b86ed4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10415,9 +10406,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/zircote/swagger-php/issues",
|
||||
"source": "https://github.com/zircote/swagger-php/tree/4.8.2"
|
||||
"source": "https://github.com/zircote/swagger-php/tree/4.8.3"
|
||||
},
|
||||
"time": "2023-12-19T19:59:07+00:00"
|
||||
"time": "2024-01-07T22:33:09+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
@@ -10861,21 +10852,22 @@
|
||||
},
|
||||
{
|
||||
"name": "friendsofphp/php-cs-fixer",
|
||||
"version": "v3.45.0",
|
||||
"version": "v3.46.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||
"reference": "c0daa33cb2533cd73f48dde1c70c2afa3e7953b5"
|
||||
"reference": "be6831c9af1740470d2a773119b9273f8ac1c3d2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/c0daa33cb2533cd73f48dde1c70c2afa3e7953b5",
|
||||
"reference": "c0daa33cb2533cd73f48dde1c70c2afa3e7953b5",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/be6831c9af1740470d2a773119b9273f8ac1c3d2",
|
||||
"reference": "be6831c9af1740470d2a773119b9273f8ac1c3d2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer/semver": "^3.4",
|
||||
"composer/xdebug-handler": "^3.0.3",
|
||||
"ext-filter": "*",
|
||||
"ext-json": "*",
|
||||
"ext-tokenizer": "*",
|
||||
"php": "^7.4 || ^8.0",
|
||||
@@ -10939,7 +10931,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.45.0"
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.46.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10947,7 +10939,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-30T02:07:07+00:00"
|
||||
"time": "2024-01-03T21:38:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "masterminds/html5",
|
||||
@@ -11018,25 +11010,27 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.18.0",
|
||||
"version": "v5.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999"
|
||||
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999",
|
||||
"reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
|
||||
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-json": "*",
|
||||
"ext-tokenizer": "*",
|
||||
"php": ">=7.0"
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"ircmaxell/php-yacc": "^0.0.7",
|
||||
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/php-parse"
|
||||
@@ -11044,7 +11038,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.9-dev"
|
||||
"dev-master": "5.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -11068,9 +11062,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0"
|
||||
},
|
||||
"time": "2023-12-10T21:03:43+00:00"
|
||||
"time": "2024-01-07T17:17:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@@ -11185,16 +11179,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.10.50",
|
||||
"version": "1.10.55",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "06a98513ac72c03e8366b5a0cb00750b487032e4"
|
||||
"reference": "9a88f9d18ddf4cf54c922fbeac16c4cb164c5949"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/06a98513ac72c03e8366b5a0cb00750b487032e4",
|
||||
"reference": "06a98513ac72c03e8366b5a0cb00750b487032e4",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/9a88f9d18ddf4cf54c922fbeac16c4cb164c5949",
|
||||
"reference": "9a88f9d18ddf4cf54c922fbeac16c4cb164c5949",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11243,25 +11237,25 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-13T10:59:42+00:00"
|
||||
"time": "2024-01-08T12:32:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-doctrine",
|
||||
"version": "1.3.53",
|
||||
"version": "1.3.54",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
||||
"reference": "85def57e5db6ac6c8a512200c0cfadf7b6621b10"
|
||||
"reference": "f9555a2d54d685efd7003ae33c15e3d19d7d0c36"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/85def57e5db6ac6c8a512200c0cfadf7b6621b10",
|
||||
"reference": "85def57e5db6ac6c8a512200c0cfadf7b6621b10",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/f9555a2d54d685efd7003ae33c15e3d19d7d0c36",
|
||||
"reference": "f9555a2d54d685efd7003ae33c15e3d19d7d0c36",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"phpstan/phpstan": "^1.10.12"
|
||||
"phpstan/phpstan": "^1.10.48"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/collections": "<1.0",
|
||||
@@ -11278,7 +11272,7 @@
|
||||
"doctrine/dbal": "^2.13.8 || ^3.3.3",
|
||||
"doctrine/lexer": "^1.2.1",
|
||||
"doctrine/mongodb-odm": "^1.3 || ^2.1",
|
||||
"doctrine/orm": "^2.11.0",
|
||||
"doctrine/orm": "^2.14.0",
|
||||
"doctrine/persistence": "^1.3.8 || ^2.2.1",
|
||||
"gedmo/doctrine-extensions": "^3.8",
|
||||
"nesbot/carbon": "^2.49",
|
||||
@@ -11311,9 +11305,9 @@
|
||||
"description": "Doctrine extensions for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.53"
|
||||
"source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.54"
|
||||
},
|
||||
"time": "2023-11-21T10:31:58+00:00"
|
||||
"time": "2024-01-05T15:44:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
# see https://symfony.com/doc/current/reference/configuration/framework.html
|
||||
parameters:
|
||||
env(TRUSTED_PROXIES): ''
|
||||
|
||||
framework:
|
||||
# Deprecations for 7.0
|
||||
secret: '%env(APP_SECRET)%'
|
||||
csrf_protection: true
|
||||
annotations: false
|
||||
handle_all_throwables: true
|
||||
annotations:
|
||||
enabled: false
|
||||
|
||||
serializer:
|
||||
enable_attributes: true
|
||||
|
||||
# ---------------------------
|
||||
trusted_proxies: '%env(string:TRUSTED_PROXIES)%'
|
||||
|
||||
exceptions:
|
||||
App\Validator\ValidationFailedException:
|
||||
log_level: debug
|
||||
|
||||
secret: '%env(APP_SECRET)%'
|
||||
default_locale: en
|
||||
csrf_protection: true
|
||||
http_method_override: false
|
||||
|
||||
# Enables session support. Note that the session will ONLY be started if you read or write from it.
|
||||
|
||||
@@ -16,8 +16,8 @@ services:
|
||||
$projectDirectory: '%kernel.project_dir%'
|
||||
$kernelEnvironment: '%kernel.environment%'
|
||||
|
||||
# make classes in src/ available to be used as services
|
||||
# creates one service per class whose id is the fully-qualified class name
|
||||
# makes classes in src/ available to be used as services
|
||||
# this creates a service per class whose id is the fully-qualified class name
|
||||
App\:
|
||||
resource: '../src/*'
|
||||
exclude:
|
||||
|
||||
125
phpstan.neon
125
phpstan.neon
@@ -151,46 +151,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/API/UserController.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 1
|
||||
path: src/Activity/ActivityStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'billable' on mixed\\.$#"
|
||||
count: 1
|
||||
path: src/Activity/ActivityStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'counter' on mixed\\.$#"
|
||||
count: 3
|
||||
path: src/Activity/ActivityStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'duration' on mixed\\.$#"
|
||||
count: 4
|
||||
path: src/Activity/ActivityStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'exported' on mixed\\.$#"
|
||||
count: 2
|
||||
path: src/Activity/ActivityStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'id' on mixed\\.$#"
|
||||
count: 1
|
||||
path: src/Activity/ActivityStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'internalRate' on mixed\\.$#"
|
||||
count: 3
|
||||
path: src/Activity/ActivityStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'rate' on mixed\\.$#"
|
||||
count: 4
|
||||
path: src/Activity/ActivityStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Activity\\\\ActivityStatisticService\\:\\:createStatisticQueryBuilder\\(\\) has parameter \\$activities with no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
@@ -1106,21 +1066,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Controller/Reporting/CustomerMonthlyProjectsController.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getId\\(\\) on App\\\\Entity\\\\Customer\\|null\\.$#"
|
||||
count: 3
|
||||
path: src/Controller/Reporting/ProjectDateRangeController.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$begin of method App\\\\Project\\\\ProjectStatisticService\\:\\:getBudgetStatisticModelForProjectsByDateRange\\(\\) expects DateTime, DateTime\\|null given\\.$#"
|
||||
count: 1
|
||||
path: src/Controller/Reporting/ProjectDateRangeController.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$end of method App\\\\Project\\\\ProjectStatisticService\\:\\:getBudgetStatisticModelForProjectsByDateRange\\(\\) expects DateTime, DateTime\\|null given\\.$#"
|
||||
count: 1
|
||||
path: src/Controller/Reporting/ProjectDateRangeController.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getId\\(\\) on App\\\\Entity\\\\Customer\\|null\\.$#"
|
||||
count: 3
|
||||
@@ -1291,46 +1236,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Controller/WidgetController.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 1
|
||||
path: src/Customer/CustomerStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'billable' on mixed\\.$#"
|
||||
count: 1
|
||||
path: src/Customer/CustomerStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'counter' on mixed\\.$#"
|
||||
count: 3
|
||||
path: src/Customer/CustomerStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'duration' on mixed\\.$#"
|
||||
count: 4
|
||||
path: src/Customer/CustomerStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'exported' on mixed\\.$#"
|
||||
count: 2
|
||||
path: src/Customer/CustomerStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'id' on mixed\\.$#"
|
||||
count: 1
|
||||
path: src/Customer/CustomerStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'internalRate' on mixed\\.$#"
|
||||
count: 3
|
||||
path: src/Customer/CustomerStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'rate' on mixed\\.$#"
|
||||
count: 4
|
||||
path: src/Customer/CustomerStatisticService.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Customer\\\\CustomerStatisticService\\:\\:createStatisticQueryBuilder\\(\\) has parameter \\$customers with no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
@@ -3686,11 +3591,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Invoice/Hydrator/InvoiceModelCustomerHydrator.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$today of method App\\\\Customer\\\\CustomerStatisticService\\:\\:getBudgetStatisticModel\\(\\) expects DateTime, DateTime\\|null given\\.$#"
|
||||
count: 1
|
||||
path: src/Invoice/Hydrator/InvoiceModelCustomerHydrator.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getAddress\\(\\) on App\\\\Entity\\\\InvoiceTemplate\\|null\\.$#"
|
||||
count: 1
|
||||
@@ -5746,11 +5646,6 @@ parameters:
|
||||
count: 2
|
||||
path: src/Validator/Constraints/TeamValidator.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method format\\(\\) on DateTime\\|null\\.$#"
|
||||
count: 2
|
||||
path: src/Validator/Constraints/TimesheetBudgetUsedValidator.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getCurrency\\(\\) on App\\\\Entity\\\\Customer\\|null\\.$#"
|
||||
count: 1
|
||||
@@ -5771,31 +5666,11 @@ parameters:
|
||||
count: 1
|
||||
path: src/Validator/Constraints/TimesheetBudgetUsedValidator.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getTimezone\\(\\) on DateTime\\|null\\.$#"
|
||||
count: 1
|
||||
path: src/Validator/Constraints/TimesheetBudgetUsedValidator.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$timesheet \\(App\\\\Entity\\\\Timesheet\\) of method App\\\\Validator\\\\Constraints\\\\TimesheetBudgetUsedValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#"
|
||||
count: 2
|
||||
path: src/Validator/Constraints/TimesheetBudgetUsedValidator.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$today of method App\\\\Activity\\\\ActivityStatisticService\\:\\:getBudgetStatisticModel\\(\\) expects DateTime, DateTime\\|null given\\.$#"
|
||||
count: 1
|
||||
path: src/Validator/Constraints/TimesheetBudgetUsedValidator.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$today of method App\\\\Customer\\\\CustomerStatisticService\\:\\:getBudgetStatisticModel\\(\\) expects DateTime, DateTime\\|null given\\.$#"
|
||||
count: 1
|
||||
path: src/Validator/Constraints/TimesheetBudgetUsedValidator.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$today of method App\\\\Project\\\\ProjectStatisticService\\:\\:getBudgetStatisticModel\\(\\) expects DateTime, DateTime\\|null given\\.$#"
|
||||
count: 1
|
||||
path: src/Validator/Constraints/TimesheetBudgetUsedValidator.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#4 \\$duration of method App\\\\Validator\\\\Constraints\\\\TimesheetBudgetUsedValidator\\:\\:checkBudgets\\(\\) expects int, float\\|int\\|null given\\.$#"
|
||||
count: 3
|
||||
|
||||
@@ -16,7 +16,8 @@ use App\Model\ActivityBudgetStatisticModel;
|
||||
use App\Model\ActivityStatistic;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
@@ -26,19 +27,15 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
*/
|
||||
class ActivityStatisticService
|
||||
{
|
||||
public function __construct(private TimesheetRepository $timesheetRepository, private EventDispatcherInterface $dispatcher)
|
||||
public function __construct(private readonly TimesheetRepository $timesheetRepository, private readonly EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: this method does not respect the budget type. Your results will always be wither the "full lifetime data" or the "selected date-range".
|
||||
*
|
||||
* @param Activity $activity
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @return ActivityStatistic
|
||||
* WARNING: this method does not respect the budget type.
|
||||
* Your results will always be with the "full lifetime data" or the "selected date-range".
|
||||
*/
|
||||
public function getActivityStatistics(Activity $activity, ?DateTime $begin = null, ?DateTime $end = null): ActivityStatistic
|
||||
public function getActivityStatistics(Activity $activity, ?DateTimeInterface $begin = null, ?DateTimeInterface $end = null): ActivityStatistic
|
||||
{
|
||||
$statistics = $this->getBudgetStatistic([$activity], $begin, $end);
|
||||
$event = new ActivityStatisticEvent($activity, array_pop($statistics), $begin, $end);
|
||||
@@ -47,7 +44,7 @@ class ActivityStatisticService
|
||||
return $event->getStatistic();
|
||||
}
|
||||
|
||||
public function getBudgetStatisticModel(Activity $activity, DateTime $today): ActivityBudgetStatisticModel
|
||||
public function getBudgetStatisticModel(Activity $activity, DateTimeInterface $today): ActivityBudgetStatisticModel
|
||||
{
|
||||
$stats = new ActivityBudgetStatisticModel($activity);
|
||||
$stats->setStatisticTotal($this->getActivityStatistics($activity));
|
||||
@@ -68,10 +65,9 @@ class ActivityStatisticService
|
||||
|
||||
/**
|
||||
* @param Activity[] $activities
|
||||
* @param DateTime $today
|
||||
* @return ActivityBudgetStatisticModel[]
|
||||
*/
|
||||
public function getBudgetStatisticModelForActivities(array $activities, DateTime $today): array
|
||||
public function getBudgetStatisticModelForActivities(array $activities, DateTimeInterface $today): array
|
||||
{
|
||||
$models = [];
|
||||
$monthly = [];
|
||||
@@ -121,11 +117,9 @@ class ActivityStatisticService
|
||||
|
||||
/**
|
||||
* @param Activity[] $activities
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @return array<int, ActivityStatistic>
|
||||
*/
|
||||
private function getBudgetStatistic(array $activities, ?DateTime $begin = null, ?DateTime $end = null): array
|
||||
private function getBudgetStatistic(array $activities, ?DateTimeInterface $begin = null, ?DateTimeInterface $end = null): array
|
||||
{
|
||||
$statistics = [];
|
||||
foreach ($activities as $activity) {
|
||||
@@ -139,25 +133,25 @@ class ActivityStatisticService
|
||||
if (null !== $result) {
|
||||
foreach ($result as $resultRow) {
|
||||
$statistic = $statistics[$resultRow['id']];
|
||||
$statistic->setDuration($statistic->getDuration() + $resultRow['duration']);
|
||||
$statistic->setRate($statistic->getRate() + $resultRow['rate']);
|
||||
$statistic->setInternalRate($statistic->getInternalRate() + $resultRow['internalRate']);
|
||||
$statistic->setCounter($statistic->getCounter() + $resultRow['counter']);
|
||||
$statistic->addDuration((int) $resultRow['duration']);
|
||||
$statistic->addRate((float) $resultRow['rate']);
|
||||
$statistic->addInternalRate((float) $resultRow['internalRate']);
|
||||
$statistic->addCounter((int) $resultRow['counter']);
|
||||
if ($resultRow['billable']) {
|
||||
$statistic->setDurationBillable($statistic->getDurationBillable() + $resultRow['duration']);
|
||||
$statistic->setRateBillable($statistic->getRateBillable() + $resultRow['rate']);
|
||||
$statistic->setInternalRateBillable($statistic->getInternalRateBillable() + $resultRow['internalRate']);
|
||||
$statistic->setCounterBillable($statistic->getCounterBillable() + $resultRow['counter']);
|
||||
$statistic->addDurationBillable((int) $resultRow['duration']);
|
||||
$statistic->addRateBillable((float) $resultRow['rate']);
|
||||
$statistic->addInternalRateBillable((float) $resultRow['internalRate']);
|
||||
$statistic->addCounterBillable((int) $resultRow['counter']);
|
||||
if ($resultRow['exported']) {
|
||||
$statistic->setDurationBillableExported($statistic->getDurationBillableExported() + $resultRow['duration']);
|
||||
$statistic->setRateBillableExported($statistic->getRateBillableExported() + $resultRow['rate']);
|
||||
$statistic->addDurationBillableExported((int) $resultRow['duration']);
|
||||
$statistic->addRateBillableExported((float) $resultRow['rate']);
|
||||
}
|
||||
}
|
||||
if ($resultRow['exported']) {
|
||||
$statistic->setDurationExported($statistic->getDurationExported() + $resultRow['duration']);
|
||||
$statistic->setRateExported($statistic->getRateExported() + $resultRow['rate']);
|
||||
$statistic->setInternalRateExported($statistic->getInternalRateExported() + $resultRow['internalRate']);
|
||||
$statistic->setCounterExported($statistic->getCounterExported() + $resultRow['counter']);
|
||||
$statistic->addDurationExported((int) $resultRow['duration']);
|
||||
$statistic->addRateExported((float) $resultRow['rate']);
|
||||
$statistic->addInternalRateExported((float) $resultRow['internalRate']);
|
||||
$statistic->addCounterExported((int) $resultRow['counter']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,7 +159,7 @@ class ActivityStatisticService
|
||||
return $statistics;
|
||||
}
|
||||
|
||||
private function createStatisticQueryBuilder(array $activities, DateTime $begin = null, ?DateTime $end = null): QueryBuilder
|
||||
private function createStatisticQueryBuilder(array $activities, \DateTimeInterface $begin = null, ?\DateTimeInterface $end = null): QueryBuilder
|
||||
{
|
||||
$qb = $this->timesheetRepository->createQueryBuilder('t');
|
||||
$qb
|
||||
@@ -187,14 +181,14 @@ class ActivityStatisticService
|
||||
if ($begin !== null) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->gte('t.begin', ':begin'))
|
||||
->setParameter('begin', $begin, Types::DATETIME_MUTABLE)
|
||||
->setParameter('begin', DateTimeImmutable::createFromInterface($begin), Types::DATETIME_IMMUTABLE)
|
||||
;
|
||||
}
|
||||
|
||||
if ($end !== null) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->lte('t.begin', ':end'))
|
||||
->setParameter('end', $end, Types::DATETIME_MUTABLE)
|
||||
->setParameter('end', DateTimeImmutable::createFromInterface($end), Types::DATETIME_IMMUTABLE)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,10 +158,12 @@ final class ExportCreateCommand extends Command
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
if (!$start instanceof \DateTime) {
|
||||
if (!$start instanceof \DateTimeInterface) {
|
||||
$start = $dateFactory->getStartOfMonth();
|
||||
}
|
||||
$start->setTime(0, 0, 0);
|
||||
|
||||
$start = \DateTimeImmutable::createFromInterface($start);
|
||||
$start = $start->setTime(0, 0, 0);
|
||||
|
||||
$end = $input->getOption('end');
|
||||
if (!empty($end)) {
|
||||
@@ -174,11 +176,12 @@ final class ExportCreateCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($end)) {
|
||||
if (!$end instanceof \DateTimeInterface) {
|
||||
$end = $dateFactory->getEndOfMonth($start);
|
||||
}
|
||||
|
||||
$end->setTime(23, 59, 59);
|
||||
$end = \DateTimeImmutable::createFromInterface($end);
|
||||
$end = $end->setTime(23, 59, 59);
|
||||
|
||||
$directory = rtrim(sys_get_temp_dir(), '/') . '/';
|
||||
if ($input->getOption('directory') !== null) {
|
||||
|
||||
@@ -64,7 +64,7 @@ final class InvoiceCreateCommand extends Command
|
||||
->addOption('project', null, InputOption::VALUE_OPTIONAL, 'Comma separated list of project IDs', null)
|
||||
->addOption('by-customer', null, InputOption::VALUE_NONE, 'If set, one invoice for each active customer in the given timerange is created')
|
||||
->addOption('by-project', null, InputOption::VALUE_NONE, 'If set, one invoice for each active project in the given timerange is created')
|
||||
->addOption('set-exported', null, InputOption::VALUE_NONE, 'Whether the invoice items should be marked as exported')
|
||||
->addOption('set-exported', null, InputOption::VALUE_NONE, '[DEPRECATED] this flag has no meaning any more: invoiced items are always exported')
|
||||
->addOption('template', null, InputOption::VALUE_OPTIONAL, 'Invoice template', null)
|
||||
->addOption('search', null, InputOption::VALUE_OPTIONAL, 'Search term to filter invoice entries', null)
|
||||
->addOption('exported', null, InputOption::VALUE_OPTIONAL, 'Exported filter for invoice entries (possible values: exported, all), by default only "not exported" items are fetched', null)
|
||||
@@ -156,10 +156,12 @@ final class InvoiceCreateCommand extends Command
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
if (!$start instanceof \DateTime) {
|
||||
if (!$start instanceof \DateTimeInterface) {
|
||||
$start = $dateFactory->getStartOfMonth();
|
||||
}
|
||||
$start->setTime(0, 0, 0);
|
||||
|
||||
$start = \DateTimeImmutable::createFromInterface($start);
|
||||
$start = $start->setTime(0, 0, 0);
|
||||
|
||||
$end = $input->getOption('end');
|
||||
if (!empty($end)) {
|
||||
@@ -171,17 +173,18 @@ final class InvoiceCreateCommand extends Command
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
if (!$end instanceof \DateTime) {
|
||||
if (!$end instanceof \DateTimeInterface) {
|
||||
$end = $dateFactory->getEndOfMonth();
|
||||
}
|
||||
$end->setTime(23, 59, 59);
|
||||
|
||||
$end = \DateTimeImmutable::createFromInterface($end);
|
||||
$end = $end->setTime(23, 59, 59);
|
||||
|
||||
$searchTerm = null;
|
||||
if (null !== $input->getOption('search')) {
|
||||
$searchTerm = new SearchTerm($input->getOption('search'));
|
||||
}
|
||||
|
||||
$markAsExported = false;
|
||||
if ($input->getOption('preview') !== null) {
|
||||
$this->previewUniqueFile = (bool) $input->getOption('preview-unique');
|
||||
$this->previewDirectory = rtrim($input->getOption('preview'), '/') . '/';
|
||||
@@ -191,7 +194,7 @@ final class InvoiceCreateCommand extends Command
|
||||
return Command::FAILURE;
|
||||
}
|
||||
} elseif ($input->getOption('set-exported')) {
|
||||
$markAsExported = true;
|
||||
@trigger_error('The "set-exported" option of kimai:invoice:create command has no meaning anymore, it will be removed soon', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
// =============== VALIDATION END ===============
|
||||
|
||||
@@ -17,11 +17,11 @@ class Constants
|
||||
/**
|
||||
* The current release version
|
||||
*/
|
||||
public const VERSION = '2.8.0';
|
||||
public const VERSION = '2.9.0';
|
||||
/**
|
||||
* The current release: major * 10000 + minor * 100 + patch
|
||||
*/
|
||||
public const VERSION_ID = 20800;
|
||||
public const VERSION_ID = 20900;
|
||||
/**
|
||||
* The software name
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Controller\Reporting;
|
||||
|
||||
use App\Controller\AbstractController;
|
||||
use App\Entity\Customer;
|
||||
use App\Form\Model\DateRange;
|
||||
use App\Project\ProjectStatisticService;
|
||||
use App\Reporting\ProjectDateRange\ProjectDateRangeForm;
|
||||
@@ -37,15 +38,20 @@ final class ProjectDateRangeController extends AbstractController
|
||||
]);
|
||||
$form->submit($request->query->all(), false);
|
||||
|
||||
$begin = $query->getMonth() ?? $defaultStart;
|
||||
|
||||
$dateRange = new DateRange(true);
|
||||
$dateRange->setBegin($query->getMonth() ?? $defaultStart);
|
||||
$dateRange->setEnd($dateFactory->getEndOfMonth($dateRange->getBegin()));
|
||||
$dateRange->setBegin($begin);
|
||||
$end = $dateFactory->getEndOfMonth($dateRange->getBegin()); // this resets the time
|
||||
|
||||
$dateRange->setEnd($end);
|
||||
|
||||
$projects = $service->findProjectsForDateRange($query, $dateRange);
|
||||
$entries = $service->getBudgetStatisticModelForProjectsByDateRange($projects, $dateRange->getBegin(), $dateRange->getEnd(), $dateRange->getEnd());
|
||||
$entries = $service->getBudgetStatisticModelForProjectsByDateRange($projects, $begin, $end, $end);
|
||||
|
||||
$byCustomer = [];
|
||||
foreach ($entries as $entry) {
|
||||
/** @var Customer $customer */
|
||||
$customer = $entry->getProject()->getCustomer();
|
||||
if (!isset($byCustomer[$customer->getId()])) {
|
||||
$byCustomer[$customer->getId()] = ['customer' => $customer, 'projects' => []];
|
||||
|
||||
@@ -17,6 +17,8 @@ use App\Model\CustomerStatistic;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
@@ -27,17 +29,12 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
*/
|
||||
class CustomerStatisticService
|
||||
{
|
||||
public function __construct(private TimesheetRepository $timesheetRepository, private EventDispatcherInterface $dispatcher)
|
||||
public function __construct(private readonly TimesheetRepository $timesheetRepository, private readonly EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: this method does not respect the budget type. Your results will always be wither the "full lifetime data" or the "selected date-range".
|
||||
*
|
||||
* @param Customer $customer
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @return CustomerStatistic
|
||||
* WARNING: this method does not respect the budget type. Your results will always be with the "full lifetime data" or the "selected date-range".
|
||||
*/
|
||||
public function getCustomerStatistics(Customer $customer, ?DateTime $begin = null, ?DateTime $end = null): CustomerStatistic
|
||||
{
|
||||
@@ -48,13 +45,13 @@ class CustomerStatisticService
|
||||
return $event->getStatistic();
|
||||
}
|
||||
|
||||
public function getBudgetStatisticModel(Customer $customer, DateTime $today): CustomerBudgetStatisticModel
|
||||
public function getBudgetStatisticModel(Customer $customer, DateTimeInterface $today): CustomerBudgetStatisticModel
|
||||
{
|
||||
$stats = new CustomerBudgetStatisticModel($customer);
|
||||
$stats->setStatisticTotal($this->getCustomerStatistics($customer));
|
||||
|
||||
$begin = null;
|
||||
$end = $today;
|
||||
$end = DateTime::createFromInterface($today);
|
||||
|
||||
if ($customer->isMonthlyBudget()) {
|
||||
$dateFactory = new DateTimeFactory($today->getTimezone());
|
||||
@@ -69,11 +66,9 @@ class CustomerStatisticService
|
||||
|
||||
/**
|
||||
* @param Customer[] $customers
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @return array<int, CustomerStatistic>
|
||||
*/
|
||||
private function getBudgetStatistic(array $customers, ?DateTime $begin = null, ?DateTime $end = null): array
|
||||
private function getBudgetStatistic(array $customers, ?DateTimeInterface $begin = null, ?DateTimeInterface $end = null): array
|
||||
{
|
||||
$statistics = [];
|
||||
foreach ($customers as $customer) {
|
||||
@@ -87,25 +82,25 @@ class CustomerStatisticService
|
||||
if (null !== $result) {
|
||||
foreach ($result as $resultRow) {
|
||||
$statistic = $statistics[$resultRow['id']];
|
||||
$statistic->setDuration($statistic->getDuration() + $resultRow['duration']);
|
||||
$statistic->setRate($statistic->getRate() + $resultRow['rate']);
|
||||
$statistic->setInternalRate($statistic->getInternalRate() + $resultRow['internalRate']);
|
||||
$statistic->setCounter($statistic->getCounter() + $resultRow['counter']);
|
||||
$statistic->addDuration((int) $resultRow['duration']);
|
||||
$statistic->addRate((float) $resultRow['rate']);
|
||||
$statistic->addInternalRate((float) $resultRow['internalRate']);
|
||||
$statistic->addCounter((int) $resultRow['counter']);
|
||||
if ($resultRow['billable']) {
|
||||
$statistic->setDurationBillable($statistic->getDurationBillable() + $resultRow['duration']);
|
||||
$statistic->setRateBillable($statistic->getRateBillable() + $resultRow['rate']);
|
||||
$statistic->setInternalRateBillable($statistic->getInternalRateBillable() + $resultRow['internalRate']);
|
||||
$statistic->setCounterBillable($statistic->getCounterBillable() + $resultRow['counter']);
|
||||
$statistic->addDurationBillable((int) $resultRow['duration']);
|
||||
$statistic->addRateBillable((float) $resultRow['rate']);
|
||||
$statistic->addInternalRateBillable((float) $resultRow['internalRate']);
|
||||
$statistic->addCounterBillable((int) $resultRow['counter']);
|
||||
if ($resultRow['exported']) {
|
||||
$statistic->setDurationBillableExported($statistic->getDurationBillableExported() + $resultRow['duration']);
|
||||
$statistic->setRateBillableExported($statistic->getRateBillableExported() + $resultRow['rate']);
|
||||
$statistic->addDurationBillableExported((int) $resultRow['duration']);
|
||||
$statistic->addRateBillableExported((float) $resultRow['rate']);
|
||||
}
|
||||
}
|
||||
if ($resultRow['exported']) {
|
||||
$statistic->setDurationExported($statistic->getDurationExported() + $resultRow['duration']);
|
||||
$statistic->setRateExported($statistic->getRateExported() + $resultRow['rate']);
|
||||
$statistic->setInternalRateExported($statistic->getInternalRateExported() + $resultRow['internalRate']);
|
||||
$statistic->setCounterExported($statistic->getCounterExported() + $resultRow['counter']);
|
||||
$statistic->addDurationExported((int) $resultRow['duration']);
|
||||
$statistic->addRateExported((float) $resultRow['rate']);
|
||||
$statistic->addInternalRateExported((float) $resultRow['internalRate']);
|
||||
$statistic->addCounterExported((int) $resultRow['counter']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,7 +108,7 @@ class CustomerStatisticService
|
||||
return $statistics;
|
||||
}
|
||||
|
||||
private function createStatisticQueryBuilder(array $customers, DateTime $begin = null, ?DateTime $end = null): QueryBuilder
|
||||
private function createStatisticQueryBuilder(array $customers, ?DateTimeInterface $begin = null, ?DateTimeInterface $end = null): QueryBuilder
|
||||
{
|
||||
$qb = $this->timesheetRepository->createQueryBuilder('t');
|
||||
$qb
|
||||
@@ -136,14 +131,14 @@ class CustomerStatisticService
|
||||
if ($begin !== null) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->gte('t.begin', ':begin'))
|
||||
->setParameter('begin', $begin, Types::DATETIME_MUTABLE)
|
||||
->setParameter('begin', DateTimeImmutable::createFromInterface($begin), Types::DATETIME_IMMUTABLE)
|
||||
;
|
||||
}
|
||||
|
||||
if ($end !== null) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->lte('t.begin', ':end'))
|
||||
->setParameter('end', $end, Types::DATETIME_MUTABLE)
|
||||
->setParameter('end', DateTimeImmutable::createFromInterface($end), Types::DATETIME_IMMUTABLE)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,8 +109,7 @@ final class AppExtension extends Extension
|
||||
$locales = explode('|', $container->getParameter('app_locales'));
|
||||
|
||||
$directory = $container->getParameter('kernel.project_dir');
|
||||
$config = $directory . DIRECTORY_SEPARATOR . 'config/locales.php';
|
||||
$settings = include $config;
|
||||
$settings = include $directory . DIRECTORY_SEPARATOR . 'config/locales.php';
|
||||
|
||||
$appLocales = [];
|
||||
$defaults = [
|
||||
|
||||
@@ -13,13 +13,27 @@ use App\Model\ActivityBudgetStatisticModel;
|
||||
|
||||
final class ActivityBudgetStatisticEvent
|
||||
{
|
||||
private readonly ?\DateTime $begin;
|
||||
private readonly ?\DateTime $end;
|
||||
|
||||
/**
|
||||
* @param ActivityBudgetStatisticModel[] $models
|
||||
* @param \DateTime|null $begin
|
||||
* @param \DateTime|null $end
|
||||
*/
|
||||
public function __construct(private array $models, private ?\DateTime $begin = null, private ?\DateTime $end = null)
|
||||
public function __construct(
|
||||
private readonly array $models,
|
||||
?\DateTimeInterface $begin = null,
|
||||
?\DateTimeInterface $end = null
|
||||
)
|
||||
{
|
||||
if ($begin !== null) {
|
||||
$begin = \DateTime::createFromInterface($begin);
|
||||
}
|
||||
$this->begin = $begin;
|
||||
|
||||
if ($end !== null) {
|
||||
$end = \DateTime::createFromInterface($end);
|
||||
}
|
||||
$this->end = $end;
|
||||
}
|
||||
|
||||
public function getModel(int $activityId): ?ActivityBudgetStatisticModel
|
||||
|
||||
@@ -11,12 +11,32 @@ namespace App\Event;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Model\ActivityStatistic;
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
|
||||
final class ActivityStatisticEvent extends AbstractActivityEvent
|
||||
{
|
||||
public function __construct(Activity $activity, private ActivityStatistic $statistic, private ?\DateTime $begin = null, private ?\DateTime $end = null)
|
||||
private readonly ?DateTime $begin;
|
||||
private readonly ?DateTime $end;
|
||||
|
||||
public function __construct(
|
||||
Activity $activity,
|
||||
private readonly ActivityStatistic $statistic,
|
||||
?DateTimeInterface $begin = null,
|
||||
?DateTimeInterface $end = null
|
||||
)
|
||||
{
|
||||
parent::__construct($activity);
|
||||
|
||||
if ($begin !== null) {
|
||||
$begin = DateTime::createFromInterface($begin);
|
||||
}
|
||||
$this->begin = $begin;
|
||||
|
||||
if ($end !== null) {
|
||||
$end = DateTime::createFromInterface($end);
|
||||
}
|
||||
$this->end = $end;
|
||||
}
|
||||
|
||||
public function getStatistic(): ActivityStatistic
|
||||
@@ -24,12 +44,12 @@ final class ActivityStatisticEvent extends AbstractActivityEvent
|
||||
return $this->statistic;
|
||||
}
|
||||
|
||||
public function getBegin(): ?\DateTime
|
||||
public function getBegin(): ?DateTime
|
||||
{
|
||||
return $this->begin;
|
||||
}
|
||||
|
||||
public function getEnd(): ?\DateTime
|
||||
public function getEnd(): ?DateTime
|
||||
{
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
@@ -10,16 +10,32 @@
|
||||
namespace App\Event;
|
||||
|
||||
use App\Model\ProjectBudgetStatisticModel;
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
|
||||
final class ProjectBudgetStatisticEvent
|
||||
{
|
||||
private readonly ?DateTime $begin;
|
||||
private readonly ?DateTime $end;
|
||||
|
||||
/**
|
||||
* @param ProjectBudgetStatisticModel[] $models
|
||||
* @param \DateTime|null $begin
|
||||
* @param \DateTime|null $end
|
||||
*/
|
||||
public function __construct(private array $models, private ?\DateTime $begin = null, private ?\DateTime $end = null)
|
||||
public function __construct(
|
||||
private readonly array $models,
|
||||
?DateTimeInterface $begin = null,
|
||||
?DateTimeInterface $end = null
|
||||
)
|
||||
{
|
||||
if ($begin !== null) {
|
||||
$begin = \DateTime::createFromInterface($begin);
|
||||
}
|
||||
$this->begin = $begin;
|
||||
|
||||
if ($end !== null) {
|
||||
$end = \DateTime::createFromInterface($end);
|
||||
}
|
||||
$this->end = $end;
|
||||
}
|
||||
|
||||
public function getModel(int $projectId): ?ProjectBudgetStatisticModel
|
||||
@@ -45,12 +61,12 @@ final class ProjectBudgetStatisticEvent
|
||||
return $this->models;
|
||||
}
|
||||
|
||||
public function getBegin(): ?\DateTime
|
||||
public function getBegin(): ?DateTime
|
||||
{
|
||||
return $this->begin;
|
||||
}
|
||||
|
||||
public function getEnd(): ?\DateTime
|
||||
public function getEnd(): ?DateTime
|
||||
{
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,12 @@ use App\Model\ProjectStatistic;
|
||||
|
||||
final class ProjectStatisticEvent extends AbstractProjectEvent
|
||||
{
|
||||
public function __construct(Project $project, private ProjectStatistic $statistic, private ?\DateTime $begin = null, private ?\DateTime $end = null)
|
||||
public function __construct(
|
||||
Project $project,
|
||||
private readonly ProjectStatistic $statistic,
|
||||
private readonly ?\DateTimeInterface $begin = null,
|
||||
private readonly ?\DateTimeInterface $end = null
|
||||
)
|
||||
{
|
||||
parent::__construct($project);
|
||||
}
|
||||
@@ -24,12 +29,12 @@ final class ProjectStatisticEvent extends AbstractProjectEvent
|
||||
return $this->statistic;
|
||||
}
|
||||
|
||||
public function getBegin(): ?\DateTime
|
||||
public function getBegin(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->begin;
|
||||
}
|
||||
|
||||
public function getEnd(): ?\DateTime
|
||||
public function getEnd(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ abstract class AbstractSpreadsheetRenderer
|
||||
|
||||
$sheet->setCellValue(CellAddress::fromColumnAndRow($column, $row), $excelDate);
|
||||
// TODO why is that format hardcoded and does not depend on the users locale?
|
||||
$sheet->getStyle(CellAddress::fromColumnAndRow($column, $row))->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDD2);
|
||||
$sheet->getStyle(CellAddress::fromColumnAndRow($column, $row))->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDD);
|
||||
}
|
||||
|
||||
protected function setDurationTotal(Worksheet $sheet, int $column, int $row, string $startCoordinate, string $endCoordinate): void
|
||||
|
||||
@@ -24,11 +24,11 @@ final class DateFormatter implements CellFormatterInterface
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$value instanceof \DateTime) {
|
||||
throw new \InvalidArgumentException('Unsupported value given, only DateTime is supported');
|
||||
if (!$value instanceof \DateTimeInterface) {
|
||||
throw new \InvalidArgumentException('Unsupported value given, only DateTimeInterface is supported');
|
||||
}
|
||||
|
||||
$sheet->setCellValue(CellAddress::fromColumnAndRow($column, $row), Date::PHPToExcel($value));
|
||||
$sheet->getStyleByColumnAndRow($column, $row)->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDD2);
|
||||
$sheet->getStyle(CellAddress::fromColumnAndRow($column, $row))->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDD);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ final class DateTimeFormatter implements CellFormatterInterface
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$value instanceof \DateTime) {
|
||||
throw new \InvalidArgumentException('Unsupported value given, only DateTime is supported');
|
||||
if (!$value instanceof \DateTimeInterface) {
|
||||
throw new \InvalidArgumentException('Unsupported value given, only DateTimeInterface is supported');
|
||||
}
|
||||
|
||||
$sheet->setCellValue(CellAddress::fromColumnAndRow($column, $row), Date::PHPToExcel($value));
|
||||
$sheet->getStyleByColumnAndRow($column, $row)->getNumberFormat()->setFormatCode(self::DATETIME_FORMAT);
|
||||
$sheet->getStyle(CellAddress::fromColumnAndRow($column, $row))->getNumberFormat()->setFormatCode(self::DATETIME_FORMAT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ final class TimeFormatter implements CellFormatterInterface
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$value instanceof \DateTime) {
|
||||
throw new \InvalidArgumentException('Unsupported value given, only DateTime is supported');
|
||||
if (!$value instanceof \DateTimeInterface) {
|
||||
throw new \InvalidArgumentException('Unsupported value given, only DateTimeInterface is supported');
|
||||
}
|
||||
|
||||
$sheet->setCellValue(CellAddress::fromColumnAndRow($column, $row), Date::PHPToExcel($value));
|
||||
|
||||
@@ -26,9 +26,9 @@ final class DateRange implements EquatableInterface
|
||||
return $this->begin;
|
||||
}
|
||||
|
||||
public function setBegin(DateTime $begin): DateRange
|
||||
public function setBegin(\DateTimeInterface $begin): DateRange
|
||||
{
|
||||
$this->begin = $begin;
|
||||
$this->begin = DateTime::createFromInterface($begin);
|
||||
if ($this->resetTimes) {
|
||||
$this->begin->setTime(0, 0, 0);
|
||||
}
|
||||
@@ -41,9 +41,9 @@ final class DateRange implements EquatableInterface
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
public function setEnd(DateTime $end): DateRange
|
||||
public function setEnd(\DateTimeInterface $end): DateRange
|
||||
{
|
||||
$this->end = $end;
|
||||
$this->end = DateTime::createFromInterface($end);
|
||||
if ($this->resetTimes) {
|
||||
$this->end->setTime(23, 59, 59);
|
||||
}
|
||||
|
||||
@@ -39,10 +39,12 @@ class DatePickerType extends AbstractType
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($reverseTransform instanceof \DateTime && $options['force_time']) {
|
||||
if ($reverseTransform instanceof \DateTimeInterface && $options['force_time']) {
|
||||
if ($options['force_time'] === 'start') {
|
||||
$reverseTransform = \DateTime::createFromInterface($reverseTransform);
|
||||
$reverseTransform->setTime(0, 0, 0);
|
||||
} elseif ($options['force_time'] === 'end') {
|
||||
$reverseTransform = \DateTime::createFromInterface($reverseTransform);
|
||||
$reverseTransform->setTime(23, 59, 59);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ final class InvoiceModelCustomerHydrator implements InvoiceModelHydrator
|
||||
'customer.invoice_text' => $customer->getInvoiceText() ?? '',
|
||||
];
|
||||
|
||||
$statistic = $this->customerStatisticService->getBudgetStatisticModel($customer, $model->getQuery()->getEnd());
|
||||
/** @var \DateTime $end */
|
||||
$end = $model->getQuery()->getEnd();
|
||||
$statistic = $this->customerStatisticService->getBudgetStatisticModel($customer, $end);
|
||||
|
||||
$values = array_merge($values, $this->getBudgetValues('customer.', $statistic, $model));
|
||||
|
||||
|
||||
@@ -57,7 +57,12 @@ final class DocxRenderer extends AbstractRenderer implements RendererInterface
|
||||
$i++;
|
||||
}
|
||||
|
||||
$cacheFile = $template->save();
|
||||
$cacheFile = @tempnam(sys_get_temp_dir(), 'kimai-invoice-docx');
|
||||
if (false === $cacheFile) {
|
||||
throw new \Exception('Could not open temporary file');
|
||||
}
|
||||
|
||||
$template->saveAs($cacheFile);
|
||||
|
||||
clearstatcache(true, $cacheFile);
|
||||
|
||||
|
||||
@@ -47,6 +47,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->counter = $counter;
|
||||
}
|
||||
|
||||
public function addCounter(int $counter): void
|
||||
{
|
||||
$this->counter += $counter;
|
||||
}
|
||||
|
||||
public function getCounterBillable(): int
|
||||
{
|
||||
return $this->counterBillable;
|
||||
@@ -57,6 +62,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->counterBillable = $counter;
|
||||
}
|
||||
|
||||
public function addCounterBillable(int $counter): void
|
||||
{
|
||||
$this->counterBillable += $counter;
|
||||
}
|
||||
|
||||
public function getCounterExported(): int
|
||||
{
|
||||
return $this->counterExported;
|
||||
@@ -67,6 +77,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->counterExported = $counter;
|
||||
}
|
||||
|
||||
public function addCounterExported(int $counter): void
|
||||
{
|
||||
$this->counterExported += $counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* For unified access, used in frontend.
|
||||
*
|
||||
@@ -82,6 +97,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->recordDuration = $duration;
|
||||
}
|
||||
|
||||
public function addDuration(int $duration): void
|
||||
{
|
||||
$this->recordDuration += $duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* For unified access, used in frontend.
|
||||
*
|
||||
@@ -107,6 +127,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->recordRate = $rate;
|
||||
}
|
||||
|
||||
public function addRate(float $rate): void
|
||||
{
|
||||
$this->recordRate += $rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total internal rate of all included timesheet records.
|
||||
*
|
||||
@@ -127,6 +152,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->internalRateBillable = $internalRateBillable;
|
||||
}
|
||||
|
||||
public function addInternalRateBillable(float $internalRateBillable): void
|
||||
{
|
||||
$this->internalRateBillable += $internalRateBillable;
|
||||
}
|
||||
|
||||
public function getInternalRateExported(): float
|
||||
{
|
||||
return $this->internalRateExported;
|
||||
@@ -137,11 +167,21 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->internalRateExported = $internalRateExported;
|
||||
}
|
||||
|
||||
public function addInternalRateExported(float $internalRateExported): void
|
||||
{
|
||||
$this->internalRateExported += $internalRateExported;
|
||||
}
|
||||
|
||||
public function setInternalRate(float $internalRate): void
|
||||
{
|
||||
$this->internalRate = $internalRate;
|
||||
}
|
||||
|
||||
public function addInternalRate(float $internalRate): void
|
||||
{
|
||||
$this->internalRate += $internalRate;
|
||||
}
|
||||
|
||||
public function getDurationBillable(): int
|
||||
{
|
||||
return $this->recordDurationBillable;
|
||||
@@ -152,6 +192,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->recordDurationBillable = $recordDuration;
|
||||
}
|
||||
|
||||
public function addDurationBillable(int $recordDuration): void
|
||||
{
|
||||
$this->recordDurationBillable += $recordDuration;
|
||||
}
|
||||
|
||||
public function getDurationBillableExported(): int
|
||||
{
|
||||
return $this->recordDurationBillableExported;
|
||||
@@ -162,6 +207,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->recordDurationBillableExported = $recordDuration;
|
||||
}
|
||||
|
||||
public function addDurationBillableExported(int $recordDuration): void
|
||||
{
|
||||
$this->recordDurationBillableExported += $recordDuration;
|
||||
}
|
||||
|
||||
public function getRateBillable(): float
|
||||
{
|
||||
return $this->recordRateBillable;
|
||||
@@ -172,6 +222,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->recordRateBillable = $recordRate;
|
||||
}
|
||||
|
||||
public function addRateBillable(float $recordRate): void
|
||||
{
|
||||
$this->recordRateBillable += $recordRate;
|
||||
}
|
||||
|
||||
public function getRateBillableExported(): float
|
||||
{
|
||||
return $this->recordRateBillableExported;
|
||||
@@ -182,6 +237,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->recordRateBillableExported = $recordRate;
|
||||
}
|
||||
|
||||
public function addRateBillableExported(float $recordRate): void
|
||||
{
|
||||
$this->recordRateBillableExported += $recordRate;
|
||||
}
|
||||
|
||||
public function getDurationExported(): int
|
||||
{
|
||||
return $this->recordDurationExported;
|
||||
@@ -192,6 +252,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->recordDurationExported = $recordDuration;
|
||||
}
|
||||
|
||||
public function addDurationExported(int $recordDuration): void
|
||||
{
|
||||
$this->recordDurationExported += $recordDuration;
|
||||
}
|
||||
|
||||
public function getRateExported(): float
|
||||
{
|
||||
return $this->recordRateExported;
|
||||
@@ -202,6 +267,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->recordRateExported = $recordRate;
|
||||
}
|
||||
|
||||
public function addRateExported(float $recordRate): void
|
||||
{
|
||||
$this->recordRateExported += $recordRate;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -34,7 +34,8 @@ use App\Repository\ProjectRepository;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Repository\UserRepository;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
@@ -54,14 +55,9 @@ class ProjectStatisticService
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: this method does not respect the budget type. Your results will always be wither the "full lifetime data" or the "selected date-range".
|
||||
*
|
||||
* @param Project $project
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @return ProjectStatistic
|
||||
* WARNING: this method does not respect the budget type. Your results will always be with the "full lifetime data" or the "selected date-range".
|
||||
*/
|
||||
public function getProjectStatistics(Project $project, ?DateTime $begin = null, ?DateTime $end = null): ProjectStatistic
|
||||
public function getProjectStatistics(Project $project, ?DateTimeInterface $begin = null, ?DateTimeInterface $end = null): ProjectStatistic
|
||||
{
|
||||
$statistics = $this->getBudgetStatistic([$project], $begin, $end);
|
||||
$event = new ProjectStatisticEvent($project, array_pop($statistics), $begin, $end);
|
||||
@@ -71,14 +67,13 @@ class ProjectStatisticService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ProjectInactiveQuery $query
|
||||
* @return Project[]
|
||||
*/
|
||||
public function findInactiveProjects(ProjectInactiveQuery $query): array
|
||||
{
|
||||
$user = $query->getUser();
|
||||
$lastChange = clone $query->getLastChange();
|
||||
$now = new DateTime('now', $lastChange->getTimezone());
|
||||
$lastChange = DateTimeImmutable::createFromInterface($query->getLastChange());
|
||||
$now = new DateTimeImmutable('now', $lastChange->getTimezone());
|
||||
|
||||
$qb2 = $this->projectRepository->createQueryBuilder('t1');
|
||||
$qb2
|
||||
@@ -101,15 +96,15 @@ class ProjectStatisticService
|
||||
$qb->expr()->lte('p.start', ':project_start')
|
||||
)
|
||||
)
|
||||
->setParameter('project_start', $now, Types::DATETIME_MUTABLE)
|
||||
->setParameter('project_start', $now, Types::DATETIME_IMMUTABLE)
|
||||
->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('p.end'),
|
||||
$qb->expr()->gte('p.end', ':project_end')
|
||||
)
|
||||
)
|
||||
->setParameter('project_end', $now, Types::DATETIME_MUTABLE)
|
||||
->setParameter('begin', $lastChange, Types::DATETIME_MUTABLE)
|
||||
->setParameter('project_end', $now, Types::DATETIME_IMMUTABLE)
|
||||
->setParameter('begin', $lastChange, Types::DATETIME_IMMUTABLE)
|
||||
;
|
||||
|
||||
$this->projectRepository->addPermissionCriteria($qb, $user);
|
||||
@@ -125,7 +120,6 @@ class ProjectStatisticService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ProjectDateRangeQuery $query
|
||||
* @return Project[]
|
||||
*/
|
||||
public function findProjectsForDateRange(ProjectDateRangeQuery $query, DateRange $dateRange): array
|
||||
@@ -208,7 +202,7 @@ class ProjectStatisticService
|
||||
return $projects;
|
||||
}
|
||||
|
||||
public function getBudgetStatisticModel(Project $project, DateTime $today): ProjectBudgetStatisticModel
|
||||
public function getBudgetStatisticModel(Project $project, DateTimeInterface $today): ProjectBudgetStatisticModel
|
||||
{
|
||||
$stats = new ProjectBudgetStatisticModel($project);
|
||||
$stats->setStatisticTotal($this->getProjectStatistics($project));
|
||||
@@ -229,10 +223,9 @@ class ProjectStatisticService
|
||||
|
||||
/**
|
||||
* @param Project[] $projects
|
||||
* @param DateTime $today
|
||||
* @return ProjectBudgetStatisticModel[]
|
||||
*/
|
||||
public function getBudgetStatisticModelForProjects(array $projects, DateTime $today): array
|
||||
public function getBudgetStatisticModelForProjects(array $projects, DateTimeInterface $today): array
|
||||
{
|
||||
$models = [];
|
||||
$monthly = [];
|
||||
@@ -282,12 +275,9 @@ class ProjectStatisticService
|
||||
|
||||
/**
|
||||
* @param Project[] $projects
|
||||
* @param DateTime $begin
|
||||
* @param DateTime $end
|
||||
* @param DateTime|null $totalsEnd
|
||||
* @return ProjectBudgetStatisticModel[]
|
||||
*/
|
||||
public function getBudgetStatisticModelForProjectsByDateRange(array $projects, DateTime $begin, DateTime $end, ?DateTime $totalsEnd = null): array
|
||||
public function getBudgetStatisticModelForProjectsByDateRange(array $projects, DateTimeInterface $begin, DateTimeInterface $end, ?DateTimeInterface $totalsEnd = null): array
|
||||
{
|
||||
$models = [];
|
||||
|
||||
@@ -313,11 +303,9 @@ class ProjectStatisticService
|
||||
|
||||
/**
|
||||
* @param Project[] $projects
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @return array<int, ProjectStatistic>
|
||||
*/
|
||||
public function getBudgetStatistic(array $projects, ?DateTime $begin = null, ?DateTime $end = null): array
|
||||
public function getBudgetStatistic(array $projects, ?DateTimeInterface $begin = null, ?DateTimeInterface $end = null): array
|
||||
{
|
||||
$statistics = [];
|
||||
foreach ($projects as $project) {
|
||||
@@ -711,15 +699,13 @@ class ProjectStatisticService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param Project[] $projects
|
||||
* @param DateTime $today
|
||||
* @return ProjectViewModel[]
|
||||
*/
|
||||
public function getProjectView(User $user, array $projects, DateTime $today): array
|
||||
public function getProjectView(User $user, array $projects, DateTimeInterface $today): array
|
||||
{
|
||||
$factory = DateTimeFactory::createByUser($user);
|
||||
$today = clone $today;
|
||||
$today = DateTimeImmutable::createFromInterface($today);
|
||||
|
||||
$startOfWeek = $factory->getStartOfWeek($today);
|
||||
$endOfWeek = $factory->getEndOfWeek($today);
|
||||
|
||||
30
src/Repository/Query/DateRangeInterface.php
Normal file
30
src/Repository/Query/DateRangeInterface.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Repository\Query;
|
||||
|
||||
use App\Form\Model\DateRange;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface DateRangeInterface
|
||||
{
|
||||
public function getBegin(): ?\DateTime;
|
||||
|
||||
public function setBegin(\DateTimeInterface $begin): void;
|
||||
|
||||
public function getEnd(): ?\DateTime;
|
||||
|
||||
public function setEnd(\DateTimeInterface $end): void;
|
||||
|
||||
public function getDateRange(): ?DateRange;
|
||||
|
||||
public function setDateRange(DateRange $dateRange): void;
|
||||
}
|
||||
@@ -20,9 +20,9 @@ trait DateRangeTrait
|
||||
return $this->dateRange?->getBegin();
|
||||
}
|
||||
|
||||
public function setBegin(\DateTime $begin): void
|
||||
public function setBegin(\DateTimeInterface $begin): void
|
||||
{
|
||||
$this->dateRange->setBegin($begin);
|
||||
$this->dateRange->setBegin(\DateTime::createFromInterface($begin));
|
||||
}
|
||||
|
||||
public function getEnd(): ?\DateTime
|
||||
@@ -30,9 +30,9 @@ trait DateRangeTrait
|
||||
return $this->dateRange?->getEnd();
|
||||
}
|
||||
|
||||
public function setEnd(\DateTime $end): void
|
||||
public function setEnd(\DateTimeInterface $end): void
|
||||
{
|
||||
$this->dateRange->setEnd($end);
|
||||
$this->dateRange->setEnd(\DateTime::createFromInterface($end));
|
||||
}
|
||||
|
||||
public function getDateRange(): ?DateRange
|
||||
|
||||
@@ -16,7 +16,7 @@ use App\Form\Model\DateRange;
|
||||
/**
|
||||
* Query for created invoices.
|
||||
*/
|
||||
class InvoiceArchiveQuery extends BaseQuery
|
||||
class InvoiceArchiveQuery extends BaseQuery implements DateRangeInterface
|
||||
{
|
||||
use DateRangeTrait;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ use App\Entity\Tag;
|
||||
use App\Entity\User;
|
||||
use App\Form\Model\DateRange;
|
||||
|
||||
class TimesheetQuery extends ActivityQuery implements BillableInterface
|
||||
class TimesheetQuery extends ActivityQuery implements BillableInterface, DateRangeInterface
|
||||
{
|
||||
use BillableTrait;
|
||||
use DateRangeTrait;
|
||||
|
||||
@@ -62,10 +62,10 @@ final class LocaleFormatExtensions extends AbstractExtension implements LocaleAw
|
||||
return [
|
||||
new TwigTest('weekend', [$this, 'isWeekend']),
|
||||
new TwigTest('today', function ($dateTime): bool {
|
||||
if (!$dateTime instanceof \DateTime) {
|
||||
if (!$dateTime instanceof \DateTimeInterface) {
|
||||
return false;
|
||||
}
|
||||
$compare = new \DateTime('now', $dateTime->getTimezone());
|
||||
$compare = new \DateTimeImmutable('now', $dateTime->getTimezone());
|
||||
|
||||
return $compare->format('Y-m-d') === $dateTime->format('Y-m-d');
|
||||
}),
|
||||
|
||||
@@ -58,8 +58,10 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
|
||||
return;
|
||||
}
|
||||
|
||||
$begin = $timesheet->getBegin();
|
||||
|
||||
// we can only work with stopped entries
|
||||
if (null === $timesheet->getEnd() || null === $timesheet->getUser()) {
|
||||
if ($begin === null || $timesheet->getEnd() === null || $timesheet->getUser() === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,7 +117,7 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
|
||||
if ($duration === $rawData['duration'] &&
|
||||
$rate === $rawData['rate'] &&
|
||||
$timesheet->isBillable() === $rawData['billable'] &&
|
||||
$timesheet->getBegin()->format('Y.m.d') === $rawData['begin']->format('Y.m.d') &&
|
||||
$begin->format('Y.m.d') === $rawData['begin']->format('Y.m.d') &&
|
||||
$timesheet->getProject()->getId() === $projectId &&
|
||||
($timesheet->getActivity() === null || $timesheet->getActivity()->getId() === $activityId)
|
||||
) {
|
||||
@@ -145,11 +147,11 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
|
||||
}
|
||||
}
|
||||
|
||||
$monthWasChanged = $timesheet->getBegin()->format('Y.m') !== $rawData['begin']->format('Y.m');
|
||||
$monthWasChanged = $begin->format('Y.m') !== $rawData['begin']->format('Y.m');
|
||||
}
|
||||
|
||||
$now = new DateTime('now', $timesheet->getBegin()->getTimezone());
|
||||
$recordDate = $timesheet->getBegin();
|
||||
$now = new DateTime('now', $begin->getTimezone());
|
||||
$recordDate = $begin;
|
||||
|
||||
if (null !== ($activity = $timesheet->getActivity()) && $activity->hasBudgets()) {
|
||||
$dateTime = $activity->isMonthlyBudget() ? $recordDate : $now;
|
||||
|
||||
@@ -52,7 +52,7 @@ final class TimesheetLockdownValidator extends ConstraintValidator
|
||||
$now = new \DateTime('now', $timesheetStart->getTimezone());
|
||||
|
||||
if (!empty($constraint->now)) {
|
||||
if ($constraint->now instanceof \DateTime) {
|
||||
if ($constraint->now instanceof \DateTimeInterface) {
|
||||
$now = $constraint->now;
|
||||
} elseif (\is_string($constraint->now)) {
|
||||
try {
|
||||
|
||||
20
symfony.lock
20
symfony.lock
@@ -443,13 +443,23 @@
|
||||
"version": "v4.0.3"
|
||||
},
|
||||
"symfony/framework-bundle": {
|
||||
"version": "3.3",
|
||||
"version": "6.4",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "3.3",
|
||||
"ref": "137a14eeb6b3f5370e7147af8aff6518504f50c7"
|
||||
}
|
||||
"branch": "main",
|
||||
"version": "6.4",
|
||||
"ref": "a91c965766ad3ff2ae15981801643330eb42b6a5"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/cache.yaml",
|
||||
"config/packages/framework.yaml",
|
||||
"config/preload.php",
|
||||
"config/routes/framework.yaml",
|
||||
"config/services.yaml",
|
||||
"public/index.php",
|
||||
"src/Controller/.gitignore",
|
||||
"src/Kernel.php"
|
||||
]
|
||||
},
|
||||
"symfony/http-client": {
|
||||
"version": "v4.4.5"
|
||||
|
||||
@@ -19,7 +19,7 @@ use PHPUnit\Framework\TestCase;
|
||||
*/
|
||||
class ActivityBudgetStatisticEventTest extends TestCase
|
||||
{
|
||||
public function testStatistic()
|
||||
public function testStatistic(): void
|
||||
{
|
||||
$activity = $this->createMock(Activity::class);
|
||||
$activity->expects($this->exactly(2))->method('getId')->willReturn(12);
|
||||
@@ -39,7 +39,7 @@ class ActivityBudgetStatisticEventTest extends TestCase
|
||||
self::assertNull($sut->getModel(1));
|
||||
self::assertSame($model1, $sut->getModel(12));
|
||||
self::assertSame($model2, $sut->getModel(4));
|
||||
self::assertSame($begin, $sut->getBegin());
|
||||
self::assertSame($end, $sut->getEnd());
|
||||
self::assertEquals($begin, $sut->getBegin());
|
||||
self::assertEquals($end, $sut->getEnd());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class ActivityStatisticEventTest extends AbstractActivityEventTest
|
||||
return new ActivityStatisticEvent($activity, new ActivityStatistic());
|
||||
}
|
||||
|
||||
public function testStatistic()
|
||||
public function testStatistic(): void
|
||||
{
|
||||
$activity = new Activity();
|
||||
$statistic = new ActivityStatistic();
|
||||
@@ -39,7 +39,7 @@ class ActivityStatisticEventTest extends AbstractActivityEventTest
|
||||
$begin = new \DateTime('2020-08-08 12:34:56');
|
||||
$end = new \DateTime('2020-09-08 12:34:56');
|
||||
$sut = new ActivityStatisticEvent($activity, $statistic, $begin, $end);
|
||||
self::assertSame($begin, $sut->getBegin());
|
||||
self::assertSame($end, $sut->getEnd());
|
||||
self::assertEquals($begin, $sut->getBegin());
|
||||
self::assertEquals($end, $sut->getEnd());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ use PHPUnit\Framework\TestCase;
|
||||
*/
|
||||
class ProjectBudgetStatisticEventTest extends TestCase
|
||||
{
|
||||
public function testStatistic()
|
||||
public function testStatistic(): void
|
||||
{
|
||||
$project = $this->createMock(Project::class);
|
||||
$project->expects($this->exactly(2))->method('getId')->willReturn(12);
|
||||
@@ -39,7 +39,7 @@ class ProjectBudgetStatisticEventTest extends TestCase
|
||||
self::assertNull($sut->getModel(1));
|
||||
self::assertSame($model1, $sut->getModel(12));
|
||||
self::assertSame($model2, $sut->getModel(4));
|
||||
self::assertSame($begin, $sut->getBegin());
|
||||
self::assertSame($end, $sut->getEnd());
|
||||
self::assertEquals($begin, $sut->getBegin());
|
||||
self::assertEquals($end, $sut->getEnd());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class ProjectStatisticEventTest extends AbstractProjectEventTest
|
||||
return new ProjectStatisticEvent($project, new ProjectStatistic());
|
||||
}
|
||||
|
||||
public function testStatistic()
|
||||
public function testStatistic(): void
|
||||
{
|
||||
$project = new Project();
|
||||
$statistic = new ProjectStatistic();
|
||||
@@ -36,8 +36,8 @@ class ProjectStatisticEventTest extends AbstractProjectEventTest
|
||||
self::assertNull($sut->getBegin());
|
||||
self::assertNull($sut->getEnd());
|
||||
|
||||
$begin = new \DateTime('2020-08-08 12:34:56');
|
||||
$end = new \DateTime('2020-09-08 12:34:56');
|
||||
$begin = new \DateTimeImmutable('2020-08-08 12:34:56');
|
||||
$end = new \DateTimeImmutable('2020-09-08 12:34:56');
|
||||
$sut = new ProjectStatisticEvent($project, $statistic, $begin, $end);
|
||||
self::assertSame($begin, $sut->getBegin());
|
||||
self::assertSame($end, $sut->getEnd());
|
||||
|
||||
@@ -41,7 +41,7 @@ class DateFormatterTest extends AbstractFormatterTest
|
||||
public function testFormattedValueWithInvalidValue()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Unsupported value given, only DateTime is supported');
|
||||
$this->expectExceptionMessage('Unsupported value given, only DateTimeInterface is supported');
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
@@ -40,7 +40,7 @@ class DateTimeFormatterTest extends AbstractFormatterTest
|
||||
public function testFormattedValueWithInvalidValue()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Unsupported value given, only DateTime is supported');
|
||||
$this->expectExceptionMessage('Unsupported value given, only DateTimeInterface is supported');
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
@@ -40,7 +40,7 @@ class TimeFormatterTest extends AbstractFormatterTest
|
||||
public function testFormattedValueWithInvalidValue()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Unsupported value given, only DateTime is supported');
|
||||
$this->expectExceptionMessage('Unsupported value given, only DateTimeInterface is supported');
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
@@ -22,7 +22,7 @@ class DocxRendererTest extends TestCase
|
||||
{
|
||||
use RendererTestTrait;
|
||||
|
||||
public function testSupports()
|
||||
public function testSupports(): void
|
||||
{
|
||||
$sut = $this->getAbstractRenderer(DocxRenderer::class);
|
||||
|
||||
@@ -34,7 +34,7 @@ class DocxRendererTest extends TestCase
|
||||
$this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods', true)));
|
||||
}
|
||||
|
||||
public function testRender()
|
||||
public function testRender(): void
|
||||
{
|
||||
/** @var DocxRenderer $sut */
|
||||
$sut = $this->getAbstractRenderer(DocxRenderer::class);
|
||||
@@ -48,7 +48,7 @@ class DocxRendererTest extends TestCase
|
||||
$this->assertEquals('application/vnd.openxmlformats-officedocument.wordprocessingml.document', $response->headers->get('Content-Type'));
|
||||
$this->assertEquals('attachment; filename=' . $filename, $response->headers->get('Content-Disposition'));
|
||||
|
||||
$this->assertTrue(file_exists($file->getRealPath()));
|
||||
$this->assertTrue(file_exists($file->getPathname()));
|
||||
|
||||
ob_start();
|
||||
$response->sendContent();
|
||||
|
||||
@@ -17,6 +17,7 @@ use App\Entity\Team;
|
||||
use App\Form\Model\DateRange;
|
||||
use App\Repository\Query\ActivityQuery;
|
||||
use App\Repository\Query\BaseQuery;
|
||||
use App\Repository\Query\DateRangeInterface;
|
||||
use App\Repository\Query\ProjectQuery;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use App\Utils\SearchTerm;
|
||||
@@ -334,7 +335,7 @@ class BaseQueryTest extends TestCase
|
||||
$this->assertEquals([13, 27], $sut->getProjectIds());
|
||||
}
|
||||
|
||||
protected function assertDateRangeTrait($sut): void
|
||||
protected function assertDateRangeTrait(DateRangeInterface $sut): void
|
||||
{
|
||||
self::assertNull($sut->getBegin());
|
||||
self::assertNull($sut->getEnd());
|
||||
@@ -346,14 +347,15 @@ class BaseQueryTest extends TestCase
|
||||
self::assertNull($sut->getBegin());
|
||||
self::assertNull($sut->getEnd());
|
||||
|
||||
$begin = new \DateTime('2013-11-23 13:45:07');
|
||||
$end = new \DateTime('2014-01-01 23:45:11');
|
||||
$dateRange->setBegin($begin);
|
||||
$dateRange->setEnd($end);
|
||||
$dateRange->setBegin(new \DateTimeImmutable('2013-11-23 13:45:07'));
|
||||
$dateRange->setEnd(new \DateTimeImmutable('2014-01-01 23:45:11'));
|
||||
|
||||
self::assertSame($begin, $sut->getDateRange()->getBegin());
|
||||
self::assertSame($begin, $sut->getBegin());
|
||||
self::assertSame($end, $sut->getDateRange()->getEnd());
|
||||
self::assertSame($end, $sut->getEnd());
|
||||
$begin1 = new \DateTimeImmutable('2013-11-23 00:00:00');
|
||||
$end1 = new \DateTimeImmutable('2014-01-01 23:59:59');
|
||||
|
||||
self::assertEquals($begin1, $sut->getDateRange()->getBegin());
|
||||
self::assertEquals($begin1, $sut->getBegin());
|
||||
self::assertEquals($end1, $sut->getDateRange()->getEnd());
|
||||
self::assertEquals($end1, $sut->getEnd());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4072,11 +4072,6 @@ parameters:
|
||||
count: 1
|
||||
path: Event/AbstractTimesheetMultipleEventTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Event\\\\ActivityBudgetStatisticEventTest\\:\\:testStatistic\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: Event/ActivityBudgetStatisticEventTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Event\\\\ActivityMetaDefinitionEventTest\\:\\:testGetterAndSetter\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
@@ -4087,11 +4082,6 @@ parameters:
|
||||
count: 1
|
||||
path: Event/ActivityMetaDisplayEventTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Event\\\\ActivityStatisticEventTest\\:\\:testStatistic\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: Event/ActivityStatisticEventTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Event\\\\CalendarConfigurationEventTest\\:\\:testGetterAndSetter\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
@@ -4202,11 +4192,6 @@ parameters:
|
||||
count: 5
|
||||
path: Event/PermissionsEventTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Event\\\\ProjectBudgetStatisticEventTest\\:\\:testStatistic\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: Event/ProjectBudgetStatisticEventTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Event\\\\ProjectMetaDefinitionEventTest\\:\\:testGetterAndSetter\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
@@ -4222,11 +4207,6 @@ parameters:
|
||||
count: 1
|
||||
path: Event/ProjectMetaQueryDisplayTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Event\\\\ProjectStatisticEventTest\\:\\:testStatistic\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: Event/ProjectStatisticEventTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Event\\\\RecentActivityEventTest\\:\\:testGetterAndSetter\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
@@ -5912,16 +5892,6 @@ parameters:
|
||||
count: 1
|
||||
path: Invoice/Renderer/DocxRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\DocxRendererTest\\:\\:testRender\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: Invoice/Renderer/DocxRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\DocxRendererTest\\:\\:testSupports\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: Invoice/Renderer/DocxRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\OdsRendererTest\\:\\:getAbstractRenderer\\(\\) should return App\\\\Invoice\\\\Renderer\\\\AbstractRenderer but returns object\\.$#"
|
||||
count: 1
|
||||
@@ -6907,11 +6877,6 @@ parameters:
|
||||
count: 1
|
||||
path: Repository/Query/BaseQueryTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Repository\\\\Query\\\\BaseQueryTest\\:\\:assertDateRangeTrait\\(\\) has parameter \\$sut with no type specified\\.$#"
|
||||
count: 1
|
||||
path: Repository/Query/BaseQueryTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Repository\\\\Query\\\\BaseQueryTest\\:\\:assertOrder\\(\\) has parameter \\$order with no type specified\\.$#"
|
||||
count: 1
|
||||
|
||||
Reference in New Issue
Block a user