Release 2.14 (#4710)
- show "link has expired message" in password reset screen - added date objects as hydrator variables - for custom date formats in invoice templates - show meta-fields with null values (e.g. booleans with `false` where hidden) - fix permission check: allow to remove `view_own_timesheet` but still record times - prevent error 500 if customer country is empty - fix API 500 error if project does not exist when creating new timesheet - fix tags are not created in remote-search mode - do not check "export items" by default - fix daterange query, if user an request locale are different - added logging for invalid SAML responses (see various discussions)
This commit is contained in:
159
composer.lock
generated
159
composer.lock
generated
@@ -745,16 +745,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/doctrine-bundle",
|
"name": "doctrine/doctrine-bundle",
|
||||||
"version": "2.11.3",
|
"version": "2.12.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/DoctrineBundle.git",
|
"url": "https://github.com/doctrine/DoctrineBundle.git",
|
||||||
"reference": "492725310ae9a1b5b20d6ae09fb5ae6404616e68"
|
"reference": "5418e811a14724068e95e0ba43353b903ada530f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/492725310ae9a1b5b20d6ae09fb5ae6404616e68",
|
"url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/5418e811a14724068e95e0ba43353b903ada530f",
|
||||||
"reference": "492725310ae9a1b5b20d6ae09fb5ae6404616e68",
|
"reference": "5418e811a14724068e95e0ba43353b903ada530f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -792,6 +792,7 @@
|
|||||||
"symfony/property-info": "^5.4 || ^6.0 || ^7.0",
|
"symfony/property-info": "^5.4 || ^6.0 || ^7.0",
|
||||||
"symfony/proxy-manager-bridge": "^5.4 || ^6.0 || ^7.0",
|
"symfony/proxy-manager-bridge": "^5.4 || ^6.0 || ^7.0",
|
||||||
"symfony/security-bundle": "^5.4 || ^6.0 || ^7.0",
|
"symfony/security-bundle": "^5.4 || ^6.0 || ^7.0",
|
||||||
|
"symfony/stopwatch": "^5.4 || ^6.0 || ^7.0",
|
||||||
"symfony/string": "^5.4 || ^6.0 || ^7.0",
|
"symfony/string": "^5.4 || ^6.0 || ^7.0",
|
||||||
"symfony/twig-bridge": "^5.4 || ^6.0 || ^7.0",
|
"symfony/twig-bridge": "^5.4 || ^6.0 || ^7.0",
|
||||||
"symfony/validator": "^5.4 || ^6.0 || ^7.0",
|
"symfony/validator": "^5.4 || ^6.0 || ^7.0",
|
||||||
@@ -809,7 +810,7 @@
|
|||||||
"type": "symfony-bundle",
|
"type": "symfony-bundle",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Doctrine\\Bundle\\DoctrineBundle\\": ""
|
"Doctrine\\Bundle\\DoctrineBundle\\": "src"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
@@ -844,7 +845,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/DoctrineBundle/issues",
|
"issues": "https://github.com/doctrine/DoctrineBundle/issues",
|
||||||
"source": "https://github.com/doctrine/DoctrineBundle/tree/2.11.3"
|
"source": "https://github.com/doctrine/DoctrineBundle/tree/2.12.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -860,7 +861,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-02-10T20:56:20+00:00"
|
"time": "2024-03-19T07:20:37+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/doctrine-migrations-bundle",
|
"name": "doctrine/doctrine-migrations-bundle",
|
||||||
@@ -1386,16 +1387,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/orm",
|
"name": "doctrine/orm",
|
||||||
"version": "2.19.0",
|
"version": "2.19.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/orm.git",
|
"url": "https://github.com/doctrine/orm.git",
|
||||||
"reference": "a809a71aa6a233a6c82e68ebaaf8954adc4998dc"
|
"reference": "40a0964f06e142e173b783c45688e5f5efaed6aa"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/orm/zipball/a809a71aa6a233a6c82e68ebaaf8954adc4998dc",
|
"url": "https://api.github.com/repos/doctrine/orm/zipball/40a0964f06e142e173b783c45688e5f5efaed6aa",
|
||||||
"reference": "a809a71aa6a233a6c82e68ebaaf8954adc4998dc",
|
"reference": "40a0964f06e142e173b783c45688e5f5efaed6aa",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1481,22 +1482,22 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/orm/issues",
|
"issues": "https://github.com/doctrine/orm/issues",
|
||||||
"source": "https://github.com/doctrine/orm/tree/2.19.0"
|
"source": "https://github.com/doctrine/orm/tree/2.19.2"
|
||||||
},
|
},
|
||||||
"time": "2024-03-03T17:43:41+00:00"
|
"time": "2024-03-18T19:12:56+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/persistence",
|
"name": "doctrine/persistence",
|
||||||
"version": "3.3.1",
|
"version": "3.3.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/persistence.git",
|
"url": "https://github.com/doctrine/persistence.git",
|
||||||
"reference": "b6fd1f126b13c1f7e7321f7338b14a19116b5de4"
|
"reference": "477da35bd0255e032826f440b94b3e37f2d56f42"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/persistence/zipball/b6fd1f126b13c1f7e7321f7338b14a19116b5de4",
|
"url": "https://api.github.com/repos/doctrine/persistence/zipball/477da35bd0255e032826f440b94b3e37f2d56f42",
|
||||||
"reference": "b6fd1f126b13c1f7e7321f7338b14a19116b5de4",
|
"reference": "477da35bd0255e032826f440b94b3e37f2d56f42",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1565,7 +1566,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/persistence/issues",
|
"issues": "https://github.com/doctrine/persistence/issues",
|
||||||
"source": "https://github.com/doctrine/persistence/tree/3.3.1"
|
"source": "https://github.com/doctrine/persistence/tree/3.3.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1581,7 +1582,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-03-01T19:53:13+00:00"
|
"time": "2024-03-12T14:54:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/sql-formatter",
|
"name": "doctrine/sql-formatter",
|
||||||
@@ -2876,16 +2877,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mpdf/mpdf",
|
"name": "mpdf/mpdf",
|
||||||
"version": "v8.2.2",
|
"version": "v8.2.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mpdf/mpdf.git",
|
"url": "https://github.com/mpdf/mpdf.git",
|
||||||
"reference": "596a87b876d7793be7be060a8ac13424de120dd5"
|
"reference": "6f723a96becf989a831e38caf758d28364a69939"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/mpdf/mpdf/zipball/596a87b876d7793be7be060a8ac13424de120dd5",
|
"url": "https://api.github.com/repos/mpdf/mpdf/zipball/6f723a96becf989a831e38caf758d28364a69939",
|
||||||
"reference": "596a87b876d7793be7be060a8ac13424de120dd5",
|
"reference": "6f723a96becf989a831e38caf758d28364a69939",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -2953,7 +2954,7 @@
|
|||||||
"type": "custom"
|
"type": "custom"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-11-07T13:52:14+00:00"
|
"time": "2024-03-11T12:55:53+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mpdf/psr-http-message-shim",
|
"name": "mpdf/psr-http-message-shim",
|
||||||
@@ -3108,22 +3109,23 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nelmio/api-doc-bundle",
|
"name": "nelmio/api-doc-bundle",
|
||||||
"version": "v4.23.1",
|
"version": "v4.24.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nelmio/NelmioApiDocBundle.git",
|
"url": "https://github.com/nelmio/NelmioApiDocBundle.git",
|
||||||
"reference": "a15b5923602c669007ea53a1a87991e9e147daab"
|
"reference": "9fb5170b5f7a88d1aaade05e3eb399e697ac0941"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/a15b5923602c669007ea53a1a87991e9e147daab",
|
"url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/9fb5170b5f7a88d1aaade05e3eb399e697ac0941",
|
||||||
"reference": "a15b5923602c669007ea53a1a87991e9e147daab",
|
"reference": "9fb5170b5f7a88d1aaade05e3eb399e697ac0941",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"php": ">=7.2",
|
"php": ">=7.4",
|
||||||
"phpdocumentor/reflection-docblock": "^3.1|^4.0|^5.0",
|
"phpdocumentor/reflection-docblock": "^4.3.4|^5.0",
|
||||||
|
"phpdocumentor/type-resolver": "^1.8.2",
|
||||||
"psr/cache": "^1.0|^2.0|^3.0",
|
"psr/cache": "^1.0|^2.0|^3.0",
|
||||||
"psr/container": "^1.0|^2.0",
|
"psr/container": "^1.0|^2.0",
|
||||||
"psr/log": "^1.0|^2.0|^3.0",
|
"psr/log": "^1.0|^2.0|^3.0",
|
||||||
@@ -3135,9 +3137,9 @@
|
|||||||
"symfony/http-foundation": "^5.4|^6.0|^7.0",
|
"symfony/http-foundation": "^5.4|^6.0|^7.0",
|
||||||
"symfony/http-kernel": "^5.4|^6.0|^7.0",
|
"symfony/http-kernel": "^5.4|^6.0|^7.0",
|
||||||
"symfony/options-resolver": "^5.4|^6.0|^7.0",
|
"symfony/options-resolver": "^5.4|^6.0|^7.0",
|
||||||
"symfony/property-info": "^5.4|^6.0|^7.0",
|
"symfony/property-info": "^5.4.10|^6.0|^7.0",
|
||||||
"symfony/routing": "^5.4|^6.0|^7.0",
|
"symfony/routing": "^5.4|^6.0|^7.0",
|
||||||
"zircote/swagger-php": "^4.2.15"
|
"zircote/swagger-php": "^4.6.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"api-platform/core": "^2.7.0|^3",
|
"api-platform/core": "^2.7.0|^3",
|
||||||
@@ -3209,9 +3211,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/nelmio/NelmioApiDocBundle/issues",
|
"issues": "https://github.com/nelmio/NelmioApiDocBundle/issues",
|
||||||
"source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v4.23.1"
|
"source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v4.24.0"
|
||||||
},
|
},
|
||||||
"time": "2024-03-07T09:21:17+00:00"
|
"time": "2024-03-14T15:14:05+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nelmio/cors-bundle",
|
"name": "nelmio/cors-bundle",
|
||||||
@@ -10237,16 +10239,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "zircote/swagger-php",
|
"name": "zircote/swagger-php",
|
||||||
"version": "4.8.4",
|
"version": "4.8.6",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/zircote/swagger-php.git",
|
"url": "https://github.com/zircote/swagger-php.git",
|
||||||
"reference": "bdee7f5a9216ce103ba2c953c1c43c4a3e139e4c"
|
"reference": "fe45a0e98a156999ddafc31c9ad333fdcadbcb2d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/bdee7f5a9216ce103ba2c953c1c43c4a3e139e4c",
|
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/fe45a0e98a156999ddafc31c9ad333fdcadbcb2d",
|
||||||
"reference": "bdee7f5a9216ce103ba2c953c1c43c4a3e139e4c",
|
"reference": "fe45a0e98a156999ddafc31c9ad333fdcadbcb2d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -10312,24 +10314,24 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/zircote/swagger-php/issues",
|
"issues": "https://github.com/zircote/swagger-php/issues",
|
||||||
"source": "https://github.com/zircote/swagger-php/tree/4.8.4"
|
"source": "https://github.com/zircote/swagger-php/tree/4.8.6"
|
||||||
},
|
},
|
||||||
"time": "2024-02-04T21:16:47+00:00"
|
"time": "2024-03-15T01:13:55+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
{
|
{
|
||||||
"name": "composer/pcre",
|
"name": "composer/pcre",
|
||||||
"version": "3.1.2",
|
"version": "3.1.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/composer/pcre.git",
|
"url": "https://github.com/composer/pcre.git",
|
||||||
"reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace"
|
"reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/composer/pcre/zipball/4775f35b2d70865807c89d32c8e7385b86eb0ace",
|
"url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
|
||||||
"reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace",
|
"reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -10371,7 +10373,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/composer/pcre/issues",
|
"issues": "https://github.com/composer/pcre/issues",
|
||||||
"source": "https://github.com/composer/pcre/tree/3.1.2"
|
"source": "https://github.com/composer/pcre/tree/3.1.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -10387,7 +10389,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-03-07T15:38:35+00:00"
|
"time": "2024-03-19T10:26:25+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/xdebug-handler",
|
"name": "composer/xdebug-handler",
|
||||||
@@ -10759,16 +10761,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "friendsofphp/php-cs-fixer",
|
"name": "friendsofphp/php-cs-fixer",
|
||||||
"version": "v3.51.0",
|
"version": "v3.52.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||||
"reference": "127fa74f010da99053e3f5b62672615b72dd6efd"
|
"reference": "6e77207f0d851862ceeb6da63e6e22c01b1587bc"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/127fa74f010da99053e3f5b62672615b72dd6efd",
|
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/6e77207f0d851862ceeb6da63e6e22c01b1587bc",
|
||||||
"reference": "127fa74f010da99053e3f5b62672615b72dd6efd",
|
"reference": "6e77207f0d851862ceeb6da63e6e22c01b1587bc",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -10839,7 +10841,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
||||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.51.0"
|
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.52.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -10847,7 +10849,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-02-28T19:50:06+00:00"
|
"time": "2024-03-19T21:02:43+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "masterminds/html5",
|
"name": "masterminds/html5",
|
||||||
@@ -11094,16 +11096,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan",
|
"name": "phpstan/phpstan",
|
||||||
"version": "1.10.60",
|
"version": "1.10.63",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan.git",
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
"reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe"
|
"reference": "ad12836d9ca227301f5fb9960979574ed8628339"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/ad12836d9ca227301f5fb9960979574ed8628339",
|
||||||
"reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
|
"reference": "ad12836d9ca227301f5fb9960979574ed8628339",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -11152,25 +11154,25 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-03-07T13:30:19+00:00"
|
"time": "2024-03-18T16:53:53+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-doctrine",
|
"name": "phpstan/phpstan-doctrine",
|
||||||
"version": "1.3.62",
|
"version": "1.3.63",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
||||||
"reference": "f3abbd8e93e12fed8091be3aeec216b06bed0950"
|
"reference": "6ccde2b243e30ba47f7cd29f87fb752263bcb25c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/f3abbd8e93e12fed8091be3aeec216b06bed0950",
|
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/6ccde2b243e30ba47f7cd29f87fb752263bcb25c",
|
||||||
"reference": "f3abbd8e93e12fed8091be3aeec216b06bed0950",
|
"reference": "6ccde2b243e30ba47f7cd29f87fb752263bcb25c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^8.0",
|
"php": "^7.2 || ^8.0",
|
||||||
"phpstan/phpstan": "^1.10.48"
|
"phpstan/phpstan": "^1.10.63"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"doctrine/collections": "<1.0",
|
"doctrine/collections": "<1.0",
|
||||||
@@ -11222,9 +11224,9 @@
|
|||||||
"description": "Doctrine extensions for PHPStan",
|
"description": "Doctrine extensions for PHPStan",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
|
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.62"
|
"source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.63"
|
||||||
},
|
},
|
||||||
"time": "2024-02-12T11:52:17+00:00"
|
"time": "2024-03-18T18:05:58+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-phpunit",
|
"name": "phpstan/phpstan-phpunit",
|
||||||
@@ -11329,22 +11331,22 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-symfony",
|
"name": "phpstan/phpstan-symfony",
|
||||||
"version": "1.3.8",
|
"version": "1.3.9",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-symfony.git",
|
"url": "https://github.com/phpstan/phpstan-symfony.git",
|
||||||
"reference": "d8a0bc03a68d95288b6471c37d435647fbdaff1a"
|
"reference": "a32bc86da24495025d7aafd1ba62444d4a364a98"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/d8a0bc03a68d95288b6471c37d435647fbdaff1a",
|
"url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/a32bc86da24495025d7aafd1ba62444d4a364a98",
|
||||||
"reference": "d8a0bc03a68d95288b6471c37d435647fbdaff1a",
|
"reference": "a32bc86da24495025d7aafd1ba62444d4a364a98",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-simplexml": "*",
|
"ext-simplexml": "*",
|
||||||
"php": "^7.2 || ^8.0",
|
"php": "^7.2 || ^8.0",
|
||||||
"phpstan/phpstan": "^1.10.36"
|
"phpstan/phpstan": "^1.10.62"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"symfony/framework-bundle": "<3.0"
|
"symfony/framework-bundle": "<3.0"
|
||||||
@@ -11395,9 +11397,9 @@
|
|||||||
"description": "Symfony Framework extensions and rules for PHPStan",
|
"description": "Symfony Framework extensions and rules for PHPStan",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan-symfony/issues",
|
"issues": "https://github.com/phpstan/phpstan-symfony/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan-symfony/tree/1.3.8"
|
"source": "https://github.com/phpstan/phpstan-symfony/tree/1.3.9"
|
||||||
},
|
},
|
||||||
"time": "2024-03-05T16:33:08+00:00"
|
"time": "2024-03-16T16:50:20+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
@@ -12622,16 +12624,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/resource-operations",
|
"name": "sebastian/resource-operations",
|
||||||
"version": "3.0.3",
|
"version": "3.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/resource-operations.git",
|
"url": "https://github.com/sebastianbergmann/resource-operations.git",
|
||||||
"reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
|
"reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
|
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
|
||||||
"reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
|
"reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -12643,7 +12645,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.0-dev"
|
"dev-main": "3.0-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -12664,8 +12666,7 @@
|
|||||||
"description": "Provides a list of PHP built-in functions that operate on resources",
|
"description": "Provides a list of PHP built-in functions that operate on resources",
|
||||||
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
|
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/resource-operations/issues",
|
"source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4"
|
||||||
"source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
|
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -12673,7 +12674,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2020-09-28T06:45:17+00:00"
|
"time": "2024-03-14T16:00:52+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/type",
|
"name": "sebastian/type",
|
||||||
|
|||||||
15
phpstan.neon
15
phpstan.neon
@@ -2202,21 +2202,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Form/DataTransformer/StringToArrayTransformer.php
|
path: src/Form/DataTransformer/StringToArrayTransformer.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Class App\\\\Form\\\\DataTransformer\\\\TagArrayToStringTransformer implements generic interface Symfony\\\\Component\\\\Form\\\\DataTransformerInterface but does not specify its types\\: T, R$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Form/DataTransformer/TagArrayToStringTransformer.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$stringOfTags \\(string\\|null\\) of method App\\\\Form\\\\DataTransformer\\\\TagArrayToStringTransformer\\:\\:reverseTransform\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Form\\\\DataTransformerInterface\\<mixed,mixed\\>\\:\\:reverseTransform\\(\\)$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Form/DataTransformer/TagArrayToStringTransformer.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$tags \\(array\\<App\\\\Entity\\\\Tag\\>\\|null\\) of method App\\\\Form\\\\DataTransformer\\\\TagArrayToStringTransformer\\:\\:transform\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Form\\\\DataTransformerInterface\\<mixed,mixed\\>\\:\\:transform\\(\\)$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Form/DataTransformer/TagArrayToStringTransformer.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method App\\\\Form\\\\Extension\\\\DocumentationLinkExtension\\:\\:buildView\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
|
message: "#^Method App\\\\Form\\\\Extension\\\\DocumentationLinkExtension\\:\\:buildView\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ class Constants
|
|||||||
/**
|
/**
|
||||||
* The current release version
|
* The current release version
|
||||||
*/
|
*/
|
||||||
public const VERSION = '2.13.0';
|
public const VERSION = '2.14.0';
|
||||||
/**
|
/**
|
||||||
* The current release: major * 10000 + minor * 100 + patch
|
* The current release: major * 10000 + minor * 100 + patch
|
||||||
*/
|
*/
|
||||||
public const VERSION_ID = 21300;
|
public const VERSION_ID = 21400;
|
||||||
/**
|
/**
|
||||||
* The software name
|
* The software name
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -255,6 +255,8 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Use "performSearch=1" to skip loading session searches.
|
||||||
|
*
|
||||||
* @param array<string> $filterParams parameter names, which should not be saved (neither session, nor database)
|
* @param array<string> $filterParams parameter names, which should not be saved (neither session, nor database)
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
@@ -298,7 +300,6 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
|||||||
}
|
}
|
||||||
$searchName = $this->getSearchName($data);
|
$searchName = $this->getSearchName($data);
|
||||||
|
|
||||||
/** @var BookmarkRepository $bookmarkRepo */
|
|
||||||
$bookmarkRepo = $this->getBookmark();
|
$bookmarkRepo = $this->getBookmark();
|
||||||
$bookmark = $bookmarkRepo->getSearchDefaultOptions($this->getUser(), $searchName);
|
$bookmark = $bookmarkRepo->getSearchDefaultOptions($this->getUser(), $searchName);
|
||||||
|
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ final class CustomerController extends AbstractController
|
|||||||
$rates = $rateRepository->getRatesForCustomer($customer);
|
$rates = $rateRepository->getRatesForCustomer($customer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $customer->getTimezone()) {
|
if ($customer->getTimezone() !== null && $customer->getTimezone() !== '') {
|
||||||
$timezone = new \DateTimeZone($customer->getTimezone());
|
$timezone = new \DateTimeZone($customer->getTimezone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ final class PasswordResetController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$user->isPasswordRequestNonExpired($this->configuration->getPasswordResetTokenLifetime())) {
|
if (!$user->isPasswordRequestNonExpired($this->configuration->getPasswordResetTokenLifetime())) {
|
||||||
|
$this->flashWarning('This link has already expired');
|
||||||
|
|
||||||
return $this->redirectToRoute('resetting_request');
|
return $this->redirectToRoute('resetting_request');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,15 @@ use Symfony\Component\HttpFoundation\Response;
|
|||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No permission check on controller level, only for single routes.
|
||||||
|
*
|
||||||
|
* There was "view_own_timesheet" here once, but it is a bug, as some companies (rarely, but existing) want their
|
||||||
|
* employees to enter time, but not to see it afterward.
|
||||||
|
*
|
||||||
|
* It is legit to only own "create_own_timesheet" without "view_own_timesheet".
|
||||||
|
*/
|
||||||
#[Route(path: '/timesheet')]
|
#[Route(path: '/timesheet')]
|
||||||
#[IsGranted('view_own_timesheet')]
|
|
||||||
final class TimesheetController extends TimesheetAbstractController
|
final class TimesheetController extends TimesheetAbstractController
|
||||||
{
|
{
|
||||||
#[Route(path: '/', defaults: ['page' => 1], name: 'timesheet', methods: ['GET'])]
|
#[Route(path: '/', defaults: ['page' => 1], name: 'timesheet', methods: ['GET'])]
|
||||||
|
|||||||
@@ -27,8 +27,15 @@ use Symfony\Component\HttpFoundation\Response;
|
|||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No permission check on controller level, only for single routes.
|
||||||
|
*
|
||||||
|
* There was "view_other_timesheet" here once, but it is a bug.
|
||||||
|
* Some companies (rarely, but existing) want their employees to enter time, but not to see it afterward.
|
||||||
|
*
|
||||||
|
* It is legit to only own "create_other_timesheet" without "view_other_timesheet".
|
||||||
|
*/
|
||||||
#[Route(path: '/team/timesheet')]
|
#[Route(path: '/team/timesheet')]
|
||||||
#[IsGranted('view_other_timesheet')]
|
|
||||||
final class TimesheetTeamController extends TimesheetAbstractController
|
final class TimesheetTeamController extends TimesheetAbstractController
|
||||||
{
|
{
|
||||||
#[Route(path: '/', defaults: ['page' => 1], name: 'admin_timesheet', methods: ['GET'])]
|
#[Route(path: '/', defaults: ['page' => 1], name: 'admin_timesheet', methods: ['GET'])]
|
||||||
|
|||||||
@@ -14,32 +14,30 @@ use App\Repository\TagRepository;
|
|||||||
use Symfony\Component\Form\DataTransformerInterface;
|
use Symfony\Component\Form\DataTransformerInterface;
|
||||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @implements DataTransformerInterface<array<Tag>, string>
|
||||||
|
*/
|
||||||
final class TagArrayToStringTransformer implements DataTransformerInterface
|
final class TagArrayToStringTransformer implements DataTransformerInterface
|
||||||
{
|
{
|
||||||
private bool $create = true;
|
public function __construct(
|
||||||
|
private readonly TagRepository $tagRepository,
|
||||||
public function __construct(private TagRepository $tagRepository)
|
private readonly bool $create
|
||||||
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setCreate(bool $create): void
|
|
||||||
{
|
|
||||||
$this->create = $create;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms an array of tags to a string.
|
* Transforms an array of tags to a string.
|
||||||
*
|
*
|
||||||
* @param Tag[]|null $tags
|
* @param Tag[]|null $value
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function transform(mixed $tags): mixed
|
public function transform(mixed $value): string
|
||||||
{
|
{
|
||||||
if (empty($tags)) {
|
if (empty($value)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return implode(', ', $tags);
|
return implode(', ', $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,18 +45,17 @@ final class TagArrayToStringTransformer implements DataTransformerInterface
|
|||||||
*
|
*
|
||||||
* @see \Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer::reverseTransform()
|
* @see \Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer::reverseTransform()
|
||||||
*
|
*
|
||||||
* @param string|null $stringOfTags
|
* @param string|null $value
|
||||||
* @return Tag[]
|
* @return Tag[]
|
||||||
* @throws TransformationFailedException
|
* @throws TransformationFailedException
|
||||||
*/
|
*/
|
||||||
public function reverseTransform(mixed $stringOfTags): mixed
|
public function reverseTransform(mixed $value): mixed
|
||||||
{
|
{
|
||||||
// check for empty tag list
|
// check for empty tag list
|
||||||
if ('' === $stringOfTags || null === $stringOfTags) {
|
if ('' === $value || null === $value) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
$names = array_filter(array_unique(array_map('trim', explode(',', $value))));
|
||||||
$names = array_filter(array_unique(array_map('trim', explode(',', $stringOfTags))));
|
|
||||||
|
|
||||||
// get the current tags and find the new ones that should be created
|
// get the current tags and find the new ones that should be created
|
||||||
$tags = $this->tagRepository->findBy(['name' => $names]);
|
$tags = $this->tagRepository->findBy(['name' => $names]);
|
||||||
@@ -69,9 +66,9 @@ final class TagArrayToStringTransformer implements DataTransformerInterface
|
|||||||
foreach ($newNames as $name) {
|
foreach ($newNames as $name) {
|
||||||
$tag = new Tag();
|
$tag = new Tag();
|
||||||
$tag->setName(mb_substr($name, 0, 100));
|
$tag->setName(mb_substr($name, 0, 100));
|
||||||
$tags[] = $tag;
|
$this->tagRepository->saveTag($tag);
|
||||||
|
|
||||||
// new tags persist automatically thanks to the cascade={"persist"}
|
$tags[] = $tag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,14 +73,13 @@ trait FormTrait
|
|||||||
if ($isNew && \is_int($project)) {
|
if ($isNew && \is_int($project)) {
|
||||||
/** @var Project $project */
|
/** @var Project $project */
|
||||||
$project = $repo->find($project);
|
$project = $repo->find($project);
|
||||||
if ($project === null) {
|
if ($project !== null) {
|
||||||
throw new \Exception('Unknown project');
|
if (!$project->getCustomer()->isVisible()) {
|
||||||
}
|
$customer = null;
|
||||||
if (!$project->getCustomer()->isVisible()) {
|
$project = null;
|
||||||
$customer = null;
|
} elseif (!$project->isVisible()) {
|
||||||
$project = null;
|
$project = null;
|
||||||
} elseif (!$project->isVisible()) {
|
}
|
||||||
$project = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ final class CustomerToolbarForm extends AbstractType
|
|||||||
$countries = $qb->getQuery()->getSingleColumnResult();
|
$countries = $qb->getQuery()->getSingleColumnResult();
|
||||||
$choices = [];
|
$choices = [];
|
||||||
foreach ($countries as $country) {
|
foreach ($countries as $country) {
|
||||||
if (\is_string($country) && \is_string($options['locale'])) {
|
if (\is_string($country) && $country !== '' && \is_string($options['locale'])) {
|
||||||
$choices[$country] = Countries::getName($country, $options['locale']);
|
$choices[$country] = Countries::getName($country, $options['locale']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ final class DateRangeType extends AbstractType
|
|||||||
|
|
||||||
return ['pattern' => $pattern . self::DATE_SPACER . $pattern];
|
return ['pattern' => $pattern . self::DATE_SPACER . $pattern];
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
namespace App\Form\Type;
|
namespace App\Form\Type;
|
||||||
|
|
||||||
use App\Form\DataTransformer\TagArrayToStringTransformer;
|
use App\Form\DataTransformer\TagArrayToStringTransformer;
|
||||||
|
use App\Repository\TagRepository;
|
||||||
use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer;
|
use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
@@ -24,18 +25,17 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
|||||||
*/
|
*/
|
||||||
final class TagsInputType extends AbstractType
|
final class TagsInputType extends AbstractType
|
||||||
{
|
{
|
||||||
public function __construct(private TagArrayToStringTransformer $transformer, private UrlGeneratorInterface $router)
|
public function __construct(
|
||||||
|
private readonly TagRepository $tagRepository,
|
||||||
|
private readonly UrlGeneratorInterface $router
|
||||||
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
if ($options['allow_create'] === false) {
|
|
||||||
$this->transformer->setCreate(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
$builder->addModelTransformer(new CollectionToArrayTransformer(), true);
|
$builder->addModelTransformer(new CollectionToArrayTransformer(), true);
|
||||||
$builder->addModelTransformer($this->transformer, true);
|
$builder->addModelTransformer(new TagArrayToStringTransformer($this->tagRepository, (bool) $options['allow_create']), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
@@ -48,6 +48,7 @@ final class TagsInputType extends AbstractType
|
|||||||
'allow_create' => false,
|
'allow_create' => false,
|
||||||
'label' => 'tag',
|
'label' => 'tag',
|
||||||
]);
|
]);
|
||||||
|
$resolver->setAllowedTypes('allow_create', 'bool');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||||
|
|||||||
@@ -28,78 +28,47 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
|||||||
*/
|
*/
|
||||||
final class TagsSelectType extends AbstractType
|
final class TagsSelectType extends AbstractType
|
||||||
{
|
{
|
||||||
public function __construct(private TagRepository $tagRepository)
|
public function __construct(
|
||||||
|
private readonly TagRepository $tagRepository
|
||||||
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) {
|
if (!$options['allow_create']) {
|
||||||
if (!$options['allow_create']) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
|
||||||
|
/** @var array<string> $tagIds */
|
||||||
$tagIds = $event->getData();
|
$tagIds = $event->getData();
|
||||||
if (!\is_array($tagIds)) {
|
if (!\is_array($tagIds)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$ids = array_filter($tagIds, function ($tagId) {
|
|
||||||
|
$tags = [];
|
||||||
|
foreach ($tagIds as $tagId) {
|
||||||
|
$tag = null;
|
||||||
|
|
||||||
if (is_numeric($tagId)) {
|
if (is_numeric($tagId)) {
|
||||||
return true;
|
$tag = $this->tagRepository->find($tagId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
if ($tag === null) {
|
||||||
});
|
$tag = $this->tagRepository->findTagByName($tagId);
|
||||||
|
|
||||||
// get the current tags and find the new ones that should be created
|
|
||||||
$tags = $this->tagRepository->findBy(['id' => $ids]);
|
|
||||||
|
|
||||||
$foundIds = [];
|
|
||||||
foreach ($tags as $tag) {
|
|
||||||
$foundIds[] = (string) $tag->getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
$newData = [];
|
|
||||||
/** @var array<string> $newNames */
|
|
||||||
$newNames = [];
|
|
||||||
foreach ($tagIds as $tag) {
|
|
||||||
if (!\in_array($tag, $foundIds, true)) {
|
|
||||||
$newNames[] = $tag;
|
|
||||||
} else {
|
|
||||||
$newData[] = $tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. in case someone is using tags like "1234" this can interfere with the ID
|
|
||||||
// 2. if we would load only visible tags, we would try to create new ones below
|
|
||||||
// and that would trigger the unique constraint
|
|
||||||
$tags = $this->tagRepository->findTagsByName($newNames, null);
|
|
||||||
$foundTagNames = [];
|
|
||||||
foreach ($tags as $tag) {
|
|
||||||
$newData[] = (string) $tag->getId();
|
|
||||||
$foundTagNames[] = $tag->getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var array<string> $newNamesCreate */
|
|
||||||
$newNamesCreate = array_udiff($newNames, $foundTagNames, function (mixed $userTag, mixed $existingTag) {
|
|
||||||
if (!\is_string($userTag) || !\is_string($existingTag)) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mb_strtolower($userTag) === mb_strtolower($existingTag)) {
|
if ($tag === null) {
|
||||||
return 0;
|
$tag = new Tag();
|
||||||
|
$tag->setName(mb_substr($tagId, 0, 100));
|
||||||
|
$this->tagRepository->saveTag($tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
return strcmp($userTag, $existingTag);
|
$tags[] = $tag->getId();
|
||||||
});
|
|
||||||
|
|
||||||
foreach ($newNamesCreate as $name) {
|
|
||||||
$tag = new Tag();
|
|
||||||
$tag->setName(mb_substr($name, 0, 100));
|
|
||||||
$this->tagRepository->saveTag($tag);
|
|
||||||
$newData[] = $tag->getId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$event->setData($newData);
|
$event->setData($tags);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +101,8 @@ final class TagsSelectType extends AbstractType
|
|||||||
return $repo->getQueryBuilderForFormType($query);
|
return $repo->getQueryBuilderForFormType($query);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$resolver->setAllowedTypes('allow_create', 'bool');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ use App\Invoice\InvoiceModelHydrator;
|
|||||||
|
|
||||||
final class InvoiceModelDefaultHydrator implements InvoiceModelHydrator
|
final class InvoiceModelDefaultHydrator implements InvoiceModelHydrator
|
||||||
{
|
{
|
||||||
|
private const DATE_PROCESS_FORMAT = 'Y-m-d h:i:s';
|
||||||
|
|
||||||
public function hydrate(InvoiceModel $model): array
|
public function hydrate(InvoiceModel $model): array
|
||||||
{
|
{
|
||||||
$currency = $model->getCurrency();
|
$currency = $model->getCurrency();
|
||||||
@@ -24,7 +26,9 @@ final class InvoiceModelDefaultHydrator implements InvoiceModelHydrator
|
|||||||
|
|
||||||
$values = [
|
$values = [
|
||||||
'invoice.due_date' => $formatter->getFormattedDateTime($model->getDueDate()),
|
'invoice.due_date' => $formatter->getFormattedDateTime($model->getDueDate()),
|
||||||
|
'invoice.due_date_process' => $model->getDueDate()->format(self::DATE_PROCESS_FORMAT), // since 2.14
|
||||||
'invoice.date' => $formatter->getFormattedDateTime($model->getInvoiceDate()),
|
'invoice.date' => $formatter->getFormattedDateTime($model->getInvoiceDate()),
|
||||||
|
'invoice.date_process' => $model->getInvoiceDate()->format(self::DATE_PROCESS_FORMAT), // since 2.14
|
||||||
'invoice.number' => $model->getInvoiceNumber(),
|
'invoice.number' => $model->getInvoiceNumber(),
|
||||||
'invoice.currency' => $currency,
|
'invoice.currency' => $currency,
|
||||||
'invoice.language' => $model->getTemplate()->getLanguage(), // since 1.9
|
'invoice.language' => $model->getTemplate()->getLanguage(), // since 1.9
|
||||||
@@ -55,11 +59,13 @@ final class InvoiceModelDefaultHydrator implements InvoiceModelHydrator
|
|||||||
|
|
||||||
'query.begin' => '',
|
'query.begin' => '',
|
||||||
'query.begin_day' => '',
|
'query.begin_day' => '',
|
||||||
|
'query.begin_process' => null, // since 2.14
|
||||||
'query.begin_month' => '',
|
'query.begin_month' => '',
|
||||||
'query.begin_month_number' => '',
|
'query.begin_month_number' => '',
|
||||||
'query.begin_year' => '',
|
'query.begin_year' => '',
|
||||||
'query.end' => '', // since 1.9
|
'query.end' => '', // since 1.9
|
||||||
'query.end_day' => '', // since 1.9
|
'query.end_day' => '', // since 1.9
|
||||||
|
'query.end_process' => null, // since 2.14
|
||||||
'query.end_month' => '', // since 1.9
|
'query.end_month' => '', // since 1.9
|
||||||
'query.end_month_number' => '', // since 1.9
|
'query.end_month_number' => '', // since 1.9
|
||||||
'query.end_year' => '', // since 1.9
|
'query.end_year' => '', // since 1.9
|
||||||
@@ -82,6 +88,7 @@ final class InvoiceModelDefaultHydrator implements InvoiceModelHydrator
|
|||||||
'query.year' => $begin->format('Y'),
|
'query.year' => $begin->format('Y'),
|
||||||
// @deprecated - but impossible to delete
|
// @deprecated - but impossible to delete
|
||||||
'query.begin' => $formatter->getFormattedDateTime($begin),
|
'query.begin' => $formatter->getFormattedDateTime($begin),
|
||||||
|
'query.begin_process' => $begin->format(self::DATE_PROCESS_FORMAT), // since 2.14
|
||||||
'query.begin_day' => $begin->format('d'),
|
'query.begin_day' => $begin->format('d'),
|
||||||
'query.begin_month' => $formatter->getFormattedMonthName($begin),
|
'query.begin_month' => $formatter->getFormattedMonthName($begin),
|
||||||
'query.begin_month_number' => $begin->format('m'),
|
'query.begin_month_number' => $begin->format('m'),
|
||||||
@@ -93,13 +100,10 @@ final class InvoiceModelDefaultHydrator implements InvoiceModelHydrator
|
|||||||
if ($end !== null) {
|
if ($end !== null) {
|
||||||
$values = array_merge($values, [
|
$values = array_merge($values, [
|
||||||
'query.end' => $formatter->getFormattedDateTime($end),
|
'query.end' => $formatter->getFormattedDateTime($end),
|
||||||
// since 1.9
|
'query.end_process' => $end->format(self::DATE_PROCESS_FORMAT), // since 2.14
|
||||||
'query.end_day' => $end->format('d'),
|
'query.end_day' => $end->format('d'),
|
||||||
// since 1.9
|
|
||||||
'query.end_month' => $formatter->getFormattedMonthName($end),
|
'query.end_month' => $formatter->getFormattedMonthName($end),
|
||||||
// since 1.9
|
|
||||||
'query.end_month_number' => $end->format('m'),
|
'query.end_month_number' => $end->format('m'),
|
||||||
// since 1.9
|
|
||||||
'query.end_year' => $end->format('Y'),
|
'query.end_year' => $end->format('Y'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -145,7 +149,9 @@ final class InvoiceModelDefaultHydrator implements InvoiceModelHydrator
|
|||||||
if ($min !== null && $max !== null) {
|
if ($min !== null && $max !== null) {
|
||||||
$values = array_merge($values, [
|
$values = array_merge($values, [
|
||||||
'invoice.first' => $formatter->getFormattedDateTime($min->getBegin()),
|
'invoice.first' => $formatter->getFormattedDateTime($min->getBegin()),
|
||||||
|
'invoice.first_process' => $min->getBegin()?->format(self::DATE_PROCESS_FORMAT), // since 2.14
|
||||||
'invoice.last' => $formatter->getFormattedDateTime($max->getEnd()),
|
'invoice.last' => $formatter->getFormattedDateTime($max->getEnd()),
|
||||||
|
'invoice.last_process' => $max->getEnd()?->format(self::DATE_PROCESS_FORMAT), // since 2.14
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace App\Saml;
|
|||||||
use App\Configuration\SamlConfigurationInterface;
|
use App\Configuration\SamlConfigurationInterface;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
|
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
|
||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
@@ -24,7 +25,8 @@ final class SamlProvider
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly UserRepository $repository,
|
private readonly UserRepository $repository,
|
||||||
private readonly UserProviderInterface $userProvider,
|
private readonly UserProviderInterface $userProvider,
|
||||||
private readonly SamlConfigurationInterface $configuration
|
private readonly SamlConfigurationInterface $configuration,
|
||||||
|
private readonly LoggerInterface $logger
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +39,8 @@ final class SamlProvider
|
|||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = $this->userProvider->loadUserByIdentifier($token->getUserIdentifier());
|
$user = $this->userProvider->loadUserByIdentifier($token->getUserIdentifier());
|
||||||
}
|
}
|
||||||
} catch (UserNotFoundException $e) {
|
} catch (UserNotFoundException $ex) {
|
||||||
|
$this->logger->error($ex->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -49,6 +52,7 @@ final class SamlProvider
|
|||||||
|
|
||||||
$this->repository->saveUser($user);
|
$this->repository->saveUser($user);
|
||||||
} catch (\Exception $ex) {
|
} catch (\Exception $ex) {
|
||||||
|
$this->logger->error($ex->getMessage());
|
||||||
throw new AuthenticationException(
|
throw new AuthenticationException(
|
||||||
sprintf('Failed creating or hydrating user "%s": %s', $token->getUserIdentifier(), $ex->getMessage())
|
sprintf('Failed creating or hydrating user "%s": %s', $token->getUserIdentifier(), $ex->getMessage())
|
||||||
);
|
);
|
||||||
@@ -110,7 +114,8 @@ final class SamlProvider
|
|||||||
if (method_exists($user, $setter)) {
|
if (method_exists($user, $setter)) {
|
||||||
$user->$setter($value);
|
$user->$setter($value);
|
||||||
} else {
|
} else {
|
||||||
throw new \RuntimeException('Invalid mapping field given: ' . $field);
|
// this should never happen, because it is validated when the container is built
|
||||||
|
throw new \RuntimeException('Invalid SAML mapping field: ' . $field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +141,7 @@ final class SamlProvider
|
|||||||
if ($part[0] === '$') {
|
if ($part[0] === '$') {
|
||||||
$key = substr($part, 1);
|
$key = substr($part, 1);
|
||||||
if (!\array_key_exists($key, $attributes)) {
|
if (!\array_key_exists($key, $attributes)) {
|
||||||
throw new \RuntimeException('Missing user attribute: ' . $key);
|
throw new \RuntimeException('Missing SAML attribute in response: ' . $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\is_array($attributes[$key]) && isset($attributes[$key][0])) {
|
if (\is_array($attributes[$key]) && isset($attributes[$key][0])) {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ final class ProjectVoter extends Voter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// those cannot be assigned to teams
|
// those cannot be assigned to teams
|
||||||
if (\in_array($attribute, ['create', 'delete'])) {
|
if (\in_array($attribute, ['create', 'delete'], true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -290,7 +290,7 @@
|
|||||||
|
|
||||||
{% macro meta_field_value(entity, field) %}
|
{% macro meta_field_value(entity, field) %}
|
||||||
{% set metaField = entity.metaField(field.name) %}
|
{% set metaField = entity.metaField(field.name) %}
|
||||||
{% if not metaField is null and metaField.value is not null and metaField.value is not empty %}
|
{% if not metaField is null %}
|
||||||
{% set metaField = metaField.merge(field) %}
|
{% set metaField = metaField.merge(field) %}
|
||||||
{{ _self.form_type_value(metaField.type, metaField.value, entity) }}
|
{{ _self.form_type_value(metaField.type, metaField.value, entity) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
<fieldset class="form-fieldset form-fieldset-light">
|
<fieldset class="form-fieldset form-fieldset-light">
|
||||||
<legend>{{ 'work_times_should'|trans }}</legend>
|
<legend>{{ 'work_times_should'|trans }}</legend>
|
||||||
<div class="form-text mb-3 help-text">{{ 'work_times_should.help'|trans }}</div>
|
|
||||||
{{ form_row(form.workHoursMonday) }}
|
{{ form_row(form.workHoursMonday) }}
|
||||||
{{ form_row(form.workHoursTuesday) }}
|
{{ form_row(form.workHoursTuesday) }}
|
||||||
{{ form_row(form.workHoursWednesday) }}
|
{{ form_row(form.workHoursWednesday) }}
|
||||||
|
|||||||
@@ -465,7 +465,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'hourlyRate' => 127,
|
'hourlyRate' => 127,
|
||||||
'billable' => false
|
'billable' => false
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets', 'POST', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets', 'POST', [], $json);
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
@@ -494,7 +496,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'hourlyRate' => 127,
|
'hourlyRate' => 127,
|
||||||
'billable' => true
|
'billable' => true
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets?full=true', 'POST', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets?full=true', 'POST', [], $json);
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
@@ -526,7 +530,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:m:0'),
|
'end' => ($dateTime->createDateTime())->format('Y-m-d H:m:0'),
|
||||||
'description' => 'foo',
|
'description' => 'foo',
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets', 'POST', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets', 'POST', [], $json);
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
@@ -571,7 +577,60 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'end' => (new \DateTime())->format('Y-m-d H:m:s'),
|
'end' => (new \DateTime())->format('Y-m-d H:m:s'),
|
||||||
'description' => 'foo',
|
'description' => 'foo',
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets', 'POST', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets', 'POST', [], $json);
|
||||||
|
$this->assertApiCallValidationError($client->getResponse(), ['project']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostActionWithUnknownActivity(): void
|
||||||
|
{
|
||||||
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||||
|
|
||||||
|
$em = $this->getEntityManager();
|
||||||
|
$customer = new Customer('foo-bar-1');
|
||||||
|
$customer->setVisible(false);
|
||||||
|
$customer->setCountry('DE');
|
||||||
|
$customer->setTimezone('Europe/Berlin');
|
||||||
|
$em->persist($customer);
|
||||||
|
$project = new Project();
|
||||||
|
$project->setName('foo-bar-2');
|
||||||
|
$project->setVisible(true);
|
||||||
|
$project->setCustomer($customer);
|
||||||
|
$em->persist($project);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'begin' => (new \DateTime('- 8 hours'))->format('Y-m-d H:m:s'),
|
||||||
|
'end' => (new \DateTime())->format('Y-m-d H:m:s'),
|
||||||
|
'project' => $project->getId(),
|
||||||
|
'activity' => 99,
|
||||||
|
];
|
||||||
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets', 'POST', [], $json);
|
||||||
|
$this->assertApiCallValidationError($client->getResponse(), ['project']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostActionWithNonExistingProject(): void
|
||||||
|
{
|
||||||
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||||
|
|
||||||
|
$em = $this->getEntityManager();
|
||||||
|
$activity = new Activity();
|
||||||
|
$activity->setName('foo-bar-3');
|
||||||
|
$activity->setVisible(true);
|
||||||
|
$em->persist($activity);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'begin' => (new \DateTime('- 8 hours'))->format('Y-m-d H:m:s'),
|
||||||
|
'end' => (new \DateTime())->format('Y-m-d H:m:s'),
|
||||||
|
'project' => 99,
|
||||||
|
'activity' => $activity->getId(),
|
||||||
|
];
|
||||||
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets', 'POST', [], $json);
|
||||||
$this->assertApiCallValidationError($client->getResponse(), ['project']);
|
$this->assertApiCallValidationError($client->getResponse(), ['project']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +664,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'end' => (new \DateTime())->format('Y-m-d H:m'),
|
'end' => (new \DateTime())->format('Y-m-d H:m'),
|
||||||
'description' => 'foo',
|
'description' => 'foo',
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets', 'POST', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets', 'POST', [], $json);
|
||||||
$this->assertApiCallValidationError($client->getResponse(), ['activity']);
|
$this->assertApiCallValidationError($client->getResponse(), ['activity']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,7 +696,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'end' => (new \DateTime())->format('Y-m-d H:m'),
|
'end' => (new \DateTime())->format('Y-m-d H:m'),
|
||||||
'description' => 'foo',
|
'description' => 'foo',
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets', 'POST', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets', 'POST', [], $json);
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
@@ -674,7 +737,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'description' => 'foo',
|
'description' => 'foo',
|
||||||
'billable' => true,
|
'billable' => true,
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets', 'POST', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets', 'POST', [], $json);
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
@@ -700,7 +765,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'description' => 'foo',
|
'description' => 'foo',
|
||||||
'billable' => false,
|
'billable' => false,
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'PATCH', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'PATCH', [], $json);
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
@@ -739,7 +806,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'description' => 'foo',
|
'description' => 'foo',
|
||||||
'exported' => true,
|
'exported' => true,
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'PATCH', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'PATCH', [], $json);
|
||||||
$response = $client->getResponse();
|
$response = $client->getResponse();
|
||||||
$this->assertApiResponseAccessDenied($response);
|
$this->assertApiResponseAccessDenied($response);
|
||||||
}
|
}
|
||||||
@@ -761,7 +830,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'end' => (new \DateTime('- 1 hours'))->format('Y-m-d H:m'),
|
'end' => (new \DateTime('- 1 hours'))->format('Y-m-d H:m'),
|
||||||
'description' => 'foo',
|
'description' => 'foo',
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'PATCH', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'PATCH', [], $json);
|
||||||
|
|
||||||
$response = $client->getResponse();
|
$response = $client->getResponse();
|
||||||
self::assertEquals(400, $response->getStatusCode());
|
self::assertEquals(400, $response->getStatusCode());
|
||||||
@@ -1066,7 +1137,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'description' => 'foo',
|
'description' => 'foo',
|
||||||
'tags' => ['another', 'testing', 'bar']
|
'tags' => ['another', 'testing', 'bar']
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets/' . $id, 'PATCH', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets/' . $id, 'PATCH', [], $json);
|
||||||
|
|
||||||
$this->request($client, '/api/timesheets/' . $id . '/restart', 'PATCH');
|
$this->request($client, '/api/timesheets/' . $id . '/restart', 'PATCH');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
@@ -1100,7 +1173,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'description' => 'foo',
|
'description' => 'foo',
|
||||||
'tags' => ['another', 'testing', 'bar']
|
'tags' => ['another', 'testing', 'bar']
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets/' . $id, 'PATCH', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets/' . $id, 'PATCH', [], $json);
|
||||||
|
|
||||||
$begin = new \DateTime('2019-11-27 13:55:00');
|
$begin = new \DateTime('2019-11-27 13:55:00');
|
||||||
$this->request($client, '/api/timesheets/' . $id . '/restart', 'PATCH', ['begin' => $begin->format(BaseApiController::DATE_FORMAT_PHP)]);
|
$this->request($client, '/api/timesheets/' . $id . '/restart', 'PATCH', ['begin' => $begin->format(BaseApiController::DATE_FORMAT_PHP)]);
|
||||||
@@ -1211,7 +1286,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'fixedRate' => 2016,
|
'fixedRate' => 2016,
|
||||||
'hourlyRate' => 127
|
'hourlyRate' => 127
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets', 'POST', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets', 'POST', [], $json);
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
@@ -1343,7 +1420,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
'name' => 'metatestmock',
|
'name' => 'metatestmock',
|
||||||
'value' => 'another,testing,bar'
|
'value' => 'another,testing,bar'
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/timesheets/' . $id . '/meta', 'PATCH', [], json_encode($data));
|
$json = json_encode($data);
|
||||||
|
self::assertIsString($json);
|
||||||
|
$this->request($client, '/api/timesheets/' . $id . '/meta', 'PATCH', [], $json);
|
||||||
|
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class TagArrayToStringTransformerTest extends TestCase
|
|||||||
|
|
||||||
$repository = $this->getMockBuilder(TagRepository::class)->disableOriginalConstructor()->getMock();
|
$repository = $this->getMockBuilder(TagRepository::class)->disableOriginalConstructor()->getMock();
|
||||||
|
|
||||||
$sut = new TagArrayToStringTransformer($repository);
|
$sut = new TagArrayToStringTransformer($repository, true);
|
||||||
|
|
||||||
$this->assertEquals('', $sut->transform([]));
|
$this->assertEquals('', $sut->transform([]));
|
||||||
$this->assertEquals('', $sut->transform(null));
|
$this->assertEquals('', $sut->transform(null));
|
||||||
@@ -45,10 +45,12 @@ class TagArrayToStringTransformerTest extends TestCase
|
|||||||
(new Tag())->setName('bar'),
|
(new Tag())->setName('bar'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$repository = $this->getMockBuilder(TagRepository::class)->onlyMethods(['findBy'])->disableOriginalConstructor()->getMock();
|
$repository = $this->getMockBuilder(TagRepository::class)
|
||||||
|
->onlyMethods(['findBy', 'saveTag'])
|
||||||
|
->disableOriginalConstructor()->getMock();
|
||||||
$repository->expects($this->once())->method('findBy')->willReturn($results);
|
$repository->expects($this->once())->method('findBy')->willReturn($results);
|
||||||
|
|
||||||
$sut = new TagArrayToStringTransformer($repository);
|
$sut = new TagArrayToStringTransformer($repository, true);
|
||||||
|
|
||||||
$this->assertEquals([], $sut->reverseTransform(''));
|
$this->assertEquals([], $sut->reverseTransform(''));
|
||||||
$this->assertEquals([], $sut->reverseTransform(null));
|
$this->assertEquals([], $sut->reverseTransform(null));
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ class InvoiceModelDefaultHydratorTest extends TestCase
|
|||||||
{
|
{
|
||||||
$keys = [
|
$keys = [
|
||||||
'invoice.due_date',
|
'invoice.due_date',
|
||||||
|
'invoice.due_date_process',
|
||||||
'invoice.date',
|
'invoice.date',
|
||||||
|
'invoice.date_process',
|
||||||
'invoice.number',
|
'invoice.number',
|
||||||
'invoice.currency',
|
'invoice.currency',
|
||||||
'invoice.currency_symbol',
|
'invoice.currency_symbol',
|
||||||
@@ -47,7 +49,9 @@ class InvoiceModelDefaultHydratorTest extends TestCase
|
|||||||
'invoice.total_time',
|
'invoice.total_time',
|
||||||
'invoice.duration_decimal',
|
'invoice.duration_decimal',
|
||||||
'invoice.first',
|
'invoice.first',
|
||||||
|
'invoice.first_process',
|
||||||
'invoice.last',
|
'invoice.last',
|
||||||
|
'invoice.last_process',
|
||||||
'invoice.total',
|
'invoice.total',
|
||||||
'invoice.total_nc',
|
'invoice.total_nc',
|
||||||
'invoice.total_plain',
|
'invoice.total_plain',
|
||||||
@@ -68,11 +72,13 @@ class InvoiceModelDefaultHydratorTest extends TestCase
|
|||||||
'query.month_number',
|
'query.month_number',
|
||||||
'query.year',
|
'query.year',
|
||||||
'query.begin',
|
'query.begin',
|
||||||
|
'query.begin_process',
|
||||||
'query.begin_day',
|
'query.begin_day',
|
||||||
'query.begin_month',
|
'query.begin_month',
|
||||||
'query.begin_month_number',
|
'query.begin_month_number',
|
||||||
'query.begin_year',
|
'query.begin_year',
|
||||||
'query.end',
|
'query.end',
|
||||||
|
'query.end_process',
|
||||||
'query.end_day',
|
'query.end_day',
|
||||||
'query.end_month',
|
'query.end_month',
|
||||||
'query.end_month_number',
|
'query.end_month_number',
|
||||||
|
|||||||
@@ -79,7 +79,9 @@ class DebugRendererTest extends TestCase
|
|||||||
{
|
{
|
||||||
$keys = [
|
$keys = [
|
||||||
'invoice.due_date',
|
'invoice.due_date',
|
||||||
|
'invoice.due_date_process',
|
||||||
'invoice.date',
|
'invoice.date',
|
||||||
|
'invoice.date_process',
|
||||||
'invoice.number',
|
'invoice.number',
|
||||||
'invoice.currency',
|
'invoice.currency',
|
||||||
'invoice.currency_symbol',
|
'invoice.currency_symbol',
|
||||||
@@ -92,7 +94,9 @@ class DebugRendererTest extends TestCase
|
|||||||
'invoice.total_time',
|
'invoice.total_time',
|
||||||
'invoice.duration_decimal',
|
'invoice.duration_decimal',
|
||||||
'invoice.first',
|
'invoice.first',
|
||||||
|
'invoice.first_process',
|
||||||
'invoice.last',
|
'invoice.last',
|
||||||
|
'invoice.last_process',
|
||||||
'invoice.total',
|
'invoice.total',
|
||||||
'invoice.total_nc',
|
'invoice.total_nc',
|
||||||
'invoice.total_plain',
|
'invoice.total_plain',
|
||||||
@@ -113,11 +117,13 @@ class DebugRendererTest extends TestCase
|
|||||||
'query.month_number',
|
'query.month_number',
|
||||||
'query.year',
|
'query.year',
|
||||||
'query.begin',
|
'query.begin',
|
||||||
|
'query.begin_process',
|
||||||
'query.begin_day',
|
'query.begin_day',
|
||||||
'query.begin_month',
|
'query.begin_month',
|
||||||
'query.begin_month_number',
|
'query.begin_month_number',
|
||||||
'query.begin_year',
|
'query.begin_year',
|
||||||
'query.end',
|
'query.end',
|
||||||
|
'query.end_process',
|
||||||
'query.end_day',
|
'query.end_day',
|
||||||
'query.end_month',
|
'query.end_month',
|
||||||
'query.end_month_number',
|
'query.end_month_number',
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ class ExportQueryTest extends TimesheetQueryTest
|
|||||||
|
|
||||||
public function assertMarkAsExported(ExportQuery $sut): void
|
public function assertMarkAsExported(ExportQuery $sut): void
|
||||||
{
|
{
|
||||||
$this->assertTrue($sut->isMarkAsExported());
|
|
||||||
|
|
||||||
$sut->setMarkAsExported(false);
|
|
||||||
$this->assertFalse($sut->isMarkAsExported());
|
$this->assertFalse($sut->isMarkAsExported());
|
||||||
|
|
||||||
|
$sut->setMarkAsExported(true);
|
||||||
|
$this->assertTrue($sut->isMarkAsExported());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function assertRenderer(ExportQuery $sut): void
|
public function assertRenderer(ExportQuery $sut): void
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use App\Saml\SamlProvider;
|
|||||||
use App\Tests\Configuration\TestConfigLoader;
|
use App\Tests\Configuration\TestConfigLoader;
|
||||||
use App\Tests\Mocks\SystemConfigurationFactory;
|
use App\Tests\Mocks\SystemConfigurationFactory;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ class SamlProviderTest extends TestCase
|
|||||||
$userProvider->method('loadUserByIdentifier')->willReturn(new User());
|
$userProvider->method('loadUserByIdentifier')->willReturn(new User());
|
||||||
}
|
}
|
||||||
|
|
||||||
$provider = new SamlProvider($repository, $userProvider, $samlConfig);
|
$provider = new SamlProvider($repository, $userProvider, $samlConfig, $this->createMock(LoggerInterface::class));
|
||||||
|
|
||||||
return $provider;
|
return $provider;
|
||||||
}
|
}
|
||||||
@@ -107,7 +108,7 @@ class SamlProviderTest extends TestCase
|
|||||||
public function testAuthenticateThrowsAuthenticationException(): void
|
public function testAuthenticateThrowsAuthenticationException(): void
|
||||||
{
|
{
|
||||||
$this->expectException(AuthenticationException::class);
|
$this->expectException(AuthenticationException::class);
|
||||||
$this->expectExceptionMessage('Failed creating or hydrating user "foo1@example.com": Missing user attribute: Email');
|
$this->expectExceptionMessage('Failed creating or hydrating user "foo1@example.com": Missing SAML attribute in response: Email');
|
||||||
|
|
||||||
$user = new User();
|
$user = new User();
|
||||||
$user->setAuth(User::AUTH_SAML);
|
$user->setAuth(User::AUTH_SAML);
|
||||||
|
|||||||
@@ -502,11 +502,6 @@ parameters:
|
|||||||
count: 6
|
count: 6
|
||||||
path: API/TimesheetControllerTest.php
|
path: API/TimesheetControllerTest.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#5 \\$content of method App\\\\Tests\\\\API\\\\APIControllerBaseTest\\:\\:request\\(\\) expects string\\|null, string\\|false given\\.$#"
|
|
||||||
count: 14
|
|
||||||
path: API/TimesheetControllerTest.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Cannot access offset 'enabled' on mixed\\.$#"
|
message: "#^Cannot access offset 'enabled' on mixed\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
|||||||
@@ -1466,7 +1466,7 @@
|
|||||||
<target state="translated">مرَض</target>
|
<target state="translated">مرَض</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">الساعات المستهدَفة</target>
|
<target state="translated">الساعات المستهدَفة</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="AXr_aHI" resname="completed_month_pdf_note">
|
<trans-unit id="AXr_aHI" resname="completed_month_pdf_note">
|
||||||
|
|||||||
@@ -1531,10 +1531,6 @@
|
|||||||
<source>work_times_result</source>
|
<source>work_times_result</source>
|
||||||
<target state="translated">Odpracované hodiny</target>
|
<target state="translated">Odpracované hodiny</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">Zde nakonfigurované hodnoty se používají k výpočtu hodinového účtu.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
||||||
<source>work_times_should.none_configured</source>
|
<source>work_times_should.none_configured</source>
|
||||||
<target state="translated">Pro tohoto uživatele nebyly v nastavení pracovní smlouvy nakonfigurovány žádné cílové hodiny.</target>
|
<target state="translated">Pro tohoto uživatele nebyly v nastavení pracovní smlouvy nakonfigurovány žádné cílové hodiny.</target>
|
||||||
@@ -1548,7 +1544,7 @@
|
|||||||
<target state="translated">Je-hodin</target>
|
<target state="translated">Je-hodin</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Cílové hodiny</target>
|
<target state="translated">Cílové hodiny</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8yqaKzX" resname="confirmed_by_at">
|
<trans-unit id="8yqaKzX" resname="confirmed_by_at">
|
||||||
|
|||||||
@@ -647,12 +647,8 @@
|
|||||||
<target>Arbeitszeitkonto</target>
|
<target>Arbeitszeitkonto</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target>Soll-Stunden</target>
|
<target>Erwartete Stundenzahl</target>
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target>Die hier konfigurierten Werte werden zur Berechnung des Stundenkontos verwendet.</target>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
||||||
<source>work_times_should.none_configured</source>
|
<source>work_times_should.none_configured</source>
|
||||||
|
|||||||
@@ -647,12 +647,8 @@
|
|||||||
<target state="translated">Account working times</target>
|
<target state="translated">Account working times</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target>Target-hours</target>
|
<target>Expected number of hours</target>
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target>The values configured here are used to calculate the hourly account.</target>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
||||||
<source>work_times_should.none_configured</source>
|
<source>work_times_should.none_configured</source>
|
||||||
|
|||||||
@@ -1496,13 +1496,9 @@
|
|||||||
<target state="translated">Evaluación</target>
|
<target state="translated">Evaluación</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Horas de objetivo</target>
|
<target state="translated">Horas de objetivo</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">Los valores configurados aquí se utilizan para calcular las horas.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="AXr_aHI" resname="completed_month_pdf_note">
|
<trans-unit id="AXr_aHI" resname="completed_month_pdf_note">
|
||||||
<source>completed_month_pdf_note</source>
|
<source>completed_month_pdf_note</source>
|
||||||
<target state="translated">Este documento sirve como prueba de las horas trabajadas.</target>
|
<target state="translated">Este documento sirve como prueba de las horas trabajadas.</target>
|
||||||
|
|||||||
@@ -1504,7 +1504,7 @@
|
|||||||
<target state="translated">Évaluation</target>
|
<target state="translated">Évaluation</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Heures cibles</target>
|
<target state="translated">Heures cibles</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
||||||
@@ -1535,10 +1535,6 @@
|
|||||||
<source>Working times</source>
|
<source>Working times</source>
|
||||||
<target state="translated">Heures de travail</target>
|
<target state="translated">Heures de travail</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">Les valeurs configurées ici sont utilisées pour calculer le compte horaire.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="YPO294a" resname="absence_other.intro" xml:space="preserve">
|
<trans-unit id="YPO294a" resname="absence_other.intro" xml:space="preserve">
|
||||||
<source>absence_other.intro</source>
|
<source>absence_other.intro</source>
|
||||||
<target state="translated">Evènements familiaux, déménagement, catastrophes climatiques, etc.</target>
|
<target state="translated">Evènements familiaux, déménagement, catastrophes climatiques, etc.</target>
|
||||||
|
|||||||
@@ -1446,10 +1446,6 @@
|
|||||||
<source>Account working times</source>
|
<source>Account working times</source>
|
||||||
<target state="translated">זמני העבודה בחשבון</target>
|
<target state="translated">זמני העבודה בחשבון</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">הערכים שהוגדרו כאן משמשים לחישוב החשבון השעתי.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="31dFXhR" resname="work_times_is">
|
<trans-unit id="31dFXhR" resname="work_times_is">
|
||||||
<source>work_times_is</source>
|
<source>work_times_is</source>
|
||||||
<target state="translated">שעות בפועל</target>
|
<target state="translated">שעות בפועל</target>
|
||||||
@@ -1487,7 +1483,7 @@
|
|||||||
<target state="translated">סקירה</target>
|
<target state="translated">סקירה</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">שעות יעד</target>
|
<target state="translated">שעות יעד</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
||||||
|
|||||||
@@ -1500,13 +1500,9 @@
|
|||||||
<target state="translated">Ovaj dokument služi kao dokaz o odrađenim satima.</target>
|
<target state="translated">Ovaj dokument služi kao dokaz o odrađenim satima.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Ciljani sati</target>
|
<target state="translated">Ciljani sati</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">Ovdje konfigurirane vrijednosti se koriste za računanje računa po satu.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="wZRRqg0" resname="manual_bookings">
|
<trans-unit id="wZRRqg0" resname="manual_bookings">
|
||||||
<source>manual_bookings</source>
|
<source>manual_bookings</source>
|
||||||
<target state="translated">Ručne rezervacije</target>
|
<target state="translated">Ručne rezervacije</target>
|
||||||
|
|||||||
@@ -1491,7 +1491,7 @@
|
|||||||
<target state="final">Nessun obiettivo di ore è stato configurato per questo utente nelle impostazioni del contratto di lavoro.</target>
|
<target state="final">Nessun obiettivo di ore è stato configurato per questo utente nelle impostazioni del contratto di lavoro.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should" approved="yes">
|
<trans-unit id="1btzNDa" resname="work_times_should" approved="yes">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="final">Obiettivo di ore</target>
|
<target state="final">Obiettivo di ore</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="znIJiOw" resname="manual_bookings.duration_help" approved="yes">
|
<trans-unit id="znIJiOw" resname="manual_bookings.duration_help" approved="yes">
|
||||||
@@ -1522,10 +1522,6 @@
|
|||||||
<source>completed_month_pdf_note</source>
|
<source>completed_month_pdf_note</source>
|
||||||
<target state="final">Questo documento serve come prova delle ore lavorate.</target>
|
<target state="final">Questo documento serve come prova delle ore lavorate.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help" approved="yes">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="final">I valori configurati qui vengono usati per calcolare il conto orario.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="8yqaKzX" resname="confirmed_by_at" approved="yes">
|
<trans-unit id="8yqaKzX" resname="confirmed_by_at" approved="yes">
|
||||||
<source>confirmed_by_at</source>
|
<source>confirmed_by_at</source>
|
||||||
<target state="final">Confermato da %user% il %date%</target>
|
<target state="final">Confermato da %user% il %date%</target>
|
||||||
|
|||||||
@@ -1484,7 +1484,7 @@
|
|||||||
<target state="translated">Arbeidsovereenkomst</target>
|
<target state="translated">Arbeidsovereenkomst</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Streefuren</target>
|
<target state="translated">Streefuren</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8yqaKzX" resname="confirmed_by_at">
|
<trans-unit id="8yqaKzX" resname="confirmed_by_at">
|
||||||
@@ -1507,10 +1507,6 @@
|
|||||||
<source>manual_bookings.work_contract_intro</source>
|
<source>manual_bookings.work_contract_intro</source>
|
||||||
<target state="translated">Handmatige boeken kunnen niet achtereenvolgens worden gewijzigd of verwijderd!</target>
|
<target state="translated">Handmatige boeken kunnen niet achtereenvolgens worden gewijzigd of verwijderd!</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">De hier geconfigureerde waarden worden gebruikt om de rekening per uur te berekenen.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
||||||
<source>work_times_should.none_configured</source>
|
<source>work_times_should.none_configured</source>
|
||||||
<target state="translated">Voor deze gebruiker zijn geen streefuren geconfigureerd in de instellingen van de arbeidsovereenkomst.</target>
|
<target state="translated">Voor deze gebruiker zijn geen streefuren geconfigureerd in de instellingen van de arbeidsovereenkomst.</target>
|
||||||
|
|||||||
@@ -1512,7 +1512,7 @@
|
|||||||
<target state="translated">Czas pracy</target>
|
<target state="translated">Czas pracy</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Godziny docelowe</target>
|
<target state="translated">Godziny docelowe</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="t1r372x" resname="invisible">
|
<trans-unit id="t1r372x" resname="invisible">
|
||||||
|
|||||||
@@ -1688,13 +1688,9 @@
|
|||||||
<target state="translated">Avaliação</target>
|
<target state="translated">Avaliação</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Horas-alvo</target>
|
<target state="translated">Horas-alvo</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">Os valores configurados aqui são usados para calcular a hora da conta.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
||||||
<source>work_times_should.none_configured</source>
|
<source>work_times_should.none_configured</source>
|
||||||
<target state="translated">Nenhuma hora-alvo foi configurada para esse utilizador nas configurações do contrato de trabalho.</target>
|
<target state="translated">Nenhuma hora-alvo foi configurada para esse utilizador nas configurações do contrato de trabalho.</target>
|
||||||
|
|||||||
@@ -1496,13 +1496,9 @@
|
|||||||
<target state="translated">Horários de trabalho da conta</target>
|
<target state="translated">Horários de trabalho da conta</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Horas-alvo</target>
|
<target state="translated">Horas-alvo</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">Os valores configurados aqui são usados para calcular a hora da conta.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="31dFXhR" resname="work_times_is">
|
<trans-unit id="31dFXhR" resname="work_times_is">
|
||||||
<source>work_times_is</source>
|
<source>work_times_is</source>
|
||||||
<target state="translated">Is-horas</target>
|
<target state="translated">Is-horas</target>
|
||||||
|
|||||||
@@ -1329,10 +1329,6 @@
|
|||||||
<source>work_times</source>
|
<source>work_times</source>
|
||||||
<target state="translated">Время работы</target>
|
<target state="translated">Время работы</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">Настроенные здесь значения используются для расчета почасового счета.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="31dFXhR" resname="work_times_is">
|
<trans-unit id="31dFXhR" resname="work_times_is">
|
||||||
<source>work_times_is</source>
|
<source>work_times_is</source>
|
||||||
<target state="needs-translation">Ис-часы</target>
|
<target state="needs-translation">Ис-часы</target>
|
||||||
@@ -1406,7 +1402,7 @@
|
|||||||
<target state="translated">Создано</target>
|
<target state="translated">Создано</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Целевые часы</target>
|
<target state="translated">Целевые часы</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="uHrZlbq" resname="work_times_result">
|
<trans-unit id="uHrZlbq" resname="work_times_result">
|
||||||
|
|||||||
@@ -1355,13 +1355,9 @@
|
|||||||
<target state="translated">Táto faktúra je generovaná s touto šablónou.</target>
|
<target state="translated">Táto faktúra je generovaná s touto šablónou.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Cieľové hodiny</target>
|
<target state="translated">Cieľové hodiny</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">Nakonfigurované hodnoty se používajú k výpočtu hodinového účtu.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="gcFtM3o" resname="holiday">
|
<trans-unit id="gcFtM3o" resname="holiday">
|
||||||
<source>holiday</source>
|
<source>holiday</source>
|
||||||
<target state="translated">Dovolenka</target>
|
<target state="translated">Dovolenka</target>
|
||||||
|
|||||||
@@ -1516,7 +1516,7 @@
|
|||||||
<target state="translated">İnceleme</target>
|
<target state="translated">İnceleme</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Hedef saat</target>
|
<target state="translated">Hedef saat</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="mS4SSmt" resname="work_times">
|
<trans-unit id="mS4SSmt" resname="work_times">
|
||||||
@@ -1531,10 +1531,6 @@
|
|||||||
<source>manual_bookings</source>
|
<source>manual_bookings</source>
|
||||||
<target state="translated">Elle rezervasyonlar</target>
|
<target state="translated">Elle rezervasyonlar</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">Burada yapılandırılan değerler saatlik hesabı hesaplamak için kullanılır.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
<trans-unit id="s9NPI3v" resname="work_times_should.none_configured">
|
||||||
<source>work_times_should.none_configured</source>
|
<source>work_times_should.none_configured</source>
|
||||||
<target state="translated">İş sözleşmesi ayarlarında bu kullanıcı için herhangi bir hedef saat yapılandırılmamış.</target>
|
<target state="translated">İş sözleşmesi ayarlarında bu kullanıcı için herhangi bir hedef saat yapılandırılmamış.</target>
|
||||||
|
|||||||
@@ -1426,10 +1426,6 @@
|
|||||||
<source>work_times</source>
|
<source>work_times</source>
|
||||||
<target state="translated">Робочі години</target>
|
<target state="translated">Робочі години</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">Налаштовані тут значення використовуються для погодинного розрахунку облікового запису.</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="EYXzfTO" resname="deleted">
|
<trans-unit id="EYXzfTO" resname="deleted">
|
||||||
<source>deleted</source>
|
<source>deleted</source>
|
||||||
<target state="translated">Видалено</target>
|
<target state="translated">Видалено</target>
|
||||||
@@ -1443,7 +1439,7 @@
|
|||||||
<target state="needs-translation">Після завершення місяця користувач більше не зможе створювати або редагувати час для цього періоду.</target>
|
<target state="needs-translation">Після завершення місяця користувач більше не зможе створювати або редагувати час для цього періоду.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">Цільові години</target>
|
<target state="translated">Цільові години</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="uHrZlbq" resname="work_times_result">
|
<trans-unit id="uHrZlbq" resname="work_times_result">
|
||||||
|
|||||||
@@ -1495,10 +1495,6 @@
|
|||||||
<source>work_times_should.none_configured</source>
|
<source>work_times_should.none_configured</source>
|
||||||
<target state="translated">在雇佣合同设置中未为此用户配置目标工时。</target>
|
<target state="translated">在雇佣合同设置中未为此用户配置目标工时。</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">这里配置的数值用于计算每小时的账户。</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="W8XQTkJ" resname="complete_month.help" xml:space="preserve">
|
<trans-unit id="W8XQTkJ" resname="complete_month.help" xml:space="preserve">
|
||||||
<source>complete_month.help</source>
|
<source>complete_month.help</source>
|
||||||
<target state="translated">这将锁定所选日期之前一年中的所有日期。 用户将无法再创建或编辑锁定期间的时间。</target>
|
<target state="translated">这将锁定所选日期之前一年中的所有日期。 用户将无法再创建或编辑锁定期间的时间。</target>
|
||||||
@@ -1528,7 +1524,7 @@
|
|||||||
<target state="translated">工作时间</target>
|
<target state="translated">工作时间</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">目标小时数</target>
|
<target state="translated">目标小时数</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="31dFXhR" resname="work_times_is">
|
<trans-unit id="31dFXhR" resname="work_times_is">
|
||||||
|
|||||||
@@ -1047,7 +1047,7 @@
|
|||||||
<target state="translated">病假</target>
|
<target state="translated">病假</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1btzNDa" resname="work_times_should">
|
<trans-unit id="1btzNDa" resname="work_times_should">
|
||||||
<source>work_times_should</source>
|
<source>Expected number of hours</source>
|
||||||
<target state="translated">目標時數</target>
|
<target state="translated">目標時數</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="c3d6p33" resname="invoice.total_working_time">
|
<trans-unit id="c3d6p33" resname="invoice.total_working_time">
|
||||||
@@ -1078,10 +1078,6 @@
|
|||||||
<source>api_password.missing_title</source>
|
<source>api_password.missing_title</source>
|
||||||
<target state="translated">無法使用 API</target>
|
<target state="translated">無法使用 API</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="PE2iiaW" resname="work_times_should.help">
|
|
||||||
<source>work_times_should.help</source>
|
|
||||||
<target state="translated">這裡所設定的數值會用於計算鐘點。</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Y0PRG9Z" resname="profile.2fa_confirmation">
|
<trans-unit id="Y0PRG9Z" resname="profile.2fa_confirmation">
|
||||||
<source>profile.2fa_confirmation</source>
|
<source>profile.2fa_confirmation</source>
|
||||||
<target state="translated">請確認代碼,以啟用該帳號的雙重要素驗證。</target>
|
<target state="translated">請確認代碼,以啟用該帳號的雙重要素驗證。</target>
|
||||||
|
|||||||
Reference in New Issue
Block a user