diff --git a/composer.lock b/composer.lock
index b81e32cd..ed39a231 100644
--- a/composer.lock
+++ b/composer.lock
@@ -107,20 +107,21 @@
},
{
"name": "doctrine/annotations",
- "version": "v1.8.0",
+ "version": "1.10.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
- "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc"
+ "reference": "5eb79f3dbdffed6544e1fc287572c0f462bd29bb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/annotations/zipball/904dca4eb10715b92569fbcd79e201d5c349b6bc",
- "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc",
+ "url": "https://api.github.com/repos/doctrine/annotations/zipball/5eb79f3dbdffed6544e1fc287572c0f462bd29bb",
+ "reference": "5eb79f3dbdffed6544e1fc287572c0f462bd29bb",
"shasum": ""
},
"require": {
"doctrine/lexer": "1.*",
+ "ext-tokenizer": "*",
"php": "^7.1"
},
"require-dev": {
@@ -130,7 +131,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.7.x-dev"
+ "dev-master": "1.9.x-dev"
}
},
"autoload": {
@@ -171,7 +172,7 @@
"docblock",
"parser"
],
- "time": "2019-10-01T18:55:10+00:00"
+ "time": "2020-04-02T12:33:25+00:00"
},
{
"name": "doctrine/cache",
@@ -1005,16 +1006,16 @@
},
{
"name": "doctrine/orm",
- "version": "v2.7.1",
+ "version": "v2.7.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/orm.git",
- "reference": "445796af0e873d9bd04f2502d322a7d5009b6846"
+ "reference": "dafe298ce5d0b995ebe1746670704c0a35868a6a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/orm/zipball/445796af0e873d9bd04f2502d322a7d5009b6846",
- "reference": "445796af0e873d9bd04f2502d322a7d5009b6846",
+ "url": "https://api.github.com/repos/doctrine/orm/zipball/dafe298ce5d0b995ebe1746670704c0a35868a6a",
+ "reference": "dafe298ce5d0b995ebe1746670704c0a35868a6a",
"shasum": ""
},
"require": {
@@ -1085,20 +1086,34 @@
"database",
"orm"
],
- "time": "2020-02-15T14:35:56+00:00"
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine/orm",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-19T06:41:02+00:00"
},
{
"name": "doctrine/persistence",
- "version": "1.3.6",
+ "version": "1.3.7",
"source": {
"type": "git",
"url": "https://github.com/doctrine/persistence.git",
- "reference": "5dd3ac5eebef2d0b074daa4440bb18f93132dee4"
+ "reference": "0af483f91bada1c9ded6c2cfd26ab7d5ab2094e0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/persistence/zipball/5dd3ac5eebef2d0b074daa4440bb18f93132dee4",
- "reference": "5dd3ac5eebef2d0b074daa4440bb18f93132dee4",
+ "url": "https://api.github.com/repos/doctrine/persistence/zipball/0af483f91bada1c9ded6c2cfd26ab7d5ab2094e0",
+ "reference": "0af483f91bada1c9ded6c2cfd26ab7d5ab2094e0",
"shasum": ""
},
"require": {
@@ -1106,7 +1121,7 @@
"doctrine/cache": "^1.0",
"doctrine/collections": "^1.0",
"doctrine/event-manager": "^1.0",
- "doctrine/reflection": "^1.1",
+ "doctrine/reflection": "^1.2",
"php": "^7.1"
},
"conflict": {
@@ -1168,20 +1183,34 @@
"orm",
"persistence"
],
- "time": "2020-01-16T22:06:23+00:00"
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fpersistence",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-21T15:13:52+00:00"
},
{
"name": "doctrine/reflection",
- "version": "v1.1.0",
+ "version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/reflection.git",
- "reference": "bc420ead87fdfe08c03ecc3549db603a45b06d4c"
+ "reference": "55e71912dfcd824b2fdd16f2d9afe15684cfce79"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/reflection/zipball/bc420ead87fdfe08c03ecc3549db603a45b06d4c",
- "reference": "bc420ead87fdfe08c03ecc3549db603a45b06d4c",
+ "url": "https://api.github.com/repos/doctrine/reflection/zipball/55e71912dfcd824b2fdd16f2d9afe15684cfce79",
+ "reference": "55e71912dfcd824b2fdd16f2d9afe15684cfce79",
"shasum": ""
},
"require": {
@@ -1202,7 +1231,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "1.2.x-dev"
}
},
"autoload": {
@@ -1246,7 +1275,7 @@
"reflection",
"static"
],
- "time": "2020-01-08T19:53:19+00:00"
+ "time": "2020-03-27T11:06:43+00:00"
},
{
"name": "egulias/email-validator",
@@ -1393,16 +1422,16 @@
},
{
"name": "friendsofsymfony/rest-bundle",
- "version": "2.7.2",
+ "version": "2.7.3",
"source": {
"type": "git",
"url": "https://github.com/FriendsOfSymfony/FOSRestBundle.git",
- "reference": "22fbbc35a2964564bfde765007655174262315c9"
+ "reference": "21a27cebe54f02f6499d78db8978162ce7183112"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/FriendsOfSymfony/FOSRestBundle/zipball/22fbbc35a2964564bfde765007655174262315c9",
- "reference": "22fbbc35a2964564bfde765007655174262315c9",
+ "url": "https://api.github.com/repos/FriendsOfSymfony/FOSRestBundle/zipball/21a27cebe54f02f6499d78db8978162ce7183112",
+ "reference": "21a27cebe54f02f6499d78db8978162ce7183112",
"shasum": ""
},
"require": {
@@ -1466,6 +1495,7 @@
"FOS\\RestBundle\\": ""
},
"exclude-from-classmap": [
+ "Resources/",
"Tests/"
]
},
@@ -1492,7 +1522,7 @@
"keywords": [
"rest"
],
- "time": "2020-03-04T21:18:53+00:00"
+ "time": "2020-03-28T08:23:35+00:00"
},
{
"name": "gedmo/doctrine-extensions",
@@ -2484,7 +2514,7 @@
},
{
"name": "illuminate/cache",
- "version": "v7.2.2",
+ "version": "v7.5.1",
"source": {
"type": "git",
"url": "https://github.com/illuminate/cache.git",
@@ -2535,16 +2565,16 @@
},
{
"name": "illuminate/contracts",
- "version": "v7.2.2",
+ "version": "v7.5.1",
"source": {
"type": "git",
"url": "https://github.com/illuminate/contracts.git",
- "reference": "075d70c8d621e474d10279bf62c19e121be30e95"
+ "reference": "2db7e8a936cc09e396d14902178ea177f4dc42ac"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/contracts/zipball/075d70c8d621e474d10279bf62c19e121be30e95",
- "reference": "075d70c8d621e474d10279bf62c19e121be30e95",
+ "url": "https://api.github.com/repos/illuminate/contracts/zipball/2db7e8a936cc09e396d14902178ea177f4dc42ac",
+ "reference": "2db7e8a936cc09e396d14902178ea177f4dc42ac",
"shasum": ""
},
"require": {
@@ -2575,20 +2605,20 @@
],
"description": "The Illuminate Contracts package.",
"homepage": "https://laravel.com",
- "time": "2020-03-16T13:40:39+00:00"
+ "time": "2020-04-04T15:13:36+00:00"
},
{
"name": "illuminate/support",
- "version": "v7.2.2",
+ "version": "v7.5.1",
"source": {
"type": "git",
"url": "https://github.com/illuminate/support.git",
- "reference": "91066b0b829ea0ab1315eeaefbe7f191cc31d7a1"
+ "reference": "99b3538c63b3ce5374d5f8fad181fac07efaedec"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/support/zipball/91066b0b829ea0ab1315eeaefbe7f191cc31d7a1",
- "reference": "91066b0b829ea0ab1315eeaefbe7f191cc31d7a1",
+ "url": "https://api.github.com/repos/illuminate/support/zipball/99b3538c63b3ce5374d5f8fad181fac07efaedec",
+ "reference": "99b3538c63b3ce5374d5f8fad181fac07efaedec",
"shasum": ""
},
"require": {
@@ -2606,7 +2636,7 @@
"suggest": {
"illuminate/filesystem": "Required to use the composer class (^7.0).",
"moontoast/math": "Required to use ordered UUIDs (^1.1).",
- "ramsey/uuid": "Required to use Str::uuid() (^3.7).",
+ "ramsey/uuid": "Required to use Str::uuid() (^3.7|^4.0).",
"symfony/process": "Required to use the composer class (^5.0).",
"symfony/var-dumper": "Required to use the dd function (^5.0).",
"vlucas/phpdotenv": "Required to use the Env class and env helper (^4.0)."
@@ -2637,7 +2667,7 @@
],
"description": "The Illuminate Support package.",
"homepage": "https://laravel.com",
- "time": "2020-03-17T14:26:31+00:00"
+ "time": "2020-04-06T13:34:26+00:00"
},
{
"name": "intervention/image",
@@ -2818,16 +2848,16 @@
},
{
"name": "jms/serializer",
- "version": "3.5.0",
+ "version": "3.6.0",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/serializer.git",
- "reference": "0e286681396d8139406e596c6c802d37ab0b2124"
+ "reference": "e82527ceddedf8dd988a4ce900f4b14fedf4d6f2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/0e286681396d8139406e596c6c802d37ab0b2124",
- "reference": "0e286681396d8139406e596c6c802d37ab0b2124",
+ "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/e82527ceddedf8dd988a4ce900f4b14fedf4d6f2",
+ "reference": "e82527ceddedf8dd988a4ce900f4b14fedf4d6f2",
"shasum": ""
},
"require": {
@@ -2868,7 +2898,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.5-dev"
+ "dev-master": "3.6-dev"
}
},
"autoload": {
@@ -2899,7 +2929,13 @@
"serialization",
"xml"
],
- "time": "2020-02-22T21:06:01+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/goetas",
+ "type": "github"
+ }
+ ],
+ "time": "2020-03-21T20:26:09+00:00"
},
{
"name": "jms/serializer-bundle",
@@ -3093,16 +3129,16 @@
},
{
"name": "kimai/user-bundle",
- "version": "1.1",
+ "version": "1.2",
"source": {
"type": "git",
"url": "https://github.com/kimai/user-bundle.git",
- "reference": "a52680e843f2a2408df967029c4ea471c41622ee"
+ "reference": "96de1dfb65cc535292c72f0ca31c3e3a0dcf8c93"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/kimai/user-bundle/zipball/a52680e843f2a2408df967029c4ea471c41622ee",
- "reference": "a52680e843f2a2408df967029c4ea471c41622ee",
+ "url": "https://api.github.com/repos/kimai/user-bundle/zipball/96de1dfb65cc535292c72f0ca31c3e3a0dcf8c93",
+ "reference": "96de1dfb65cc535292c72f0ca31c3e3a0dcf8c93",
"shasum": ""
},
"require": {
@@ -3155,7 +3191,7 @@
],
"description": "Kimai UserBundle",
"homepage": "https://www.kimai.org",
- "time": "2020-03-17T18:39:24+00:00"
+ "time": "2020-04-07T23:16:45+00:00"
},
{
"name": "laravolt/avatar",
@@ -3766,16 +3802,16 @@
},
{
"name": "nelmio/api-doc-bundle",
- "version": "v3.6.0",
+ "version": "v3.6.1",
"source": {
"type": "git",
"url": "https://github.com/nelmio/NelmioApiDocBundle.git",
- "reference": "0fcf2d5c9a9b4e230e5bcf6b3f4ad469ba738ed2"
+ "reference": "ab014fe032beb1a7875939ff4162e8d05ad778ab"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/0fcf2d5c9a9b4e230e5bcf6b3f4ad469ba738ed2",
- "reference": "0fcf2d5c9a9b4e230e5bcf6b3f4ad469ba738ed2",
+ "url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/ab014fe032beb1a7875939ff4162e8d05ad778ab",
+ "reference": "ab014fe032beb1a7875939ff4162e8d05ad778ab",
"shasum": ""
},
"require": {
@@ -3850,7 +3886,7 @@
"documentation",
"rest"
],
- "time": "2020-02-23T16:51:12+00:00"
+ "time": "2020-03-31T10:05:08+00:00"
},
{
"name": "nelmio/cors-bundle",
@@ -3912,16 +3948,16 @@
},
{
"name": "nesbot/carbon",
- "version": "2.31.0",
+ "version": "2.32.2",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
- "reference": "bbc0ab53f41a4c6f223c18efcdbd9bc725eb5d2d"
+ "reference": "f10e22cf546704fab1db4ad4b9dedbc5c797a0dc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bbc0ab53f41a4c6f223c18efcdbd9bc725eb5d2d",
- "reference": "bbc0ab53f41a4c6f223c18efcdbd9bc725eb5d2d",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/f10e22cf546704fab1db4ad4b9dedbc5c797a0dc",
+ "reference": "f10e22cf546704fab1db4ad4b9dedbc5c797a0dc",
"shasum": ""
},
"require": {
@@ -3930,6 +3966,7 @@
"symfony/translation": "^3.4 || ^4.0 || ^5.0"
},
"require-dev": {
+ "doctrine/orm": "^2.7",
"friendsofphp/php-cs-fixer": "^2.14 || ^3.0",
"kylekatarnls/multi-tester": "^1.1",
"phpmd/phpmd": "^2.8",
@@ -3978,7 +4015,17 @@
"datetime",
"time"
],
- "time": "2020-03-01T11:11:58+00:00"
+ "funding": [
+ {
+ "url": "https://opencollective.com/Carbon",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-31T13:43:19+00:00"
},
{
"name": "ocramius/package-versions",
@@ -4856,16 +4903,16 @@
},
{
"name": "psr/log",
- "version": "1.1.2",
+ "version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
- "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801"
+ "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801",
- "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
+ "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
"shasum": ""
},
"require": {
@@ -4899,7 +4946,7 @@
"psr",
"psr-3"
],
- "time": "2019-11-01T11:05:21+00:00"
+ "time": "2020-03-23T09:12:05+00:00"
},
{
"name": "psr/simple-cache",
@@ -5029,25 +5076,25 @@
},
{
"name": "sensio/framework-extra-bundle",
- "version": "v5.5.3",
+ "version": "v5.5.4",
"source": {
"type": "git",
"url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git",
- "reference": "98f0807137b13d0acfdf3c255a731516e97015de"
+ "reference": "d0585d4825a87a5030ca8cd34adb4a17e1066c17"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/98f0807137b13d0acfdf3c255a731516e97015de",
- "reference": "98f0807137b13d0acfdf3c255a731516e97015de",
+ "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/d0585d4825a87a5030ca8cd34adb4a17e1066c17",
+ "reference": "d0585d4825a87a5030ca8cd34adb4a17e1066c17",
"shasum": ""
},
"require": {
"doctrine/annotations": "^1.0",
"php": ">=7.1.3",
- "symfony/config": "^4.3|^5.0",
- "symfony/dependency-injection": "^4.3|^5.0",
- "symfony/framework-bundle": "^4.3|^5.0",
- "symfony/http-kernel": "^4.3|^5.0"
+ "symfony/config": "^4.4|^5.0",
+ "symfony/dependency-injection": "^4.4|^5.0",
+ "symfony/framework-bundle": "^4.4|^5.0",
+ "symfony/http-kernel": "^4.4|^5.0"
},
"conflict": {
"doctrine/doctrine-cache-bundle": "<1.3.1"
@@ -5056,24 +5103,19 @@
"doctrine/doctrine-bundle": "^1.11|^2.0",
"doctrine/orm": "^2.5",
"nyholm/psr7": "^1.1",
- "symfony/browser-kit": "^4.3|^5.0",
- "symfony/dom-crawler": "^4.3|^5.0",
- "symfony/expression-language": "^4.3|^5.0",
- "symfony/finder": "^4.3|^5.0",
+ "symfony/browser-kit": "^4.4|^5.0",
+ "symfony/dom-crawler": "^4.4|^5.0",
+ "symfony/expression-language": "^4.4|^5.0",
+ "symfony/finder": "^4.4|^5.0",
"symfony/monolog-bridge": "^4.0|^5.0",
"symfony/monolog-bundle": "^3.2",
"symfony/phpunit-bridge": "^4.3.5|^5.0",
"symfony/psr-http-message-bridge": "^1.1",
- "symfony/security-bundle": "^4.3|^5.0",
- "symfony/twig-bundle": "^4.3|^5.0",
- "symfony/yaml": "^4.3|^5.0",
+ "symfony/security-bundle": "^4.4|^5.0",
+ "symfony/twig-bundle": "^4.4|^5.0",
+ "symfony/yaml": "^4.4|^5.0",
"twig/twig": "^1.34|^2.4|^3.0"
},
- "suggest": {
- "symfony/expression-language": "",
- "symfony/psr-http-message-bridge": "To use the PSR-7 converters",
- "symfony/security-bundle": ""
- },
"type": "symfony-bundle",
"extra": {
"branch-alias": {
@@ -5103,20 +5145,20 @@
"annotations",
"controllers"
],
- "time": "2019-12-27T08:57:19+00:00"
+ "time": "2020-04-06T12:20:39+00:00"
},
{
"name": "setasign/fpdi",
- "version": "v2.3.1",
+ "version": "v2.3.2",
"source": {
"type": "git",
"url": "https://github.com/Setasign/FPDI.git",
- "reference": "f58e64e51f61922d4c3ba4c6c91366ac520cb442"
+ "reference": "527761458f504882ab844f15754523825647f291"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Setasign/FPDI/zipball/f58e64e51f61922d4c3ba4c6c91366ac520cb442",
- "reference": "f58e64e51f61922d4c3ba4c6c91366ac520cb442",
+ "url": "https://api.github.com/repos/Setasign/FPDI/zipball/527761458f504882ab844f15754523825647f291",
+ "reference": "527761458f504882ab844f15754523825647f291",
"shasum": ""
},
"require": {
@@ -5133,10 +5175,7 @@
"tecnickcom/tcpdf": "~6.2"
},
"suggest": {
- "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured.",
- "setasign/fpdi-fpdf": "Use this package to automatically evaluate dependencies to FPDF.",
- "setasign/fpdi-tcpdf": "Use this package to automatically evaluate dependencies to TCPDF.",
- "setasign/fpdi-tfpdf": "Use this package to automatically evaluate dependencies to tFPDF."
+ "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured."
},
"type": "library",
"autoload": {
@@ -5167,20 +5206,26 @@
"fpdi",
"pdf"
],
- "time": "2020-03-10T15:28:15+00:00"
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/setasign/fpdi",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-23T15:53:59+00:00"
},
{
"name": "symfony/amazon-mailer",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/amazon-mailer.git",
- "reference": "ac5202e52d676a0a4a99a8dc67d03590692e5306"
+ "reference": "97089e9e80aeb7404541907d99dc1226a8a6c3b8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/amazon-mailer/zipball/ac5202e52d676a0a4a99a8dc67d03590692e5306",
- "reference": "ac5202e52d676a0a4a99a8dc67d03590692e5306",
+ "url": "https://api.github.com/repos/symfony/amazon-mailer/zipball/97089e9e80aeb7404541907d99dc1226a8a6c3b8",
+ "reference": "97089e9e80aeb7404541907d99dc1226a8a6c3b8",
"shasum": ""
},
"require": {
@@ -5220,20 +5265,34 @@
],
"description": "Symfony Amazon Mailer Bridge",
"homepage": "https://symfony.com",
- "time": "2020-01-08T17:36:30+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/asset",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/asset.git",
- "reference": "5c1afa35f0ff6a4fa5d037b4a5ef1bf60513e65b"
+ "reference": "e3574559efcb51601022fa46fd88dd13a8febdc2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/asset/zipball/5c1afa35f0ff6a4fa5d037b4a5ef1bf60513e65b",
- "reference": "5c1afa35f0ff6a4fa5d037b4a5ef1bf60513e65b",
+ "url": "https://api.github.com/repos/symfony/asset/zipball/e3574559efcb51601022fa46fd88dd13a8febdc2",
+ "reference": "e3574559efcb51601022fa46fd88dd13a8febdc2",
"shasum": ""
},
"require": {
@@ -5276,20 +5335,34 @@
],
"description": "Symfony Asset Component",
"homepage": "https://symfony.com",
- "time": "2020-02-24T13:10:00+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/cache",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
- "reference": "28511cbd8c760a19f4b4b70961d2cd957733b3d9"
+ "reference": "f777b570291aebe51081b9827e05f3a747665e87"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/cache/zipball/28511cbd8c760a19f4b4b70961d2cd957733b3d9",
- "reference": "28511cbd8c760a19f4b4b70961d2cd957733b3d9",
+ "url": "https://api.github.com/repos/symfony/cache/zipball/f777b570291aebe51081b9827e05f3a747665e87",
+ "reference": "f777b570291aebe51081b9827e05f3a747665e87",
"shasum": ""
},
"require": {
@@ -5355,7 +5428,21 @@
"caching",
"psr6"
],
- "time": "2020-02-20T16:31:44+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/cache-contracts",
@@ -5417,16 +5504,16 @@
},
{
"name": "symfony/config",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
- "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9"
+ "reference": "3f4a3de1af498ed0ea653d4dc2317794144e6ca4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/config/zipball/cbfef5ae91ccd3b06621c18d58cd355c68c87ae9",
- "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9",
+ "url": "https://api.github.com/repos/symfony/config/zipball/3f4a3de1af498ed0ea653d4dc2317794144e6ca4",
+ "reference": "3f4a3de1af498ed0ea653d4dc2317794144e6ca4",
"shasum": ""
},
"require": {
@@ -5477,20 +5564,34 @@
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
- "time": "2020-02-04T09:32:40+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/console",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9"
+ "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/4fa15ae7be74e53f6ec8c83ed403b97e23b665e9",
- "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9",
+ "url": "https://api.github.com/repos/symfony/console/zipball/10bb3ee3c97308869d53b3e3d03f6ac23ff985f7",
+ "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7",
"shasum": ""
},
"require": {
@@ -5553,20 +5654,34 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
- "time": "2020-02-24T13:10:00+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T11:41:10+00:00"
},
{
"name": "symfony/css-selector",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
- "reference": "d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22"
+ "reference": "afc26133a6fbdd4f8842e38893e0ee4685c7c94b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22",
- "reference": "d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/afc26133a6fbdd4f8842e38893e0ee4685c7c94b",
+ "reference": "afc26133a6fbdd4f8842e38893e0ee4685c7c94b",
"shasum": ""
},
"require": {
@@ -5606,20 +5721,34 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
- "time": "2020-02-04T09:01:01+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/debug",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
- "reference": "a980d87a659648980d89193fd8b7a7ca89d97d21"
+ "reference": "346636d2cae417992ecfd761979b2ab98b339a45"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/debug/zipball/a980d87a659648980d89193fd8b7a7ca89d97d21",
- "reference": "a980d87a659648980d89193fd8b7a7ca89d97d21",
+ "url": "https://api.github.com/repos/symfony/debug/zipball/346636d2cae417992ecfd761979b2ab98b339a45",
+ "reference": "346636d2cae417992ecfd761979b2ab98b339a45",
"shasum": ""
},
"require": {
@@ -5662,20 +5791,34 @@
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
- "time": "2020-02-23T14:41:43+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/dependency-injection",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
- "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342"
+ "reference": "755b18859be26b90f4bf63753432d3387458bf31"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ebb2e882e8c9e2eb990aa61ddcd389848466e342",
- "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342",
+ "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/755b18859be26b90f4bf63753432d3387458bf31",
+ "reference": "755b18859be26b90f4bf63753432d3387458bf31",
"shasum": ""
},
"require": {
@@ -5735,20 +5878,34 @@
],
"description": "Symfony DependencyInjection Component",
"homepage": "https://symfony.com",
- "time": "2020-02-29T09:50:10+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T10:09:30+00:00"
},
{
"name": "symfony/doctrine-bridge",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/doctrine-bridge.git",
- "reference": "1b4564758fe91f5d53dfbdfd9007d15e43fa465f"
+ "reference": "7889c9df9fe4d95042c2f6e79901c9e6517343d9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/1b4564758fe91f5d53dfbdfd9007d15e43fa465f",
- "reference": "1b4564758fe91f5d53dfbdfd9007d15e43fa465f",
+ "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/7889c9df9fe4d95042c2f6e79901c9e6517343d9",
+ "reference": "7889c9df9fe4d95042c2f6e79901c9e6517343d9",
"shasum": ""
},
"require": {
@@ -5829,20 +5986,34 @@
],
"description": "Symfony Doctrine Bridge",
"homepage": "https://symfony.com",
- "time": "2020-02-24T17:16:47+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/dotenv",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/dotenv.git",
- "reference": "9bba981ecb1f57c04520d4165b3e6a17ac49319f"
+ "reference": "a78e698cfb8aca8ef6814639eb5ffc17180a4326"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dotenv/zipball/9bba981ecb1f57c04520d4165b3e6a17ac49319f",
- "reference": "9bba981ecb1f57c04520d4165b3e6a17ac49319f",
+ "url": "https://api.github.com/repos/symfony/dotenv/zipball/a78e698cfb8aca8ef6814639eb5ffc17180a4326",
+ "reference": "a78e698cfb8aca8ef6814639eb5ffc17180a4326",
"shasum": ""
},
"require": {
@@ -5886,20 +6057,34 @@
"env",
"environment"
],
- "time": "2020-02-29T10:04:02+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/error-handler",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "89aa4b9ac6f1f35171b8621b24f60477312085be"
+ "reference": "7e9828fc98aa1cf27b422fe478a84f5b0abb7358"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/89aa4b9ac6f1f35171b8621b24f60477312085be",
- "reference": "89aa4b9ac6f1f35171b8621b24f60477312085be",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/7e9828fc98aa1cf27b422fe478a84f5b0abb7358",
+ "reference": "7e9828fc98aa1cf27b422fe478a84f5b0abb7358",
"shasum": ""
},
"require": {
@@ -5942,20 +6127,34 @@
],
"description": "Symfony ErrorHandler Component",
"homepage": "https://symfony.com",
- "time": "2020-02-26T11:45:31+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T14:07:33+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d"
+ "reference": "abc8e3618bfdb55e44c8c6a00abd333f831bbfed"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4ad8e149799d3128621a3a1f70e92b9897a8930d",
- "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/abc8e3618bfdb55e44c8c6a00abd333f831bbfed",
+ "reference": "abc8e3618bfdb55e44c8c6a00abd333f831bbfed",
"shasum": ""
},
"require": {
@@ -6012,7 +6211,21 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
- "time": "2020-02-04T09:32:40+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -6074,16 +6287,16 @@
},
{
"name": "symfony/expression-language",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/expression-language.git",
- "reference": "70649e0abcac6ad1109f6243886bca0445e278b9"
+ "reference": "c4171e39e6cfc72e2bedb44922310d7d2bd2ab91"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/expression-language/zipball/70649e0abcac6ad1109f6243886bca0445e278b9",
- "reference": "70649e0abcac6ad1109f6243886bca0445e278b9",
+ "url": "https://api.github.com/repos/symfony/expression-language/zipball/c4171e39e6cfc72e2bedb44922310d7d2bd2ab91",
+ "reference": "c4171e39e6cfc72e2bedb44922310d7d2bd2ab91",
"shasum": ""
},
"require": {
@@ -6121,20 +6334,34 @@
],
"description": "Symfony ExpressionLanguage Component",
"homepage": "https://symfony.com",
- "time": "2020-02-24T13:10:00+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/filesystem",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd"
+ "reference": "fe297193bf2e6866ed900ed2d5869362768df6a7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/266c9540b475f26122b61ef8b23dd9198f5d1cfd",
- "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/fe297193bf2e6866ed900ed2d5869362768df6a7",
+ "reference": "fe297193bf2e6866ed900ed2d5869362768df6a7",
"shasum": ""
},
"require": {
@@ -6171,20 +6398,34 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
- "time": "2020-01-21T08:20:44+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/finder",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357"
+ "reference": "5729f943f9854c5781984ed4907bbb817735776b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/ea69c129aed9fdeca781d4b77eb20b62cf5d5357",
- "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/5729f943f9854c5781984ed4907bbb817735776b",
+ "reference": "5729f943f9854c5781984ed4907bbb817735776b",
"shasum": ""
},
"require": {
@@ -6220,7 +6461,21 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
- "time": "2020-02-14T07:42:58+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/flex",
@@ -6273,16 +6528,16 @@
},
{
"name": "symfony/form",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/form.git",
- "reference": "677927dad3b9f93117db62fc1df4de82027dc282"
+ "reference": "6dfd2d0f47b9a4abee73807f7172e2b8a0006571"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/form/zipball/677927dad3b9f93117db62fc1df4de82027dc282",
- "reference": "677927dad3b9f93117db62fc1df4de82027dc282",
+ "url": "https://api.github.com/repos/symfony/form/zipball/6dfd2d0f47b9a4abee73807f7172e2b8a0006571",
+ "reference": "6dfd2d0f47b9a4abee73807f7172e2b8a0006571",
"shasum": ""
},
"require": {
@@ -6353,20 +6608,34 @@
],
"description": "Symfony Form Component",
"homepage": "https://symfony.com",
- "time": "2020-02-29T10:05:28+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/framework-bundle",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/framework-bundle.git",
- "reference": "df5528926e6e1954975f3d73a91f029ba3d9c76b"
+ "reference": "80cdda836cfbe3ccb2bdd4a974f632473f0807a6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/df5528926e6e1954975f3d73a91f029ba3d9c76b",
- "reference": "df5528926e6e1954975f3d73a91f029ba3d9c76b",
+ "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/80cdda836cfbe3ccb2bdd4a974f632473f0807a6",
+ "reference": "80cdda836cfbe3ccb2bdd4a974f632473f0807a6",
"shasum": ""
},
"require": {
@@ -6484,20 +6753,34 @@
],
"description": "Symfony FrameworkBundle",
"homepage": "https://symfony.com",
- "time": "2020-02-28T13:15:16+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T11:41:10+00:00"
},
{
"name": "symfony/google-mailer",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/google-mailer.git",
- "reference": "7dd2d4722ed7e9fe3be27b0afe1865dc81643815"
+ "reference": "09cb25c097470eee608069a016f8b58cf52f8f23"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/google-mailer/zipball/7dd2d4722ed7e9fe3be27b0afe1865dc81643815",
- "reference": "7dd2d4722ed7e9fe3be27b0afe1865dc81643815",
+ "url": "https://api.github.com/repos/symfony/google-mailer/zipball/09cb25c097470eee608069a016f8b58cf52f8f23",
+ "reference": "09cb25c097470eee608069a016f8b58cf52f8f23",
"shasum": ""
},
"require": {
@@ -6537,20 +6820,34 @@
],
"description": "Symfony Google Mailer Bridge",
"homepage": "https://symfony.com",
- "time": "2020-01-04T13:00:46+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/http-client",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
- "reference": "65ccb6ca4a5a7bf7c244b8a28195fa188521b3d2"
+ "reference": "9a8f5c968dc68d58044f8e9ff39d03074489b55d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client/zipball/65ccb6ca4a5a7bf7c244b8a28195fa188521b3d2",
- "reference": "65ccb6ca4a5a7bf7c244b8a28195fa188521b3d2",
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/9a8f5c968dc68d58044f8e9ff39d03074489b55d",
+ "reference": "9a8f5c968dc68d58044f8e9ff39d03074489b55d",
"shasum": ""
},
"require": {
@@ -6605,7 +6902,21 @@
],
"description": "Symfony HttpClient component",
"homepage": "https://symfony.com",
- "time": "2020-02-25T13:55:11+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -6666,16 +6977,16 @@
},
{
"name": "symfony/http-foundation",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648"
+ "reference": "62f92509c9abfd1f73e17b8cf1b72c0bdac6611b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7e41b4fcad4619535f45f8bfa7744c4f384e1648",
- "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/62f92509c9abfd1f73e17b8cf1b72c0bdac6611b",
+ "reference": "62f92509c9abfd1f73e17b8cf1b72c0bdac6611b",
"shasum": ""
},
"require": {
@@ -6717,20 +7028,34 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
- "time": "2020-02-13T19:40:01+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T14:07:33+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "8c8734486dada83a6041ab744709bdc1651a8462"
+ "reference": "f356a489e51856b99908005eb7f2c51a1dfc95dc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/8c8734486dada83a6041ab744709bdc1651a8462",
- "reference": "8c8734486dada83a6041ab744709bdc1651a8462",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f356a489e51856b99908005eb7f2c51a1dfc95dc",
+ "reference": "f356a489e51856b99908005eb7f2c51a1dfc95dc",
"shasum": ""
},
"require": {
@@ -6807,20 +7132,34 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "https://symfony.com",
- "time": "2020-02-29T10:31:38+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T14:59:15+00:00"
},
{
"name": "symfony/inflector",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/inflector.git",
- "reference": "f419ab2853cc00471ffd7fc18e544b5f5a90adb1"
+ "reference": "53cfa47fe9142f39b5605df67bada3893dd4f46c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/inflector/zipball/f419ab2853cc00471ffd7fc18e544b5f5a90adb1",
- "reference": "f419ab2853cc00471ffd7fc18e544b5f5a90adb1",
+ "url": "https://api.github.com/repos/symfony/inflector/zipball/53cfa47fe9142f39b5605df67bada3893dd4f46c",
+ "reference": "53cfa47fe9142f39b5605df67bada3893dd4f46c",
"shasum": ""
},
"require": {
@@ -6865,20 +7204,34 @@
"symfony",
"words"
],
- "time": "2020-01-04T13:00:46+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/intl",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/intl.git",
- "reference": "f675f139e20b9e0ff05bac662c081fe9ef7b2f88"
+ "reference": "63238a53b1cf0cd3e2b0b22cabc7c0b6f3fd4562"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/intl/zipball/f675f139e20b9e0ff05bac662c081fe9ef7b2f88",
- "reference": "f675f139e20b9e0ff05bac662c081fe9ef7b2f88",
+ "url": "https://api.github.com/repos/symfony/intl/zipball/63238a53b1cf0cd3e2b0b22cabc7c0b6f3fd4562",
+ "reference": "63238a53b1cf0cd3e2b0b22cabc7c0b6f3fd4562",
"shasum": ""
},
"require": {
@@ -6940,20 +7293,34 @@
"l10n",
"localization"
],
- "time": "2020-02-04T09:32:40+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/mailchimp-mailer",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailchimp-mailer.git",
- "reference": "2eaf7d50595c4902c693349c70b917d150b25ba7"
+ "reference": "51fe2f8366be279c8ae3a1f643169daa1c71b5ca"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mailchimp-mailer/zipball/2eaf7d50595c4902c693349c70b917d150b25ba7",
- "reference": "2eaf7d50595c4902c693349c70b917d150b25ba7",
+ "url": "https://api.github.com/repos/symfony/mailchimp-mailer/zipball/51fe2f8366be279c8ae3a1f643169daa1c71b5ca",
+ "reference": "51fe2f8366be279c8ae3a1f643169daa1c71b5ca",
"shasum": ""
},
"require": {
@@ -6993,20 +7360,34 @@
],
"description": "Symfony Mailchimp Mailer Bridge",
"homepage": "https://symfony.com",
- "time": "2020-02-04T22:28:01+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/mailer",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailer.git",
- "reference": "f79a2cc091c14c2d78cade2f8b772f568fb63c43"
+ "reference": "449856e70ccb1c91ebd75d6fb287ffe21be9fafe"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mailer/zipball/f79a2cc091c14c2d78cade2f8b772f568fb63c43",
- "reference": "f79a2cc091c14c2d78cade2f8b772f568fb63c43",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/449856e70ccb1c91ebd75d6fb287ffe21be9fafe",
+ "reference": "449856e70ccb1c91ebd75d6fb287ffe21be9fafe",
"shasum": ""
},
"require": {
@@ -7061,20 +7442,34 @@
],
"description": "Symfony Mailer Component",
"homepage": "https://symfony.com",
- "time": "2020-02-07T16:56:37+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/mailgun-mailer",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailgun-mailer.git",
- "reference": "de87b7fc1fd650fa5437eda8aedfa7cd63a76a03"
+ "reference": "58bfc335d089e3f362fb54c4ed876f569e6eb77c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/de87b7fc1fd650fa5437eda8aedfa7cd63a76a03",
- "reference": "de87b7fc1fd650fa5437eda8aedfa7cd63a76a03",
+ "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/58bfc335d089e3f362fb54c4ed876f569e6eb77c",
+ "reference": "58bfc335d089e3f362fb54c4ed876f569e6eb77c",
"shasum": ""
},
"require": {
@@ -7114,20 +7509,34 @@
],
"description": "Symfony Mailgun Mailer Bridge",
"homepage": "https://symfony.com",
- "time": "2020-01-08T17:36:30+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/mime",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
- "reference": "304db017bafd71c122cd5223a9ac2d03dc24da32"
+ "reference": "6dde9dc70155e91b850b1d009d1f841c54bc4aba"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mime/zipball/304db017bafd71c122cd5223a9ac2d03dc24da32",
- "reference": "304db017bafd71c122cd5223a9ac2d03dc24da32",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/6dde9dc70155e91b850b1d009d1f841c54bc4aba",
+ "reference": "6dde9dc70155e91b850b1d009d1f841c54bc4aba",
"shasum": ""
},
"require": {
@@ -7176,20 +7585,34 @@
"mime",
"mime-type"
],
- "time": "2020-02-04T09:32:40+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/monolog-bridge",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/monolog-bridge.git",
- "reference": "b14ee2f0488b3ad879fd000bc3dcce1cd2f6cfff"
+ "reference": "2df4a774d99ae6e87c8b67891430f935312be412"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/b14ee2f0488b3ad879fd000bc3dcce1cd2f6cfff",
- "reference": "b14ee2f0488b3ad879fd000bc3dcce1cd2f6cfff",
+ "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/2df4a774d99ae6e87c8b67891430f935312be412",
+ "reference": "2df4a774d99ae6e87c8b67891430f935312be412",
"shasum": ""
},
"require": {
@@ -7243,7 +7666,21 @@
],
"description": "Symfony Monolog Bridge",
"homepage": "https://symfony.com",
- "time": "2020-02-04T09:32:40+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/monolog-bundle",
@@ -7310,16 +7747,16 @@
},
{
"name": "symfony/options-resolver",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
- "reference": "9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0"
+ "reference": "9072131b5e6e21203db3249c7db26b52897bc73e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/options-resolver/zipball/9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0",
- "reference": "9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/9072131b5e6e21203db3249c7db26b52897bc73e",
+ "reference": "9072131b5e6e21203db3249c7db26b52897bc73e",
"shasum": ""
},
"require": {
@@ -7360,20 +7797,34 @@
"configuration",
"options"
],
- "time": "2020-01-04T13:00:46+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.14.0",
+ "version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38"
+ "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38",
- "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14",
+ "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14",
"shasum": ""
},
"require": {
@@ -7385,7 +7836,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.14-dev"
+ "dev-master": "1.15-dev"
}
},
"autoload": {
@@ -7418,20 +7869,34 @@
"polyfill",
"portable"
],
- "time": "2020-01-13T11:15:53+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-02-27T09:26:54+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
- "version": "v1.14.0",
+ "version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "699871accfb394eb6f34ba1210df437f79b14d58"
+ "reference": "b6786f69dd7b062390582f20520ab4918283217e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/699871accfb394eb6f34ba1210df437f79b14d58",
- "reference": "699871accfb394eb6f34ba1210df437f79b14d58",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b6786f69dd7b062390582f20520ab4918283217e",
+ "reference": "b6786f69dd7b062390582f20520ab4918283217e",
"shasum": ""
},
"require": {
@@ -7443,7 +7908,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.14-dev"
+ "dev-master": "1.15-dev"
}
},
"autoload": {
@@ -7478,20 +7943,34 @@
"portable",
"shim"
],
- "time": "2020-01-13T11:15:53+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-09T19:04:49+00:00"
},
{
"name": "symfony/polyfill-intl-icu",
- "version": "v1.14.0",
+ "version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-icu.git",
- "reference": "727b3bb5bfa7ca9eeb86416784cf1c08a6289b86"
+ "reference": "9c281272735eb66476e0fa7381e03fb0d4b60197"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/727b3bb5bfa7ca9eeb86416784cf1c08a6289b86",
- "reference": "727b3bb5bfa7ca9eeb86416784cf1c08a6289b86",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/9c281272735eb66476e0fa7381e03fb0d4b60197",
+ "reference": "9c281272735eb66476e0fa7381e03fb0d4b60197",
"shasum": ""
},
"require": {
@@ -7504,7 +7983,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.14-dev"
+ "dev-master": "1.15-dev"
}
},
"autoload": {
@@ -7536,20 +8015,34 @@
"portable",
"shim"
],
- "time": "2020-01-13T11:15:53+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-02-27T09:26:54+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
- "version": "v1.14.0",
+ "version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
- "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a"
+ "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6842f1a39cf7d580655688069a03dd7cd83d244a",
- "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf",
+ "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf",
"shasum": ""
},
"require": {
@@ -7563,7 +8056,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.14-dev"
+ "dev-master": "1.15-dev"
}
},
"autoload": {
@@ -7598,20 +8091,34 @@
"portable",
"shim"
],
- "time": "2020-01-17T12:01:36+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-09T19:04:49+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
- "version": "v1.14.0",
+ "version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "e62b4845992282d14037950542fc8e8650ae2a65"
+ "reference": "e62715f03f90dd8d2f3eb5daa21b4d19d71aebde"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/e62b4845992282d14037950542fc8e8650ae2a65",
- "reference": "e62b4845992282d14037950542fc8e8650ae2a65",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/e62715f03f90dd8d2f3eb5daa21b4d19d71aebde",
+ "reference": "e62715f03f90dd8d2f3eb5daa21b4d19d71aebde",
"shasum": ""
},
"require": {
@@ -7623,7 +8130,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.14-dev"
+ "dev-master": "1.15-dev"
}
},
"autoload": {
@@ -7661,20 +8168,34 @@
"portable",
"shim"
],
- "time": "2020-01-13T11:15:53+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-02-27T09:26:54+00:00"
},
{
"name": "symfony/polyfill-php73",
- "version": "v1.14.0",
+ "version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
- "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675"
+ "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/5e66a0fa1070bf46bec4bea7962d285108edd675",
- "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7",
+ "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7",
"shasum": ""
},
"require": {
@@ -7683,7 +8204,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.14-dev"
+ "dev-master": "1.15-dev"
}
},
"autoload": {
@@ -7719,20 +8240,34 @@
"portable",
"shim"
],
- "time": "2020-01-13T11:15:53+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-02-27T09:26:54+00:00"
},
{
"name": "symfony/postmark-mailer",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/postmark-mailer.git",
- "reference": "afaaca6f9652ca82086392b9955ed94ca6fc0a9e"
+ "reference": "8238aab3973427af95ed71d607f383986ab9098b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/postmark-mailer/zipball/afaaca6f9652ca82086392b9955ed94ca6fc0a9e",
- "reference": "afaaca6f9652ca82086392b9955ed94ca6fc0a9e",
+ "url": "https://api.github.com/repos/symfony/postmark-mailer/zipball/8238aab3973427af95ed71d607f383986ab9098b",
+ "reference": "8238aab3973427af95ed71d607f383986ab9098b",
"shasum": ""
},
"require": {
@@ -7772,20 +8307,34 @@
],
"description": "Symfony Postmark Mailer Bridge",
"homepage": "https://symfony.com",
- "time": "2020-01-30T10:46:32+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/property-access",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/property-access.git",
- "reference": "090b4bc92ded1ec512f7e2ed1691210769dffdb3"
+ "reference": "75cbf0f388d82685ce06515951397bc1370901d7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/property-access/zipball/090b4bc92ded1ec512f7e2ed1691210769dffdb3",
- "reference": "090b4bc92ded1ec512f7e2ed1691210769dffdb3",
+ "url": "https://api.github.com/repos/symfony/property-access/zipball/75cbf0f388d82685ce06515951397bc1370901d7",
+ "reference": "75cbf0f388d82685ce06515951397bc1370901d7",
"shasum": ""
},
"require": {
@@ -7839,20 +8388,34 @@
"property path",
"reflection"
],
- "time": "2020-01-04T13:00:46+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/property-info",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/property-info.git",
- "reference": "e6355ba81c738be31c3c3b3cd7929963f98da576"
+ "reference": "b6baecd501adec01a9d68f9c90b83659656065af"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/property-info/zipball/e6355ba81c738be31c3c3b3cd7929963f98da576",
- "reference": "e6355ba81c738be31c3c3b3cd7929963f98da576",
+ "url": "https://api.github.com/repos/symfony/property-info/zipball/b6baecd501adec01a9d68f9c90b83659656065af",
+ "reference": "b6baecd501adec01a9d68f9c90b83659656065af",
"shasum": ""
},
"require": {
@@ -7915,20 +8478,34 @@
"type",
"validator"
],
- "time": "2020-01-04T13:00:46+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/routing",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
- "reference": "4124d621d0e445732520037f888a0456951bde8c"
+ "reference": "0f562fa613e288d7dbae6c63abbc9b33ed75a8f8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/routing/zipball/4124d621d0e445732520037f888a0456951bde8c",
- "reference": "4124d621d0e445732520037f888a0456951bde8c",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/0f562fa613e288d7dbae6c63abbc9b33ed75a8f8",
+ "reference": "0f562fa613e288d7dbae6c63abbc9b33ed75a8f8",
"shasum": ""
},
"require": {
@@ -7991,20 +8568,34 @@
"uri",
"url"
],
- "time": "2020-02-25T12:41:09+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T11:41:10+00:00"
},
{
"name": "symfony/security-bundle",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-bundle.git",
- "reference": "21c4270b37f76e6c3c22f92597f397593ed1c9db"
+ "reference": "1c317cd29a75e4806479241ffd31d8035e243420"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-bundle/zipball/21c4270b37f76e6c3c22f92597f397593ed1c9db",
- "reference": "21c4270b37f76e6c3c22f92597f397593ed1c9db",
+ "url": "https://api.github.com/repos/symfony/security-bundle/zipball/1c317cd29a75e4806479241ffd31d8035e243420",
+ "reference": "1c317cd29a75e4806479241ffd31d8035e243420",
"shasum": ""
},
"require": {
@@ -8074,20 +8665,34 @@
],
"description": "Symfony SecurityBundle",
"homepage": "https://symfony.com",
- "time": "2020-02-26T10:27:30+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T11:41:10+00:00"
},
{
"name": "symfony/security-core",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-core.git",
- "reference": "6251c8e432640106e6f2f045ac3b365f1af36d44"
+ "reference": "e99ad8bcd5d1202a1cff7b3e0e76d9077d81cbe6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-core/zipball/6251c8e432640106e6f2f045ac3b365f1af36d44",
- "reference": "6251c8e432640106e6f2f045ac3b365f1af36d44",
+ "url": "https://api.github.com/repos/symfony/security-core/zipball/e99ad8bcd5d1202a1cff7b3e0e76d9077d81cbe6",
+ "reference": "e99ad8bcd5d1202a1cff7b3e0e76d9077d81cbe6",
"shasum": ""
},
"require": {
@@ -8147,20 +8752,34 @@
],
"description": "Symfony Security Component - Core Library",
"homepage": "https://symfony.com",
- "time": "2020-02-24T13:10:00+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T11:51:53+00:00"
},
{
"name": "symfony/security-csrf",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-csrf.git",
- "reference": "da4664d94164e2b50ce75f2453724c6c33222505"
+ "reference": "286a71ff176e1b0dd071f0e73dcec0970a56634b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-csrf/zipball/da4664d94164e2b50ce75f2453724c6c33222505",
- "reference": "da4664d94164e2b50ce75f2453724c6c33222505",
+ "url": "https://api.github.com/repos/symfony/security-csrf/zipball/286a71ff176e1b0dd071f0e73dcec0970a56634b",
+ "reference": "286a71ff176e1b0dd071f0e73dcec0970a56634b",
"shasum": ""
},
"require": {
@@ -8206,20 +8825,34 @@
],
"description": "Symfony Security Component - CSRF Library",
"homepage": "https://symfony.com",
- "time": "2020-01-04T13:00:46+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/security-guard",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-guard.git",
- "reference": "7dcc99e3fa89387a1914496118b4aa0927f282e1"
+ "reference": "606a741712d8adb49aee9b59d57010724db06797"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-guard/zipball/7dcc99e3fa89387a1914496118b4aa0927f282e1",
- "reference": "7dcc99e3fa89387a1914496118b4aa0927f282e1",
+ "url": "https://api.github.com/repos/symfony/security-guard/zipball/606a741712d8adb49aee9b59d57010724db06797",
+ "reference": "606a741712d8adb49aee9b59d57010724db06797",
"shasum": ""
},
"require": {
@@ -8260,28 +8893,42 @@
],
"description": "Symfony Security Component - Guard",
"homepage": "https://symfony.com",
- "time": "2020-02-24T13:10:00+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/security-http",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-http.git",
- "reference": "c38fe1cd72360dfaa39c82fcb9e4666b041f8834"
+ "reference": "b413064160255c31077bb082d25b7bd89275971b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-http/zipball/c38fe1cd72360dfaa39c82fcb9e4666b041f8834",
- "reference": "c38fe1cd72360dfaa39c82fcb9e4666b041f8834",
+ "url": "https://api.github.com/repos/symfony/security-http/zipball/b413064160255c31077bb082d25b7bd89275971b",
+ "reference": "b413064160255c31077bb082d25b7bd89275971b",
"shasum": ""
},
"require": {
"php": "^7.1.3",
- "symfony/http-foundation": "^3.4|^4.0|^5.0",
+ "symfony/http-foundation": "^3.4.40|^4.4.7|^5.0.7",
"symfony/http-kernel": "^4.4",
"symfony/property-access": "^3.4|^4.0|^5.0",
- "symfony/security-core": "^4.4"
+ "symfony/security-core": "^4.4.7"
},
"conflict": {
"symfony/event-dispatcher": ">=5",
@@ -8326,20 +8973,34 @@
],
"description": "Symfony Security Component - HTTP Integration",
"homepage": "https://symfony.com",
- "time": "2020-02-26T10:27:30+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T11:51:53+00:00"
},
{
"name": "symfony/sendgrid-mailer",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/sendgrid-mailer.git",
- "reference": "96a6c3081c9fb47a6784320147370939626aa6cd"
+ "reference": "fdf3c73f60164984f4f933c3ce0cc5dc44e7ed7b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/sendgrid-mailer/zipball/96a6c3081c9fb47a6784320147370939626aa6cd",
- "reference": "96a6c3081c9fb47a6784320147370939626aa6cd",
+ "url": "https://api.github.com/repos/symfony/sendgrid-mailer/zipball/fdf3c73f60164984f4f933c3ce0cc5dc44e7ed7b",
+ "reference": "fdf3c73f60164984f4f933c3ce0cc5dc44e7ed7b",
"shasum": ""
},
"require": {
@@ -8379,20 +9040,34 @@
],
"description": "Symfony Sendgrid Mailer Bridge",
"homepage": "https://symfony.com",
- "time": "2020-01-04T13:10:16+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/serializer",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/serializer.git",
- "reference": "bc76d242fea5ff09c3ca34c90f3bb9341a2470a4"
+ "reference": "2a508a535f2323defb325cf28301064fcbb061b9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/serializer/zipball/bc76d242fea5ff09c3ca34c90f3bb9341a2470a4",
- "reference": "bc76d242fea5ff09c3ca34c90f3bb9341a2470a4",
+ "url": "https://api.github.com/repos/symfony/serializer/zipball/2a508a535f2323defb325cf28301064fcbb061b9",
+ "reference": "2a508a535f2323defb325cf28301064fcbb061b9",
"shasum": ""
},
"require": {
@@ -8461,7 +9136,21 @@
],
"description": "Symfony Serializer Component",
"homepage": "https://symfony.com",
- "time": "2020-02-29T10:05:28+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/service-contracts",
@@ -8523,16 +9212,16 @@
},
{
"name": "symfony/stopwatch",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
- "reference": "abc08d7c48987829bac301347faa10f7e8bbf4fb"
+ "reference": "e0324d3560e4128270e3f08617480d9233d81cfc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/stopwatch/zipball/abc08d7c48987829bac301347faa10f7e8bbf4fb",
- "reference": "abc08d7c48987829bac301347faa10f7e8bbf4fb",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e0324d3560e4128270e3f08617480d9233d81cfc",
+ "reference": "e0324d3560e4128270e3f08617480d9233d81cfc",
"shasum": ""
},
"require": {
@@ -8569,20 +9258,34 @@
],
"description": "Symfony Stopwatch Component",
"homepage": "https://symfony.com",
- "time": "2020-01-04T13:00:46+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/string",
- "version": "v5.0.5",
+ "version": "v5.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "a45ae78382337833e3b0ab3097d1769074950007"
+ "reference": "48a2f4b3597514e6c885c0ddb22d3bbdb60517d0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/a45ae78382337833e3b0ab3097d1769074950007",
- "reference": "a45ae78382337833e3b0ab3097d1769074950007",
+ "url": "https://api.github.com/repos/symfony/string/zipball/48a2f4b3597514e6c885c0ddb22d3bbdb60517d0",
+ "reference": "48a2f4b3597514e6c885c0ddb22d3bbdb60517d0",
"shasum": ""
},
"require": {
@@ -8633,20 +9336,34 @@
"utf-8",
"utf8"
],
- "time": "2020-02-26T22:30:10+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-16T13:02:39+00:00"
},
{
"name": "symfony/translation",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "0a19a77fba20818a969ef03fdaf1602de0546353"
+ "reference": "4e54d336f2eca5facad449d0b0118bb449375b76"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/0a19a77fba20818a969ef03fdaf1602de0546353",
- "reference": "0a19a77fba20818a969ef03fdaf1602de0546353",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/4e54d336f2eca5facad449d0b0118bb449375b76",
+ "reference": "4e54d336f2eca5facad449d0b0118bb449375b76",
"shasum": ""
},
"require": {
@@ -8709,7 +9426,21 @@
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
- "time": "2020-02-04T09:32:40+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/translation-contracts",
@@ -8770,16 +9501,16 @@
},
{
"name": "symfony/twig-bridge",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
- "reference": "e76e963d8aeb1370a1236ca237a8028141402fc0"
+ "reference": "bef4da6724c5a89bb3408d3bc785be7cd5b9efed"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/e76e963d8aeb1370a1236ca237a8028141402fc0",
- "reference": "e76e963d8aeb1370a1236ca237a8028141402fc0",
+ "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/bef4da6724c5a89bb3408d3bc785be7cd5b9efed",
+ "reference": "bef4da6724c5a89bb3408d3bc785be7cd5b9efed",
"shasum": ""
},
"require": {
@@ -8869,20 +9600,34 @@
],
"description": "Symfony Twig Bridge",
"homepage": "https://symfony.com",
- "time": "2020-02-14T08:28:51+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/twig-bundle",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bundle.git",
- "reference": "d3e3e46e9e683e946746219570299ba07506260a"
+ "reference": "44e3e82867bf4dcf52732dd7e0c83826f9da1095"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/d3e3e46e9e683e946746219570299ba07506260a",
- "reference": "d3e3e46e9e683e946746219570299ba07506260a",
+ "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/44e3e82867bf4dcf52732dd7e0c83826f9da1095",
+ "reference": "44e3e82867bf4dcf52732dd7e0c83826f9da1095",
"shasum": ""
},
"require": {
@@ -8944,20 +9689,34 @@
],
"description": "Symfony TwigBundle",
"homepage": "https://symfony.com",
- "time": "2020-01-04T13:00:46+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/validator",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/validator.git",
- "reference": "3a3a07fe2f42492eccca6771f1a460c9900cd851"
+ "reference": "2bf1de9d5cac5e5ebc159203c53dcf5b2058d340"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/validator/zipball/3a3a07fe2f42492eccca6771f1a460c9900cd851",
- "reference": "3a3a07fe2f42492eccca6771f1a460c9900cd851",
+ "url": "https://api.github.com/repos/symfony/validator/zipball/2bf1de9d5cac5e5ebc159203c53dcf5b2058d340",
+ "reference": "2bf1de9d5cac5e5ebc159203c53dcf5b2058d340",
"shasum": ""
},
"require": {
@@ -9037,20 +9796,34 @@
],
"description": "Symfony Validator Component",
"homepage": "https://symfony.com",
- "time": "2020-02-29T10:05:28+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T11:41:10+00:00"
},
{
"name": "symfony/var-dumper",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "2572839911702b0405479410ea7a1334bfab0b96"
+ "reference": "5a0c2d93006131a36cf6f767d10e2ca8333b0d4a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2572839911702b0405479410ea7a1334bfab0b96",
- "reference": "2572839911702b0405479410ea7a1334bfab0b96",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/5a0c2d93006131a36cf6f767d10e2ca8333b0d4a",
+ "reference": "5a0c2d93006131a36cf6f767d10e2ca8333b0d4a",
"shasum": ""
},
"require": {
@@ -9113,20 +9886,34 @@
"debug",
"dump"
],
- "time": "2020-02-24T13:10:00+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/var-exporter",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
- "reference": "1a76a943f2af334da13bc9f33f49392fa530eec9"
+ "reference": "6e4939b084defee0ab60a21e6a02e3a198afd91f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-exporter/zipball/1a76a943f2af334da13bc9f33f49392fa530eec9",
- "reference": "1a76a943f2af334da13bc9f33f49392fa530eec9",
+ "url": "https://api.github.com/repos/symfony/var-exporter/zipball/6e4939b084defee0ab60a21e6a02e3a198afd91f",
+ "reference": "6e4939b084defee0ab60a21e6a02e3a198afd91f",
"shasum": ""
},
"require": {
@@ -9173,7 +9960,21 @@
"instantiate",
"serialize"
],
- "time": "2020-01-04T13:00:46+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/webpack-encore-bundle",
@@ -9230,16 +10031,16 @@
},
{
"name": "symfony/yaml",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "94d005c176db2080e98825d98e01e8b311a97a88"
+ "reference": "ef166890d821518106da3560086bfcbeb4fadfec"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/94d005c176db2080e98825d98e01e8b311a97a88",
- "reference": "94d005c176db2080e98825d98e01e8b311a97a88",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/ef166890d821518106da3560086bfcbeb4fadfec",
+ "reference": "ef166890d821518106da3560086bfcbeb4fadfec",
"shasum": ""
},
"require": {
@@ -9285,7 +10086,21 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2020-02-03T10:46:43+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-30T11:41:10+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@@ -10788,16 +11603,16 @@
},
{
"name": "phpstan/phpstan",
- "version": "0.12.17",
+ "version": "0.12.18",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "b2c37bda79fdb11d801ce208ce391cffc0f572e6"
+ "reference": "1ce27fe29c8660a27926127d350d53d80c4d4286"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b2c37bda79fdb11d801ce208ce391cffc0f572e6",
- "reference": "b2c37bda79fdb11d801ce208ce391cffc0f572e6",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ce27fe29c8660a27926127d350d53d80c4d4286",
+ "reference": "1ce27fe29c8660a27926127d350d53d80c4d4286",
"shasum": ""
},
"require": {
@@ -10823,7 +11638,21 @@
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
- "time": "2020-03-17T15:39:11+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/phpstan",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-22T16:51:47+00:00"
},
{
"name": "phpstan/phpstan-doctrine",
@@ -11269,16 +12098,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "8.5.2",
+ "version": "8.5.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "018b6ac3c8ab20916db85fa91bf6465acb64d1e0"
+ "reference": "67750516bc02f300e2742fed2f50177f8f37bedf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/018b6ac3c8ab20916db85fa91bf6465acb64d1e0",
- "reference": "018b6ac3c8ab20916db85fa91bf6465acb64d1e0",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/67750516bc02f300e2742fed2f50177f8f37bedf",
+ "reference": "67750516bc02f300e2742fed2f50177f8f37bedf",
"shasum": ""
},
"require": {
@@ -11348,7 +12177,17 @@
"testing",
"xunit"
],
- "time": "2020-01-08T08:49:49+00:00"
+ "funding": [
+ {
+ "url": "https://phpunit.de/donate.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-03-31T08:52:04+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@@ -11967,16 +12806,16 @@
},
{
"name": "symfony/browser-kit",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
- "reference": "090ce406505149d6852a7c03b0346dec3b8cf612"
+ "reference": "e4b0dc1b100bf75b5717c5b451397f230a618a42"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/browser-kit/zipball/090ce406505149d6852a7c03b0346dec3b8cf612",
- "reference": "090ce406505149d6852a7c03b0346dec3b8cf612",
+ "url": "https://api.github.com/repos/symfony/browser-kit/zipball/e4b0dc1b100bf75b5717c5b451397f230a618a42",
+ "reference": "e4b0dc1b100bf75b5717c5b451397f230a618a42",
"shasum": ""
},
"require": {
@@ -12022,20 +12861,34 @@
],
"description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com",
- "time": "2020-02-23T10:00:59+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-28T10:15:50+00:00"
},
{
"name": "symfony/debug-bundle",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug-bundle.git",
- "reference": "570c3c69e69f7709f184ee3acbebe45e5ff1adce"
+ "reference": "dc847e4971b9f76b30e02d421b303d349d5aeed2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/570c3c69e69f7709f184ee3acbebe45e5ff1adce",
- "reference": "570c3c69e69f7709f184ee3acbebe45e5ff1adce",
+ "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/dc847e4971b9f76b30e02d421b303d349d5aeed2",
+ "reference": "dc847e4971b9f76b30e02d421b303d349d5aeed2",
"shasum": ""
},
"require": {
@@ -12088,7 +12941,21 @@
],
"description": "Symfony DebugBundle",
"homepage": "https://symfony.com",
- "time": "2020-01-04T13:00:46+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/dom-crawler",
@@ -12153,16 +13020,16 @@
},
{
"name": "symfony/maker-bundle",
- "version": "v1.14.6",
+ "version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/maker-bundle.git",
- "reference": "bc4df88792fbaaeb275167101dc714218475db5f"
+ "reference": "31396f2e61803f0e2debbb43ba5aa21acbc6e15a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/bc4df88792fbaaeb275167101dc714218475db5f",
- "reference": "bc4df88792fbaaeb275167101dc714218475db5f",
+ "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/31396f2e61803f0e2debbb43ba5aa21acbc6e15a",
+ "reference": "31396f2e61803f0e2debbb43ba5aa21acbc6e15a",
"shasum": ""
},
"require": {
@@ -12217,20 +13084,34 @@
"scaffold",
"scaffolding"
],
- "time": "2020-03-04T13:57:29+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-04-05T10:50:59+00:00"
},
{
"name": "symfony/phpunit-bridge",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/phpunit-bridge.git",
- "reference": "a270dbfe54994138a8037937fd5934827b8605bf"
+ "reference": "6937c1a1590da7c314537b4f3f741c9255a7072e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/a270dbfe54994138a8037937fd5934827b8605bf",
- "reference": "a270dbfe54994138a8037937fd5934827b8605bf",
+ "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/6937c1a1590da7c314537b4f3f741c9255a7072e",
+ "reference": "6937c1a1590da7c314537b4f3f741c9255a7072e",
"shasum": ""
},
"require": {
@@ -12282,20 +13163,34 @@
],
"description": "Symfony PHPUnit Bridge",
"homepage": "https://symfony.com",
- "time": "2020-02-24T14:58:55+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/process",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7"
+ "reference": "3e40e87a20eaf83a1db825e1fa5097ae89042db3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/bf9166bac906c9e69fb7a11d94875e7ced97bcd7",
- "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7",
+ "url": "https://api.github.com/repos/symfony/process/zipball/3e40e87a20eaf83a1db825e1fa5097ae89042db3",
+ "reference": "3e40e87a20eaf83a1db825e1fa5097ae89042db3",
"shasum": ""
},
"require": {
@@ -12331,7 +13226,21 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
- "time": "2020-02-07T20:06:44+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/profiler-pack",
@@ -12363,16 +13272,16 @@
},
{
"name": "symfony/web-profiler-bundle",
- "version": "v4.4.5",
+ "version": "v4.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/web-profiler-bundle.git",
- "reference": "76c21d0137f0b9c6bbbc93ac2672cadfdf2e625a"
+ "reference": "4c432f5c21c700270819daacf95323302fa8f004"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/76c21d0137f0b9c6bbbc93ac2672cadfdf2e625a",
- "reference": "76c21d0137f0b9c6bbbc93ac2672cadfdf2e625a",
+ "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/4c432f5c21c700270819daacf95323302fa8f004",
+ "reference": "4c432f5c21c700270819daacf95323302fa8f004",
"shasum": ""
},
"require": {
@@ -12425,7 +13334,21 @@
],
"description": "Symfony WebProfilerBundle",
"homepage": "https://symfony.com",
- "time": "2020-02-14T07:42:58+00:00"
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-03-27T16:54:36+00:00"
},
{
"name": "theseer/tokenizer",
@@ -12486,5 +13409,6 @@
"platform-dev": [],
"platform-overrides": {
"php": "7.2.9"
- }
+ },
+ "plugin-api-version": "1.1.0"
}
diff --git a/config/packages/fos_rest.yaml b/config/packages/fos_rest.yaml
index 915d573b..48a48cc3 100644
--- a/config/packages/fos_rest.yaml
+++ b/config/packages/fos_rest.yaml
@@ -15,6 +15,8 @@ fos_rest:
# enabled: false
# service: null
routing_loader:
+ # TODO activate once 2.8 is released
+ #parse_controller_name: false
default_format: json
include_format: true
# prefix_methods: true
diff --git a/config/packages/nelmio_api_doc.yaml b/config/packages/nelmio_api_doc.yaml
index 28ca088c..b71a7371 100644
--- a/config/packages/nelmio_api_doc.yaml
+++ b/config/packages/nelmio_api_doc.yaml
@@ -4,14 +4,20 @@ nelmio_api_doc:
names:
- { alias: CustomerEditForm, type: App\Form\API\CustomerApiEditForm, groups: [Default, Entity, Customer] }
- { alias: CustomerEntity, type: App\Entity\Customer, groups: [Default, Entity, Customer] }
+ - { alias: CustomerRate, type: App\Entity\CustomerRate, groups: [Default, Entity, CustomerRate] }
+ - { alias: CustomerRateForm, type: App\Form\API\CustomerRateApiForm, groups: [Default, Entity, CustomerRate] }
- { alias: CustomerMetaField, type: App\Entity\CustomerMeta, groups: [Default, Customer] }
- { alias: CustomerCollection, type: App\Entity\Customer, groups: [Default, Collection, Customer] }
- { alias: ProjectEditForm, type: App\Form\API\ProjectApiEditForm, groups: [Default, Entity, Project] }
- { alias: ProjectEntity, type: App\Entity\Project, groups: [Default, Entity, Project] }
+ - { alias: ProjectRate, type: App\Entity\ProjectRate, groups: [Default, Entity, ProjectRate] }
+ - { alias: ProjectRateForm, type: App\Form\API\ProjectRateApiForm, groups: [Default, Entity, ProjectRate] }
- { alias: ProjectMetaField, type: App\Entity\ProjectMeta, groups: [Default, Project] }
- { alias: ProjectCollection, type: App\Entity\Project, groups: [Default, Collection, Project] }
- { alias: ActivityEditForm, type: App\Form\API\ActivityApiEditForm, groups: [Default, Entity, Activity] }
- { alias: ActivityEntity, type: App\Entity\Activity, groups: [Default, Entity, Activity] }
+ - { alias: ActivityRate, type: App\Entity\ActivityRate, groups: [Default, Entity, ActivityRate] }
+ - { alias: ActivityRateForm, type: App\Form\API\ActivityRateApiForm, groups: [Default, Entity, ActivityRate] }
- { alias: ActivityMetaField, type: App\Entity\ActivityMeta, groups: [Default, Activity] }
- { alias: ActivityCollection, type: App\Entity\Activity, groups: [Default, Collection, Activity] }
- { alias: TagEditForm, type: App\Form\API\TagApiEditForm, groups: [Default, Entity, Tag] }
@@ -23,6 +29,7 @@ nelmio_api_doc:
- { alias: TimesheetSubCollection, type: App\Entity\Timesheet, groups: [Default, Subresource, Timesheet] }
- { alias: UserCreateForm, type: App\Form\API\UserApiCreateForm, groups: [Default, Entity, User, User_Entity] }
- { alias: UserEditForm, type: App\Form\API\UserApiEditForm, groups: [Default, Entity, User, User_Entity] }
+ - { alias: User, type: App\Entity\User, groups: [Default, Entity, User] }
- { alias: UserEntity, type: App\Entity\User, groups: [Default, Entity, User, User_Entity] }
- { alias: UserCollection, type: App\Entity\User, groups: [Default, Collection, User] }
- { alias: TeamEditForm, type: App\Form\API\TeamApiEditForm, groups: [Default, Entity, Team] }
@@ -42,8 +49,8 @@ nelmio_api_doc:
title: Kimai 2 - API Docs
description: |
JSON API for the Kimai 2 time-tracking software. Read more about its usage in the [API documentation](https://www.kimai.org/documentation/rest-api.html) and then download a [Swagger file](doc.json) for import e.g. in Postman.
- Be aware: it is not yet considered stable and BC breaks might happen, but we try to avoid them.
- version: '0.4'
+ Be aware: it is not yet considered stable and BC breaks might happen.
+ version: '0.5'
securityDefinitions:
apiUser:
type: apiKey
diff --git a/config/serializer/App/Entity.ActivityRate.yml b/config/serializer/App/Entity.ActivityRate.yml
new file mode 100644
index 00000000..0ac75421
--- /dev/null
+++ b/config/serializer/App/Entity.ActivityRate.yml
@@ -0,0 +1,21 @@
+App\Entity\ActivityRate:
+ exclusion_policy: All
+ custom_accessor_order: [id, rate, internalRate, isFixed, user]
+ properties:
+ id:
+ include: true
+ groups: [Default]
+ activity:
+ exclude: true
+ user:
+ include: true
+ groups: [Default]
+ rate:
+ include: true
+ groups: [Default]
+ internalRate:
+ include: true
+ groups: [Default]
+ isFixed:
+ include: true
+ groups: [Default]
diff --git a/config/serializer/App/Entity.CustomerRate.yml b/config/serializer/App/Entity.CustomerRate.yml
new file mode 100644
index 00000000..e6bf8fa6
--- /dev/null
+++ b/config/serializer/App/Entity.CustomerRate.yml
@@ -0,0 +1,21 @@
+App\Entity\CustomerRate:
+ exclusion_policy: All
+ custom_accessor_order: [id, rate, internalRate, isFixed, user]
+ properties:
+ id:
+ include: true
+ groups: [Default]
+ customer:
+ exclude: true
+ user:
+ include: true
+ groups: [Default]
+ rate:
+ include: true
+ groups: [Default]
+ internalRate:
+ include: true
+ groups: [Default]
+ isFixed:
+ include: true
+ groups: [Default]
diff --git a/config/serializer/App/Entity.ProjectRate.yml b/config/serializer/App/Entity.ProjectRate.yml
new file mode 100644
index 00000000..14886799
--- /dev/null
+++ b/config/serializer/App/Entity.ProjectRate.yml
@@ -0,0 +1,21 @@
+App\Entity\ProjectRate:
+ exclusion_policy: All
+ custom_accessor_order: [id, rate, internalRate, isFixed, user]
+ properties:
+ id:
+ include: true
+ groups: [Default]
+ project:
+ exclude: true
+ user:
+ include: true
+ groups: [Default]
+ rate:
+ include: true
+ groups: [Default]
+ internalRate:
+ include: true
+ groups: [Default]
+ isFixed:
+ include: true
+ groups: [Default]
diff --git a/config/serializer/App/Entity.Timesheet.yml b/config/serializer/App/Entity.Timesheet.yml
index 2c58dbbe..b86e35cf 100644
--- a/config/serializer/App/Entity.Timesheet.yml
+++ b/config/serializer/App/Entity.Timesheet.yml
@@ -14,6 +14,8 @@ App\Entity\Timesheet:
include: true
rate:
include: true
+ internalRate:
+ include: true
fixedRate:
include: true
groups: [Entity]
diff --git a/phpstan.neon b/phpstan.neon
index 1a2d48d2..bb9d3fa2 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -7,8 +7,6 @@ parameters:
tmpDir: %rootDir%/../../../var/cache/phpstan
autoload_directories:
- %rootDir%/../../../src/Migrations
- symfony:
- container_xml_path: '%rootDir%/../../../var/cache/dev/srcApp_KernelDevDebugContainer.xml'
ignoreErrors:
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::scalarNode\(\).#'
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::integerNode\(\).#'
diff --git a/src/API/ActivityController.php b/src/API/ActivityController.php
index b60614d4..7d16b9e3 100644
--- a/src/API/ActivityController.php
+++ b/src/API/ActivityController.php
@@ -12,8 +12,11 @@ declare(strict_types=1);
namespace App\API;
use App\Entity\Activity;
+use App\Entity\ActivityRate;
use App\Event\ActivityMetaDefinitionEvent;
use App\Form\API\ActivityApiEditForm;
+use App\Form\API\ActivityRateApiForm;
+use App\Repository\ActivityRateRepository;
use App\Repository\ActivityRepository;
use App\Repository\Query\ActivityQuery;
use App\Utils\SearchTerm;
@@ -32,6 +35,7 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* @RouteResource("Activity")
+ * @SWG\Tag(name="Activity")
*
* @Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")
*/
@@ -49,12 +53,17 @@ class ActivityController extends BaseApiController
* @var EventDispatcherInterface
*/
private $dispatcher;
+ /**
+ * @var ActivityRateRepository
+ */
+ private $activityRateRepository;
- public function __construct(ViewHandlerInterface $viewHandler, ActivityRepository $repository, EventDispatcherInterface $dispatcher)
+ public function __construct(ViewHandlerInterface $viewHandler, ActivityRepository $repository, EventDispatcherInterface $dispatcher, ActivityRateRepository $activityRateRepository)
{
$this->viewHandler = $viewHandler;
$this->repository = $repository;
$this->dispatcher = $dispatcher;
+ $this->activityRateRepository = $activityRateRepository;
}
/**
@@ -276,7 +285,7 @@ class ActivityController extends BaseApiController
}
/**
- * Sets the value of a meta-field for an existing activity.
+ * Sets the value of a meta-field for an existing activity
*
* @SWG\Response(
* response=200,
@@ -327,4 +336,166 @@ class ActivityController extends BaseApiController
return $this->viewHandler->handle($view);
}
+
+ /**
+ * Returns a collection of all rates for one activity
+ *
+ * @SWG\Response(
+ * response=200,
+ * description="Returns a collection of activity rate entities",
+ * @SWG\Schema(
+ * type="array",
+ * @SWG\Items(ref="#/definitions/ActivityRate")
+ * )
+ * )
+ * @SWG\Parameter(
+ * name="id",
+ * in="path",
+ * type="integer",
+ * description="The activity whose rates will be returned",
+ * required=true,
+ * )
+ *
+ * @ApiSecurity(name="apiUser")
+ * @ApiSecurity(name="apiToken")
+ */
+ public function getRatesAction(int $id): Response
+ {
+ /** @var Activity|null $activity */
+ $activity = $this->repository->find($id);
+
+ if (null === $activity) {
+ throw new NotFoundException();
+ }
+
+ if (!$this->isGranted('edit', $activity)) {
+ throw new AccessDeniedHttpException('Access denied.');
+ }
+
+ $rates = $this->activityRateRepository->getRatesForActivity($activity);
+
+ $view = new View($rates, 200);
+ $view->getContext()->setGroups(['Default', 'Entity', 'ActivityRate']);
+
+ return $this->viewHandler->handle($view);
+ }
+
+ /**
+ * Deletes one rate for an activity
+ *
+ * @SWG\Delete(
+ * @SWG\Response(
+ * response=204,
+ * description="Returns no content: 204 on successful delete"
+ * )
+ * )
+ * @SWG\Parameter(
+ * name="id",
+ * in="path",
+ * type="integer",
+ * description="The activity whose rate will be removed",
+ * required=true,
+ * )
+ * @SWG\Parameter(
+ * name="rateId",
+ * in="path",
+ * type="integer",
+ * description="The rate to remove",
+ * required=true,
+ * )
+ *
+ * @ApiSecurity(name="apiUser")
+ * @ApiSecurity(name="apiToken")
+ */
+ public function deleteRateAction(string $id, string $rateId): Response
+ {
+ /** @var Activity|null $activity */
+ $activity = $this->repository->find($id);
+
+ if (null === $activity) {
+ throw new NotFoundException();
+ }
+
+ if (!$this->isGranted('edit', $activity)) {
+ throw new AccessDeniedHttpException('Access denied.');
+ }
+
+ /** @var ActivityRate|null $rate */
+ $rate = $this->activityRateRepository->find($rateId);
+
+ if (null === $rate || $rate->getActivity() !== $activity) {
+ throw new NotFoundException();
+ }
+
+ $this->activityRateRepository->deleteRate($rate);
+
+ $view = new View(null, Response::HTTP_NO_CONTENT);
+
+ return $this->viewHandler->handle($view);
+ }
+
+ /**
+ * Adds a new rate to an activity
+ *
+ * @SWG\Post(
+ * @SWG\Response(
+ * response=200,
+ * description="Returns the new created rate",
+ * @SWG\Schema(ref="#/definitions/ActivityRate")
+ * )
+ * )
+ * @SWG\Parameter(
+ * name="id",
+ * in="path",
+ * type="integer",
+ * description="The activity to add the rate for",
+ * required=true,
+ * )
+ * @SWG\Parameter(
+ * name="body",
+ * in="body",
+ * required=true,
+ * @SWG\Schema(ref="#/definitions/ActivityRateForm")
+ * )
+ *
+ * @ApiSecurity(name="apiUser")
+ * @ApiSecurity(name="apiToken")
+ */
+ public function postRateAction(int $id, Request $request): Response
+ {
+ /** @var Activity|null $activity */
+ $activity = $this->repository->find($id);
+
+ if (null === $activity) {
+ throw new NotFoundException();
+ }
+
+ if (!$this->isGranted('edit', $activity)) {
+ throw new AccessDeniedHttpException('Access denied.');
+ }
+
+ $rate = new ActivityRate();
+ $rate->setActivity($activity);
+
+ $form = $this->createForm(ActivityRateApiForm::class, $rate, [
+ 'method' => 'POST',
+ ]);
+
+ $form->setData($rate);
+ $form->submit($request->request->all(), false);
+
+ if (false === $form->isValid()) {
+ $view = new View($form, Response::HTTP_OK);
+ $view->getContext()->setGroups(['Default', 'Entity', 'ActivityRate']);
+
+ return $this->viewHandler->handle($view);
+ }
+
+ $this->activityRateRepository->saveRate($rate);
+
+ $view = new View($rate, Response::HTTP_OK);
+ $view->getContext()->setGroups(['Default', 'Entity', 'ActivityRate']);
+
+ return $this->viewHandler->handle($view);
+ }
}
diff --git a/src/API/ConfigurationController.php b/src/API/ConfigurationController.php
index 8ed79c04..b8df98fd 100644
--- a/src/API/ConfigurationController.php
+++ b/src/API/ConfigurationController.php
@@ -25,6 +25,8 @@ use Swagger\Annotations as SWG;
use Symfony\Component\HttpFoundation\Response;
/**
+ * @SWG\Tag(name="Default")
+ *
* @Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")
*/
final class ConfigurationController extends BaseApiController
diff --git a/src/API/CustomerController.php b/src/API/CustomerController.php
index 39c358d1..75cdaaca 100644
--- a/src/API/CustomerController.php
+++ b/src/API/CustomerController.php
@@ -12,9 +12,12 @@ declare(strict_types=1);
namespace App\API;
use App\Entity\Customer;
+use App\Entity\CustomerRate;
use App\Entity\User;
use App\Event\CustomerMetaDefinitionEvent;
use App\Form\API\CustomerApiEditForm;
+use App\Form\API\CustomerRateApiForm;
+use App\Repository\CustomerRateRepository;
use App\Repository\CustomerRepository;
use App\Repository\Query\CustomerQuery;
use App\Utils\SearchTerm;
@@ -33,6 +36,7 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* @RouteResource("Customer")
+ * @SWG\Tag(name="Customer")
*
* @Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")
*/
@@ -50,12 +54,17 @@ class CustomerController extends BaseApiController
* @var EventDispatcherInterface
*/
private $dispatcher;
+ /**
+ * @var CustomerRateRepository
+ */
+ private $customerRateRepository;
- public function __construct(ViewHandlerInterface $viewHandler, CustomerRepository $repository, EventDispatcherInterface $dispatcher)
+ public function __construct(ViewHandlerInterface $viewHandler, CustomerRepository $repository, EventDispatcherInterface $dispatcher, CustomerRateRepository $customerRateRepository)
{
$this->viewHandler = $viewHandler;
$this->repository = $repository;
$this->dispatcher = $dispatcher;
+ $this->customerRateRepository = $customerRateRepository;
}
/**
@@ -66,7 +75,7 @@ class CustomerController extends BaseApiController
* description="Returns a collection of customer entities",
* @SWG\Schema(
* type="array",
- * @SWG\Items(ref="#/definitions/CustomerCollection")
+ * @SWG\Items(ref="#/definitions/CustomerEntity")
* )
* )
* @Rest\QueryParam(name="visible", requirements="\d+", strict=true, nullable=true, description="Visibility status to filter activities (1=visible, 2=hidden, 3=both)")
@@ -249,7 +258,7 @@ class CustomerController extends BaseApiController
}
/**
- * Sets the value of a meta-field for an existing customer.
+ * Sets the value of a meta-field for an existing customer
*
* @SWG\Response(
* response=200,
@@ -300,4 +309,166 @@ class CustomerController extends BaseApiController
return $this->viewHandler->handle($view);
}
+
+ /**
+ * Returns a collection of all rates for one customer
+ *
+ * @SWG\Response(
+ * response=200,
+ * description="Returns a collection of customer rate entities",
+ * @SWG\Schema(
+ * type="array",
+ * @SWG\Items(ref="#/definitions/CustomerRate")
+ * )
+ * )
+ * @SWG\Parameter(
+ * name="id",
+ * in="path",
+ * type="integer",
+ * description="The customer whose rates will be returned",
+ * required=true,
+ * )
+ *
+ * @ApiSecurity(name="apiUser")
+ * @ApiSecurity(name="apiToken")
+ */
+ public function getRatesAction(int $id): Response
+ {
+ /** @var Customer|null $customer */
+ $customer = $this->repository->find($id);
+
+ if (null === $customer) {
+ throw new NotFoundException();
+ }
+
+ if (!$this->isGranted('edit', $customer)) {
+ throw new AccessDeniedHttpException('Access denied.');
+ }
+
+ $rates = $this->customerRateRepository->getRatesForCustomer($customer);
+
+ $view = new View($rates, 200);
+ $view->getContext()->setGroups(['Default', 'Entity', 'CustomerRate']);
+
+ return $this->viewHandler->handle($view);
+ }
+
+ /**
+ * Deletes one rate for an customer
+ *
+ * @SWG\Delete(
+ * @SWG\Response(
+ * response=204,
+ * description="Returns no content: 204 on successful delete"
+ * )
+ * )
+ * @SWG\Parameter(
+ * name="id",
+ * in="path",
+ * type="integer",
+ * description="The customer whose rate will be removed",
+ * required=true,
+ * )
+ * @SWG\Parameter(
+ * name="rateId",
+ * in="path",
+ * type="integer",
+ * description="The rate to remove",
+ * required=true,
+ * )
+ *
+ * @ApiSecurity(name="apiUser")
+ * @ApiSecurity(name="apiToken")
+ */
+ public function deleteRateAction(string $id, string $rateId): Response
+ {
+ /** @var Customer|null $customer */
+ $customer = $this->repository->find($id);
+
+ if (null === $customer) {
+ throw new NotFoundException();
+ }
+
+ if (!$this->isGranted('edit', $customer)) {
+ throw new AccessDeniedHttpException('Access denied.');
+ }
+
+ /** @var CustomerRate|null $rate */
+ $rate = $this->customerRateRepository->find($rateId);
+
+ if (null === $rate || $rate->getCustomer() !== $customer) {
+ throw new NotFoundException();
+ }
+
+ $this->customerRateRepository->deleteRate($rate);
+
+ $view = new View(null, Response::HTTP_NO_CONTENT);
+
+ return $this->viewHandler->handle($view);
+ }
+
+ /**
+ * Adds a new rate to a customer
+ *
+ * @SWG\Post(
+ * @SWG\Response(
+ * response=200,
+ * description="Returns the new created rate",
+ * @SWG\Schema(ref="#/definitions/CustomerRate")
+ * )
+ * )
+ * @SWG\Parameter(
+ * name="id",
+ * in="path",
+ * type="integer",
+ * description="The customer to add the rate for",
+ * required=true,
+ * )
+ * @SWG\Parameter(
+ * name="body",
+ * in="body",
+ * required=true,
+ * @SWG\Schema(ref="#/definitions/CustomerRateForm")
+ * )
+ *
+ * @ApiSecurity(name="apiUser")
+ * @ApiSecurity(name="apiToken")
+ */
+ public function postRateAction(int $id, Request $request): Response
+ {
+ /** @var Customer|null $customer */
+ $customer = $this->repository->find($id);
+
+ if (null === $customer) {
+ throw new NotFoundException();
+ }
+
+ if (!$this->isGranted('edit', $customer)) {
+ throw new AccessDeniedHttpException('Access denied.');
+ }
+
+ $rate = new CustomerRate();
+ $rate->setCustomer($customer);
+
+ $form = $this->createForm(CustomerRateApiForm::class, $rate, [
+ 'method' => 'POST',
+ ]);
+
+ $form->setData($rate);
+ $form->submit($request->request->all(), false);
+
+ if (false === $form->isValid()) {
+ $view = new View($form, Response::HTTP_OK);
+ $view->getContext()->setGroups(['Default', 'Entity', 'CustomerRate']);
+
+ return $this->viewHandler->handle($view);
+ }
+
+ $this->customerRateRepository->saveRate($rate);
+
+ $view = new View($rate, Response::HTTP_OK);
+ $view->getContext()->setGroups(['Default', 'Entity', 'CustomerRate']);
+
+ return $this->viewHandler->handle($view);
+ }
}
diff --git a/src/API/ProjectController.php b/src/API/ProjectController.php
index a2f10356..13a6dafd 100644
--- a/src/API/ProjectController.php
+++ b/src/API/ProjectController.php
@@ -11,10 +11,14 @@ declare(strict_types=1);
namespace App\API;
+use App\Entity\Customer;
use App\Entity\Project;
+use App\Entity\ProjectRate;
use App\Entity\User;
use App\Event\ProjectMetaDefinitionEvent;
use App\Form\API\ProjectApiEditForm;
+use App\Form\API\ProjectRateApiForm;
+use App\Repository\ProjectRateRepository;
use App\Repository\ProjectRepository;
use App\Repository\Query\ProjectQuery;
use App\Timesheet\UserDateTimeFactory;
@@ -35,6 +39,7 @@ use Symfony\Component\Validator\Constraints;
/**
* @RouteResource("Project")
+ * @SWG\Tag(name="Project")
*
* @Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")
*/
@@ -56,13 +61,18 @@ class ProjectController extends BaseApiController
* @var UserDateTimeFactory
*/
private $dateTime;
+ /**
+ * @var ProjectRateRepository
+ */
+ private $projectRateRepository;
- public function __construct(ViewHandlerInterface $viewHandler, ProjectRepository $repository, EventDispatcherInterface $dispatcher, UserDateTimeFactory $dateTime)
+ public function __construct(ViewHandlerInterface $viewHandler, ProjectRepository $repository, EventDispatcherInterface $dispatcher, UserDateTimeFactory $dateTime, ProjectRateRepository $projectRateRepository)
{
$this->viewHandler = $viewHandler;
$this->repository = $repository;
$this->dispatcher = $dispatcher;
$this->dateTime = $dateTime;
+ $this->projectRateRepository = $projectRateRepository;
}
/**
@@ -299,7 +309,7 @@ class ProjectController extends BaseApiController
}
/**
- * Sets the value of a meta-field for an existing project.
+ * Sets the value of a meta-field for an existing project
*
* @SWG\Response(
* response=200,
@@ -350,4 +360,166 @@ class ProjectController extends BaseApiController
return $this->viewHandler->handle($view);
}
+
+ /**
+ * Returns a collection of all rates for one project
+ *
+ * @SWG\Response(
+ * response=200,
+ * description="Returns a collection of project rate entities",
+ * @SWG\Schema(
+ * type="array",
+ * @SWG\Items(ref="#/definitions/ProjectRate")
+ * )
+ * )
+ * @SWG\Parameter(
+ * name="id",
+ * in="path",
+ * type="integer",
+ * description="The project whose rates will be returned",
+ * required=true,
+ * )
+ *
+ * @ApiSecurity(name="apiUser")
+ * @ApiSecurity(name="apiToken")
+ */
+ public function getRatesAction(int $id): Response
+ {
+ /** @var Project|null $project */
+ $project = $this->repository->find($id);
+
+ if (null === $project) {
+ throw new NotFoundException();
+ }
+
+ if (!$this->isGranted('edit', $project)) {
+ throw new AccessDeniedHttpException('Access denied.');
+ }
+
+ $rates = $this->projectRateRepository->getRatesForProject($project);
+
+ $view = new View($rates, 200);
+ $view->getContext()->setGroups(['Default', 'Entity', 'ProjectRate']);
+
+ return $this->viewHandler->handle($view);
+ }
+
+ /**
+ * Deletes one rate for an project
+ *
+ * @SWG\Delete(
+ * @SWG\Response(
+ * response=204,
+ * description="Returns no content: 204 on successful delete"
+ * )
+ * )
+ * @SWG\Parameter(
+ * name="id",
+ * in="path",
+ * type="integer",
+ * description="The project whose rate will be removed",
+ * required=true,
+ * )
+ * @SWG\Parameter(
+ * name="rateId",
+ * in="path",
+ * type="integer",
+ * description="The rate to remove",
+ * required=true,
+ * )
+ *
+ * @ApiSecurity(name="apiUser")
+ * @ApiSecurity(name="apiToken")
+ */
+ public function deleteRateAction(string $id, string $rateId): Response
+ {
+ /** @var Project|null $project */
+ $project = $this->repository->find($id);
+
+ if (null === $project) {
+ throw new NotFoundException();
+ }
+
+ if (!$this->isGranted('edit', $project)) {
+ throw new AccessDeniedHttpException('Access denied.');
+ }
+
+ /** @var ProjectRate|null $rate */
+ $rate = $this->projectRateRepository->find($rateId);
+
+ if (null === $rate || $rate->getProject() !== $project) {
+ throw new NotFoundException();
+ }
+
+ $this->projectRateRepository->deleteRate($rate);
+
+ $view = new View(null, Response::HTTP_NO_CONTENT);
+
+ return $this->viewHandler->handle($view);
+ }
+
+ /**
+ * Adds a new rate to an project
+ *
+ * @SWG\Post(
+ * @SWG\Response(
+ * response=200,
+ * description="Returns the new created rate",
+ * @SWG\Schema(ref="#/definitions/ProjectRate")
+ * )
+ * )
+ * @SWG\Parameter(
+ * name="id",
+ * in="path",
+ * type="integer",
+ * description="The project to add the rate for",
+ * required=true,
+ * )
+ * @SWG\Parameter(
+ * name="body",
+ * in="body",
+ * required=true,
+ * @SWG\Schema(ref="#/definitions/ProjectRateForm")
+ * )
+ *
+ * @ApiSecurity(name="apiUser")
+ * @ApiSecurity(name="apiToken")
+ */
+ public function postRateAction(int $id, Request $request): Response
+ {
+ /** @var Project|null $project */
+ $project = $this->repository->find($id);
+
+ if (null === $project) {
+ throw new NotFoundException();
+ }
+
+ if (!$this->isGranted('edit', $project)) {
+ throw new AccessDeniedHttpException('Access denied.');
+ }
+
+ $rate = new ProjectRate();
+ $rate->setProject($project);
+
+ $form = $this->createForm(ProjectRateApiForm::class, $rate, [
+ 'method' => 'POST',
+ ]);
+
+ $form->setData($rate);
+ $form->submit($request->request->all(), false);
+
+ if (false === $form->isValid()) {
+ $view = new View($form, Response::HTTP_OK);
+ $view->getContext()->setGroups(['Default', 'Entity', 'ProjectRate']);
+
+ return $this->viewHandler->handle($view);
+ }
+
+ $this->projectRateRepository->saveRate($rate);
+
+ $view = new View($rate, Response::HTTP_OK);
+ $view->getContext()->setGroups(['Default', 'Entity', 'ProjectRate']);
+
+ return $this->viewHandler->handle($view);
+ }
}
diff --git a/src/API/StatusController.php b/src/API/StatusController.php
index f92558e9..25c8db2a 100644
--- a/src/API/StatusController.php
+++ b/src/API/StatusController.php
@@ -20,6 +20,9 @@ use Nelmio\ApiDocBundle\Annotation\Security as ApiSecurity;
use Swagger\Annotations as SWG;
use Symfony\Component\HttpFoundation\Response;
+/**
+ * @SWG\Tag(name="Default")
+ */
class StatusController extends BaseApiController
{
/**
diff --git a/src/API/TagController.php b/src/API/TagController.php
index 04245f00..2d48989d 100644
--- a/src/API/TagController.php
+++ b/src/API/TagController.php
@@ -28,6 +28,7 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* @RouteResource("Tag")
+ * @SWG\Tag(name="Tag")
*
* @Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")
*/
diff --git a/src/API/TeamController.php b/src/API/TeamController.php
index df1531d2..252af08b 100644
--- a/src/API/TeamController.php
+++ b/src/API/TeamController.php
@@ -33,6 +33,7 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* @RouteResource("Team")
+ * @SWG\Tag(name="Team")
*
* @Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")
*/
diff --git a/src/API/TimesheetController.php b/src/API/TimesheetController.php
index d8b8e6e2..97315265 100644
--- a/src/API/TimesheetController.php
+++ b/src/API/TimesheetController.php
@@ -45,6 +45,7 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* @RouteResource("Timesheet")
+ * @SWG\Tag(name="Timesheet")
*
* @Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")
*/
@@ -169,8 +170,9 @@ class TimesheetController extends BaseApiController
if (!empty($customers)) {
$query->setCustomers($customers);
}
- } elseif (!empty($customer = $paramFetcher->get('customer'))) {
- @trigger_error('Timesheet API parameter "customer" is deprecated and will be removed with 2.0, use "customers" instead', E_USER_DEPRECATED);
+ }
+
+ if (!empty($customer = $paramFetcher->get('customer'))) {
$query->addCustomer($customer);
}
@@ -181,8 +183,9 @@ class TimesheetController extends BaseApiController
if (!empty($projects)) {
$query->setProjects($projects);
}
- } elseif (!empty($project = $paramFetcher->get('project'))) {
- @trigger_error('Timesheet API parameter "project" is deprecated and will be removed with 2.0, use "projects" instead', E_USER_DEPRECATED);
+ }
+
+ if (!empty($project = $paramFetcher->get('project'))) {
$query->addProject($project);
}
@@ -193,8 +196,9 @@ class TimesheetController extends BaseApiController
if (!empty($activities)) {
$query->setActivities($activities);
}
- } elseif (!empty($activity = $paramFetcher->get('activity'))) {
- @trigger_error('Timesheet API parameter "activity" is deprecated and will be removed with 2.0, use "activities" instead', E_USER_DEPRECATED);
+ }
+
+ if (!empty($activity = $paramFetcher->get('activity'))) {
$query->addActivity($activity);
}
diff --git a/src/API/UserController.php b/src/API/UserController.php
index 1d99ad76..73da67c9 100644
--- a/src/API/UserController.php
+++ b/src/API/UserController.php
@@ -33,6 +33,7 @@ use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
/**
* @RouteResource("User")
+ * @SWG\Tag(name="User")
*
* @Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")
*/
diff --git a/src/Command/CreateReleaseCommand.php b/src/Command/CreateReleaseCommand.php
index e5b584f0..0bbc0ea0 100644
--- a/src/Command/CreateReleaseCommand.php
+++ b/src/Command/CreateReleaseCommand.php
@@ -125,7 +125,8 @@ class CreateReleaseCommand extends Command
'Dockerfile',
'phpunit.xml.dist',
'webpack.config.js',
- 'assets/',
+ // this seems to be required, see https://github.com/kevinpapst/kimai2/issues/1586
+ //'assets/',
'tests/',
'var/cache/*',
'var/data/kimai_test.sqlite',
diff --git a/src/Controller/AbstractController.php b/src/Controller/AbstractController.php
index 62c3cfa2..f365ea01 100644
--- a/src/Controller/AbstractController.php
+++ b/src/Controller/AbstractController.php
@@ -18,6 +18,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
/**
* The abstract base controller.
+ * @method null|User getUser()
*/
abstract class AbstractController extends BaseAbstractController implements ServiceSubscriberInterface
{
@@ -42,14 +43,6 @@ abstract class AbstractController extends BaseAbstractController implements Serv
return $this->container->get('logger');
}
- /**
- * @return User|null
- */
- protected function getUser()
- {
- return parent::getUser();
- }
-
/**
* Adds a "successful" flash message to the stack.
*
diff --git a/src/Controller/ActivityController.php b/src/Controller/ActivityController.php
index afb0673d..2cbc9ec6 100644
--- a/src/Controller/ActivityController.php
+++ b/src/Controller/ActivityController.php
@@ -134,25 +134,6 @@ final class ActivityController extends AbstractController
]);
}
- /**
- * @Route(path="/{id}/rate_delete/{rate}", name="admin_activity_rate_delete", methods={"GET"})
- * @Security("is_granted('edit', activity)")
- */
- public function deleteRateAction(Activity $activity, ActivityRate $rate, ActivityRateRepository $repository)
- {
- if ($rate->getActivity() !== $activity) {
- $this->flashError('action.delete.error', ['%reason%' => 'Invalid activity']);
- } else {
- try {
- $repository->deleteRate($rate);
- } catch (\Exception $ex) {
- $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]);
- }
- }
-
- return $this->redirectToRoute('activity_details', ['id' => $activity->getId()]);
- }
-
/**
* @Route(path="/{id}/rate", name="admin_activity_rate_add", methods={"GET", "POST"})
* @Security("is_granted('edit', activity)")
diff --git a/src/Controller/CustomerController.php b/src/Controller/CustomerController.php
index e0005368..9dc09d9e 100644
--- a/src/Controller/CustomerController.php
+++ b/src/Controller/CustomerController.php
@@ -315,25 +315,6 @@ final class CustomerController extends AbstractController
]);
}
- /**
- * @Route(path="/{id}/rate_delete/{rate}", name="admin_customer_rate_delete", methods={"GET"})
- * @Security("is_granted('edit', customer)")
- */
- public function deleteRateAction(Customer $customer, CustomerRate $rate, CustomerRateRepository $repository)
- {
- if ($rate->getCustomer() !== $customer) {
- $this->flashError('action.delete.error', ['%reason%' => 'Invalid customer']);
- } else {
- try {
- $repository->deleteRate($rate);
- } catch (\Exception $ex) {
- $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]);
- }
- }
-
- return $this->redirectToRoute('customer_details', ['id' => $customer->getId()]);
- }
-
/**
* @Route(path="/{id}/rate", name="admin_customer_rate_add", methods={"GET", "POST"})
* @Security("is_granted('edit', customer)")
diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php
index 8401875c..c2c696d8 100644
--- a/src/Controller/ProfileController.php
+++ b/src/Controller/ProfileController.php
@@ -58,7 +58,6 @@ class ProfileController extends AbstractController
}
/**
- * @Route(path="/", name="fos_user_profile_show", methods={"GET"})
* @Route(path="/", name="my_profile", methods={"GET"})
*/
public function profileAction()
diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php
index aaf1c1df..0654f477 100644
--- a/src/Controller/ProjectController.php
+++ b/src/Controller/ProjectController.php
@@ -314,25 +314,6 @@ final class ProjectController extends AbstractController
]);
}
- /**
- * @Route(path="/{id}/rate_delete/{rate}", name="admin_project_rate_delete", methods={"GET"})
- * @Security("is_granted('edit', project)")
- */
- public function deleteRateAction(Project $project, ProjectRate $rate, ProjectRateRepository $repository)
- {
- if ($rate->getProject() !== $project) {
- $this->flashError('action.delete.error', ['%reason%' => 'Invalid project']);
- } else {
- try {
- $repository->deleteRate($rate);
- } catch (\Exception $ex) {
- $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]);
- }
- }
-
- return $this->redirectToRoute('project_details', ['id' => $project->getId()]);
- }
-
/**
* @Route(path="/{id}/rate", name="admin_project_rate_add", methods={"GET", "POST"})
* @Security("is_granted('edit', project)")
diff --git a/src/Entity/Activity.php b/src/Entity/Activity.php
index d5d1d32a..8e95014d 100644
--- a/src/Entity/Activity.php
+++ b/src/Entity/Activity.php
@@ -38,7 +38,6 @@ class Activity implements EntityWithMetaFields
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
-
/**
* @var Project|null
*
@@ -46,25 +45,20 @@ class Activity implements EntityWithMetaFields
* @ORM\JoinColumn(onDelete="CASCADE")
*/
private $project;
-
/**
* @var string
*
- * Do not increase length to more than 190 chars, otherwise "Index column size too large." will be triggered.
- *
* @ORM\Column(name="name", type="string", length=150, nullable=false)
* @Assert\NotBlank()
- * @Assert\Length(min=2, max=150)
+ * @Assert\Length(min=2, max=150, allowEmptyString=false)
*/
private $name;
-
/**
* @var string
*
* @ORM\Column(name="comment", type="text", nullable=true)
*/
private $comment;
-
/**
* @var bool
*
diff --git a/src/Entity/Configuration.php b/src/Entity/Configuration.php
index 6f559dbf..68c94ce2 100644
--- a/src/Entity/Configuration.php
+++ b/src/Entity/Configuration.php
@@ -37,7 +37,8 @@ class Configuration
* @var string
*
* @ORM\Column(name="name", type="string", length=100, nullable=false)
- * @Assert\Length(min=2, max=100)
+ * @Assert\NotNull()
+ * @Assert\Length(min=2, max=100, allowEmptyString=false)
*/
private $name;
diff --git a/src/Entity/Customer.php b/src/Entity/Customer.php
index a92dba92..ed3b43fe 100644
--- a/src/Entity/Customer.php
+++ b/src/Entity/Customer.php
@@ -36,18 +36,14 @@ class Customer implements EntityWithMetaFields
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
-
/**
* @var string
*
- * Do not increase length to more than 190 chars, otherwise "Index column size too large." will be triggered.
- *
* @ORM\Column(name="name", type="string", length=150, nullable=false)
* @Assert\NotBlank()
- * @Assert\Length(min=2, max=150)
+ * @Assert\Length(min=2, max=150, allowEmptyString=false)
*/
private $name;
-
/**
* @var string
*
@@ -55,14 +51,12 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=50)
*/
private $number;
-
/**
* @var string
*
* @ORM\Column(name="comment", type="text", nullable=true)
*/
private $comment;
-
/**
* @var bool
*
@@ -70,7 +64,6 @@ class Customer implements EntityWithMetaFields
* @Assert\NotNull()
*/
private $visible = true;
-
/**
* @var string
*
@@ -78,7 +71,6 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=255)
*/
private $company;
-
/**
* @var string
*
@@ -86,7 +78,6 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=50)
*/
private $vatId;
-
/**
* @var string
*
@@ -94,14 +85,12 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=255)
*/
private $contact;
-
/**
* @var string
*
* @ORM\Column(name="address", type="text", nullable=true)
*/
private $address;
-
/**
* @var string
*
@@ -110,7 +99,6 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=2)
*/
private $country;
-
/**
* @var string
*
@@ -119,7 +107,6 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=3)
*/
private $currency = self::DEFAULT_CURRENCY;
-
/**
* @var string
*
@@ -127,7 +114,6 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=255)
*/
private $phone;
-
/**
* @var string
*
@@ -135,7 +121,6 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=255)
*/
private $fax;
-
/**
* @var string
*
@@ -143,7 +128,6 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=255)
*/
private $mobile;
-
/**
* @var string
*
@@ -153,7 +137,6 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=254)
*/
private $email;
-
/**
* @var string
*
@@ -161,7 +144,6 @@ class Customer implements EntityWithMetaFields
* @Assert\Length(max=255)
*/
private $homepage;
-
/**
* @var string
*
@@ -183,7 +165,6 @@ class Customer implements EntityWithMetaFields
* @ORM\OneToMany(targetEntity="App\Entity\CustomerMeta", mappedBy="customer", cascade={"persist"})
*/
private $meta;
-
/**
* @var Team[]|ArrayCollection
*
diff --git a/src/Entity/InvoiceTemplate.php b/src/Entity/InvoiceTemplate.php
index 19e522aa..c020f4f3 100644
--- a/src/Entity/InvoiceTemplate.php
+++ b/src/Entity/InvoiceTemplate.php
@@ -36,7 +36,7 @@ class InvoiceTemplate
*
* @ORM\Column(name="name", type="string", length=60, nullable=false)
* @Assert\NotBlank()
- * @Assert\Length(min=1, max=60)
+ * @Assert\Length(min=1, max=60, allowEmptyString=false)
*/
private $name;
diff --git a/src/Entity/MetaTableTypeTrait.php b/src/Entity/MetaTableTypeTrait.php
index 07f34c01..1dcdf9d1 100644
--- a/src/Entity/MetaTableTypeTrait.php
+++ b/src/Entity/MetaTableTypeTrait.php
@@ -31,7 +31,8 @@ trait MetaTableTypeTrait
* @var string
*
* @ORM\Column(name="name", type="string", length=50, nullable=false)
- * @Assert\Length(min=2, max=50)
+ * @Assert\NotNull()
+ * @Assert\Length(min=2, max=50, allowEmptyString=false)
*/
private $name;
diff --git a/src/Entity/Project.php b/src/Entity/Project.php
index 7d27367a..6d655d3e 100644
--- a/src/Entity/Project.php
+++ b/src/Entity/Project.php
@@ -48,11 +48,9 @@ class Project implements EntityWithMetaFields
/**
* @var string
*
- * Do not increase length to more than 190 chars, otherwise "Index column size too large." will be triggered.
- *
* @ORM\Column(name="name", type="string", length=150, nullable=false)
* @Assert\NotNull()
- * @Assert\Length(min=2, max=150)
+ * @Assert\Length(min=2, max=150, allowEmptyString=false)
*/
private $name;
/**
diff --git a/src/Entity/Rate.php b/src/Entity/Rate.php
index aac5581f..27aa0fdd 100644
--- a/src/Entity/Rate.php
+++ b/src/Entity/Rate.php
@@ -10,6 +10,7 @@
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
+use Swagger\Annotations as SWG;
use Symfony\Component\Validator\Constraints as Assert;
trait Rate
@@ -27,6 +28,7 @@ trait Rate
*
* @ORM\ManyToOne(targetEntity="App\Entity\User")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=true)
+ * @SWG\Property(ref="#/definitions/User")
*/
private $user;
/**
@@ -36,6 +38,12 @@ trait Rate
* @Assert\GreaterThanOrEqual(0)
*/
private $rate = 0.00;
+ /**
+ * @var float|null
+ *
+ * @ORM\Column(name="internal_rate", type="float", nullable=true)
+ */
+ private $internalRate;
/**
* @var bool
*
@@ -78,6 +86,18 @@ trait Rate
return $this->rate;
}
+ public function setInternalRate(?float $rate): self
+ {
+ $this->internalRate = $rate;
+
+ return $this;
+ }
+
+ public function getInternalRate(): ?float
+ {
+ return $this->internalRate;
+ }
+
public function isFixed(): bool
{
return $this->isFixed;
diff --git a/src/Entity/RateInterface.php b/src/Entity/RateInterface.php
index dbe6527e..6a00b3d0 100644
--- a/src/Entity/RateInterface.php
+++ b/src/Entity/RateInterface.php
@@ -9,12 +9,17 @@
namespace App\Entity;
+/**
+ * @internal
+ */
interface RateInterface
{
public function getUser(): ?User;
public function getRate(): float;
+ public function getInternalRate(): ?float;
+
public function isFixed(): bool;
public function getScore(): int;
diff --git a/src/Entity/Tag.php b/src/Entity/Tag.php
index 2add9e9e..d83b4516 100644
--- a/src/Entity/Tag.php
+++ b/src/Entity/Tag.php
@@ -39,7 +39,7 @@ class Tag
*
* @ORM\Column(name="name", type="string", length=100, nullable=false)
* @Assert\NotBlank()
- * @Assert\Length(min=2, max=100)
+ * @Assert\Length(min=2, max=100, allowEmptyString=false)
* @Assert\Regex(pattern="/,/",match=false,message="Tag name cannot contain comma")
*/
private $name;
diff --git a/src/Entity/Team.php b/src/Entity/Team.php
index c8aeae78..f3b76710 100644
--- a/src/Entity/Team.php
+++ b/src/Entity/Team.php
@@ -39,7 +39,7 @@ class Team
*
* @ORM\Column(name="name", type="string", length=100, nullable=false)
* @Assert\NotBlank()
- * @Assert\Length(min=2, max=100)
+ * @Assert\Length(min=2, max=100, allowEmptyString=false)
*/
private $name;
/**
diff --git a/src/Entity/Timesheet.php b/src/Entity/Timesheet.php
index 6d79706e..8f49d53b 100644
--- a/src/Entity/Timesheet.php
+++ b/src/Entity/Timesheet.php
@@ -131,7 +131,14 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface
private $rate = 0.00;
/**
- * @var float
+ * @var float|null
+ *
+ * @ORM\Column(name="internal_rate", type="float", nullable=true)
+ */
+ private $internalRate;
+
+ /**
+ * @var float|null
*
* @ORM\Column(name="fixed_rate", type="float", nullable=true)
* @Assert\GreaterThanOrEqual(0)
@@ -363,6 +370,18 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface
return $this->rate;
}
+ public function setInternalRate(?float $rate): Timesheet
+ {
+ $this->internalRate = $rate;
+
+ return $this;
+ }
+
+ public function getInternalRate(): ?float
+ {
+ return $this->internalRate;
+ }
+
/**
* @param Tag $tag
* @return Timesheet
diff --git a/src/Entity/UserPreference.php b/src/Entity/UserPreference.php
index d551e514..97bd767a 100644
--- a/src/Entity/UserPreference.php
+++ b/src/Entity/UserPreference.php
@@ -27,6 +27,7 @@ use Symfony\Component\Validator\Constraints as Assert;
class UserPreference
{
public const HOURLY_RATE = 'hourly_rate';
+ public const INTERNAL_RATE = 'internal_rate';
public const SKIN = 'skin';
public const LOCALE = 'language';
public const TIMEZONE = 'timezone';
@@ -51,7 +52,8 @@ class UserPreference
* @var string
*
* @ORM\Column(name="name", type="string", length=50, nullable=false)
- * @Assert\Length(min=2, max=50)
+ * @Assert\NotNull()
+ * @Assert\Length(min=2, max=50, allowEmptyString=false)
*/
private $name;
/**
diff --git a/src/Event/UserPreferenceEvent.php b/src/Event/UserPreferenceEvent.php
index 89bf7ba8..0512faf0 100644
--- a/src/Event/UserPreferenceEvent.php
+++ b/src/Event/UserPreferenceEvent.php
@@ -67,7 +67,7 @@ final class UserPreferenceEvent extends Event
foreach ($this->preferences as $pref) {
if (strtolower($pref->getName()) === strtolower($preference->getName())) {
throw new \InvalidArgumentException(
- 'Cannot add preference, one with the name "' . $preference->getName() . '" is already existing'
+ 'Cannot add user preference, one with the name "' . $preference->getName() . '" is already existing'
);
}
}
diff --git a/src/EventSubscriber/RegistrationSubscriber.php b/src/EventSubscriber/RegistrationSubscriber.php
index 636825dc..3481905a 100644
--- a/src/EventSubscriber/RegistrationSubscriber.php
+++ b/src/EventSubscriber/RegistrationSubscriber.php
@@ -14,6 +14,8 @@ use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Model\UserManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* This class intercepts the registration to make sure:
@@ -21,19 +23,21 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
* - the first-ever registered user will get the SUPER_ADMIN role
* - the user uses the current request locale as initial language setting
*/
-class RegistrationSubscriber implements EventSubscriberInterface
+final class RegistrationSubscriber implements EventSubscriberInterface
{
/**
* @var UserManagerInterface
*/
- protected $userManager;
-
+ private $userManager;
/**
- * @param UserManagerInterface $userManager
+ * @var UrlGeneratorInterface
*/
- public function __construct(UserManagerInterface $userManager)
+ private $router;
+
+ public function __construct(UserManagerInterface $userManager, UrlGeneratorInterface $router)
{
$this->userManager = $userManager;
+ $this->router = $router;
}
/**
@@ -42,7 +46,8 @@ class RegistrationSubscriber implements EventSubscriberInterface
public static function getSubscribedEvents(): array
{
return [
- FOSUserEvents::REGISTRATION_SUCCESS => ['onRegistrationSuccess', 200]
+ FOSUserEvents::REGISTRATION_SUCCESS => ['onRegistrationSuccess', 200],
+ FOSUserEvents::RESETTING_RESET_SUCCESS => ['onResettingSuccess', 200],
];
}
@@ -62,4 +67,12 @@ class RegistrationSubscriber implements EventSubscriberInterface
$user->setLanguage($event->getRequest()->getLocale());
$user->setRoles($roles);
}
+
+ /**
+ * @param FormEvent $event
+ */
+ public function onResettingSuccess(FormEvent $event)
+ {
+ $event->setResponse(new RedirectResponse($this->router->generate('my_profile')));
+ }
}
diff --git a/src/EventSubscriber/UserPreferenceSubscriber.php b/src/EventSubscriber/UserPreferenceSubscriber.php
index ba51a19c..3198ccbd 100644
--- a/src/EventSubscriber/UserPreferenceSubscriber.php
+++ b/src/EventSubscriber/UserPreferenceSubscriber.php
@@ -111,6 +111,15 @@ class UserPreferenceSubscriber implements EventSubscriberInterface
->setOptions($hourlyRateOptions)
->addConstraint(new Range(['min' => 0])),
+ (new UserPreference())
+ ->setName(UserPreference::INTERNAL_RATE)
+ ->setValue(null)
+ ->setOrder(101)
+ ->setType(MoneyType::class)
+ ->setEnabled($enableHourlyRate)
+ ->setOptions(array_merge($hourlyRateOptions, ['label' => 'label.rate_internal', 'required' => false]))
+ ->addConstraint(new Range(['min' => 0])),
+
(new UserPreference())
->setName(UserPreference::TIMEZONE)
->setValue($this->getDefaultTimezone())
diff --git a/src/Export/Base/AbstractSpreadsheetRenderer.php b/src/Export/Base/AbstractSpreadsheetRenderer.php
index b2b2ed55..16f57c8d 100644
--- a/src/Export/Base/AbstractSpreadsheetRenderer.php
+++ b/src/Export/Base/AbstractSpreadsheetRenderer.php
@@ -70,6 +70,7 @@ abstract class AbstractSpreadsheetRenderer
'end' => [],
'duration' => [],
'rate' => [],
+ 'rate_internal' => [],
'user' => [],
'customer' => [],
'project' => [],
@@ -226,6 +227,20 @@ abstract class AbstractSpreadsheetRenderer
};
}
+ if ($showRates && isset($columns['rate_internal']) && !isset($columns['rate_internal']['render'])) {
+ $columns['rate_internal']['render'] = function (Worksheet $sheet, int $row, int $column, ExportItemInterface $entity) {
+ $currency = '';
+ if (null !== $entity->getProject()) {
+ $currency = $entity->getProject()->getCustomer()->getCurrency();
+ }
+ $rate = $entity->getRate();
+ if (method_exists($entity, 'getInternalRate')) {
+ $rate = $entity->getInternalRate();
+ }
+ $this->setRate($sheet, $column, $row, $rate, $currency);
+ };
+ }
+
if (isset($columns['user']) && !isset($columns['user']['render'])) {
$columns['user']['render'] = function (Worksheet $sheet, int $row, int $column, ExportItemInterface $entity) {
$user = '';
@@ -468,7 +483,7 @@ abstract class AbstractSpreadsheetRenderer
}
if (!$showRates) {
- $removes = ['rate', 'fixedRate', 'hourlyRate'];
+ $removes = ['rate', 'fixedRate', 'hourlyRate', 'rate_internal'];
foreach ($removes as $removeMe) {
if (array_key_exists($removeMe, $columns)) {
unset($columns[$removeMe]);
@@ -514,6 +529,7 @@ abstract class AbstractSpreadsheetRenderer
$durationColumn = null;
$rateColumn = null;
+ $internalRateColumn = null;
foreach ($exportItems as $exportItem) {
$entryHeaderColumn = 1;
@@ -523,6 +539,8 @@ abstract class AbstractSpreadsheetRenderer
$durationColumn = $entryHeaderColumn;
} elseif ($label === 'rate') {
$rateColumn = $entryHeaderColumn;
+ } elseif ($label === 'rate_internal') {
+ $internalRateColumn = $entryHeaderColumn;
}
if (!array_key_exists('render', $settings) || !is_callable($settings['render'])) {
@@ -554,6 +572,15 @@ abstract class AbstractSpreadsheetRenderer
$style->getFont()->setBold(true);
}
+ if (null !== $internalRateColumn) {
+ $startCoordinate = $sheet->getCellByColumnAndRow($internalRateColumn, 2)->getCoordinate();
+ $endCoordinate = $sheet->getCellByColumnAndRow($internalRateColumn, $entryHeaderRow - 1)->getCoordinate();
+ $this->setRateTotal($sheet, $internalRateColumn, $entryHeaderRow, $startCoordinate, $endCoordinate);
+ $style = $sheet->getStyleByColumnAndRow($internalRateColumn, $entryHeaderRow);
+ $style->getBorders()->getTop()->setBorderStyle(Border::BORDER_THIN);
+ $style->getFont()->setBold(true);
+ }
+
return $spreadsheet;
}
diff --git a/src/Export/Base/RendererTrait.php b/src/Export/Base/RendererTrait.php
index 9f0ba64f..3128b7d7 100644
--- a/src/Export/Base/RendererTrait.php
+++ b/src/Export/Base/RendererTrait.php
@@ -54,6 +54,7 @@ trait RendererTrait
'activities' => [],
'currency' => $currency,
'rate' => 0,
+ 'rate_internal' => 0,
'duration' => 0,
];
}
@@ -63,6 +64,7 @@ trait RendererTrait
'activity' => $activityName,
'currency' => $currency,
'rate' => 0,
+ 'rate_internal' => 0,
'duration' => 0,
];
}
@@ -73,6 +75,11 @@ trait RendererTrait
}
$summary[$id]['rate'] += $exportItem->getRate();
+ if (method_exists($exportItem, 'getInternalRate')) {
+ $summary[$id]['rate_internal'] += $exportItem->getInternalRate();
+ } else {
+ $summary[$id]['rate_internal'] += $exportItem->getRate();
+ }
$summary[$id]['duration'] += $duration;
$summary[$id]['activities'][$activityId]['rate'] += $exportItem->getRate();
$summary[$id]['activities'][$activityId]['duration'] += $duration;
diff --git a/src/Form/API/ActivityRateApiForm.php b/src/Form/API/ActivityRateApiForm.php
new file mode 100644
index 00000000..64968d8a
--- /dev/null
+++ b/src/Form/API/ActivityRateApiForm.php
@@ -0,0 +1,28 @@
+setDefaults([
+ 'csrf_protection' => false,
+ ]);
+ }
+}
diff --git a/src/Form/API/CustomerRateApiForm.php b/src/Form/API/CustomerRateApiForm.php
new file mode 100644
index 00000000..b815d755
--- /dev/null
+++ b/src/Form/API/CustomerRateApiForm.php
@@ -0,0 +1,28 @@
+setDefaults([
+ 'csrf_protection' => false,
+ ]);
+ }
+}
diff --git a/src/Form/API/ProjectRateApiForm.php b/src/Form/API/ProjectRateApiForm.php
new file mode 100644
index 00000000..33513548
--- /dev/null
+++ b/src/Form/API/ProjectRateApiForm.php
@@ -0,0 +1,28 @@
+setDefaults([
+ 'csrf_protection' => false,
+ ]);
+ }
+}
diff --git a/src/Form/AbstractRateForm.php b/src/Form/AbstractRateForm.php
new file mode 100644
index 00000000..81b53e83
--- /dev/null
+++ b/src/Form/AbstractRateForm.php
@@ -0,0 +1,56 @@
+add('user', UserType::class, [
+ 'required' => false,
+ ])
+ ->add('rate', MoneyType::class, [
+ // documentation is for NelmioApiDocBundle
+ 'documentation' => [
+ 'type' => 'number',
+ 'description' => 'Rate',
+ ],
+ 'label' => 'label.rate',
+ 'attr' => [
+ 'autofocus' => 'autofocus'
+ ],
+ 'currency' => $currency,
+ 'help' => 'help.rate',
+ ])
+ ->add('internalRate', MoneyType::class, [
+ // documentation is for NelmioApiDocBundle
+ 'documentation' => [
+ 'type' => 'number',
+ 'description' => 'Internal rate',
+ ],
+ 'label' => 'label.rate_internal',
+ 'currency' => $currency,
+ 'required' => false,
+ 'help' => 'help.rate_internal',
+ ])
+ ->add('isFixed', YesNoType::class, [
+ 'label' => 'label.fixedRate',
+ 'help' => 'help.fixedRate',
+ ])
+ ;
+ }
+}
diff --git a/src/Form/ActivityRateForm.php b/src/Form/ActivityRateForm.php
index dacd409b..8f1e23cd 100644
--- a/src/Form/ActivityRateForm.php
+++ b/src/Form/ActivityRateForm.php
@@ -11,14 +11,10 @@ namespace App\Form;
use App\Entity\ActivityRate;
use App\Entity\Rate;
-use App\Form\Type\UserType;
-use App\Form\Type\YesNoType;
-use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
-class ActivityRateForm extends AbstractType
+class ActivityRateForm extends AbstractRateForm
{
/**
* {@inheritdoc}
@@ -27,7 +23,7 @@ class ActivityRateForm extends AbstractType
{
$currency = null;
- if ($options['data']) {
+ if (!empty($options['data'])) {
/** @var ActivityRate $rate */
$rate = $options['data'];
@@ -36,21 +32,7 @@ class ActivityRateForm extends AbstractType
}
}
- $builder
- ->add('user', UserType::class, [
- 'required' => false,
- ])
- ->add('rate', MoneyType::class, [
- 'label' => 'label.rate',
- 'attr' => [
- 'autofocus' => 'autofocus'
- ],
- 'currency' => $currency,
- ])
- ->add('isFixed', YesNoType::class, [
- 'label' => 'label.fixedRate'
- ])
- ;
+ $this->addFields($builder, $currency);
}
/**
diff --git a/src/Form/CustomerRateForm.php b/src/Form/CustomerRateForm.php
index aae0c23a..a104356b 100644
--- a/src/Form/CustomerRateForm.php
+++ b/src/Form/CustomerRateForm.php
@@ -11,14 +11,10 @@ namespace App\Form;
use App\Entity\CustomerRate;
use App\Entity\Rate;
-use App\Form\Type\UserType;
-use App\Form\Type\YesNoType;
-use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
-class CustomerRateForm extends AbstractType
+class CustomerRateForm extends AbstractRateForm
{
/**
* {@inheritdoc}
@@ -26,7 +22,8 @@ class CustomerRateForm extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$currency = null;
- if ($options['data']) {
+
+ if (!empty($options['data'])) {
/** @var CustomerRate $rate */
$rate = $options['data'];
@@ -35,21 +32,7 @@ class CustomerRateForm extends AbstractType
}
}
- $builder
- ->add('user', UserType::class, [
- 'required' => false,
- ])
- ->add('rate', MoneyType::class, [
- 'label' => 'label.rate',
- 'attr' => [
- 'autofocus' => 'autofocus'
- ],
- 'currency' => $currency,
- ])
- ->add('isFixed', YesNoType::class, [
- 'label' => 'label.fixedRate'
- ])
- ;
+ $this->addFields($builder, $currency);
}
/**
diff --git a/src/Form/ProjectRateForm.php b/src/Form/ProjectRateForm.php
index 301ed9c2..f66ad9a5 100644
--- a/src/Form/ProjectRateForm.php
+++ b/src/Form/ProjectRateForm.php
@@ -11,14 +11,10 @@ namespace App\Form;
use App\Entity\ProjectRate;
use App\Entity\Rate;
-use App\Form\Type\UserType;
-use App\Form\Type\YesNoType;
-use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
-class ProjectRateForm extends AbstractType
+class ProjectRateForm extends AbstractRateForm
{
/**
* {@inheritdoc}
@@ -26,7 +22,8 @@ class ProjectRateForm extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$currency = null;
- if ($options['data']) {
+
+ if (!empty($options['data'])) {
/** @var ProjectRate $rate */
$rate = $options['data'];
@@ -35,21 +32,7 @@ class ProjectRateForm extends AbstractType
}
}
- $builder
- ->add('user', UserType::class, [
- 'required' => false,
- ])
- ->add('rate', MoneyType::class, [
- 'label' => 'label.rate',
- 'attr' => [
- 'autofocus' => 'autofocus'
- ],
- 'currency' => $currency,
- ])
- ->add('isFixed', YesNoType::class, [
- 'label' => 'label.fixedRate'
- ])
- ;
+ $this->addFields($builder, $currency);
}
/**
diff --git a/src/Invoice/Calculator/AbstractMergedCalculator.php b/src/Invoice/Calculator/AbstractMergedCalculator.php
index e8c83080..bdec849f 100644
--- a/src/Invoice/Calculator/AbstractMergedCalculator.php
+++ b/src/Invoice/Calculator/AbstractMergedCalculator.php
@@ -57,6 +57,11 @@ abstract class AbstractMergedCalculator extends AbstractCalculator
$invoiceItem->setAmount($invoiceItem->getAmount() + $amount);
$invoiceItem->setUser($entry->getUser());
$invoiceItem->setRate($invoiceItem->getRate() + $entry->getRate());
+ if (method_exists($entry, 'getInternalRate')) {
+ $invoiceItem->setInternalRate($invoiceItem->getInternalRate() + $entry->getInternalRate());
+ } else {
+ $invoiceItem->setInternalRate($invoiceItem->getInternalRate() + $entry->getRate());
+ }
$invoiceItem->setDuration($duration);
if (null !== $entry->getFixedRate()) {
diff --git a/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php b/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php
index 4a96c126..d58da6e4 100644
--- a/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php
+++ b/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php
@@ -53,17 +53,11 @@ abstract class AbstractSumInvoiceCalculator extends AbstractMergedCalculator imp
return array_values($invoiceItems);
}
- /**
- * @deprecated since 1.9 - use mergeSumInvoiceItem() instead
- */
- protected function mergeSumTimesheet(InvoiceItem $invoiceItem, InvoiceItemInterface $entry)
- {
- }
-
protected function mergeSumInvoiceItem(InvoiceItem $invoiceItem, InvoiceItemInterface $entry)
{
- @trigger_error('mergeSumTimesheet() is deprecated and will be removed with 2.0', E_USER_DEPRECATED);
-
- $this->mergeSumTimesheet($invoiceItem, $entry);
+ if (method_exists($this, 'mergeSumTimesheet')) {
+ @trigger_error('mergeSumTimesheet() is deprecated and will be removed with 2.0 - use mergeSumInvoiceItem() instead', E_USER_DEPRECATED);
+ $this->mergeSumTimesheet($invoiceItem, $entry);
+ }
}
}
diff --git a/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php b/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php
index 8cebdfbc..32c409fb 100644
--- a/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php
+++ b/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php
@@ -30,6 +30,7 @@ class InvoiceItemDefaultHydrator implements InvoiceItemHydrator
$formatter = $this->model->getFormatter();
$rate = $item->getRate();
+ $internalRate = $item->getInternalRate();
$appliedRate = $item->getHourlyRate();
$amount = $formatter->getFormattedDuration($item->getDuration());
$description = $item->getDescription();
@@ -65,6 +66,9 @@ class InvoiceItemDefaultHydrator implements InvoiceItemHydrator
'entry.rate' => $formatter->getFormattedMoney($appliedRate, $currency),
'entry.rate_nc' => $formatter->getFormattedMoney($appliedRate, null),
'entry.rate_plain' => $appliedRate,
+ 'entry.rate_internal' => $formatter->getFormattedMoney($internalRate, $currency),
+ 'entry.rate_internal_nc' => $formatter->getFormattedMoney($internalRate, null),
+ 'entry.rate_internal_plain' => $internalRate,
'entry.total' => $formatter->getFormattedMoney($rate, $currency),
'entry.total_nc' => $formatter->getFormattedMoney($rate, null),
'entry.total_plain' => $rate,
diff --git a/src/Invoice/InvoiceItem.php b/src/Invoice/InvoiceItem.php
index c44067f4..937cc304 100644
--- a/src/Invoice/InvoiceItem.php
+++ b/src/Invoice/InvoiceItem.php
@@ -30,6 +30,10 @@ final class InvoiceItem
* @var float
*/
private $rate = 0.00;
+ /**
+ * @var float
+ */
+ private $rateInternal = 0.00;
/**
* @var float
*/
@@ -152,6 +156,18 @@ final class InvoiceItem
return $this;
}
+ public function getInternalRate(): float
+ {
+ return $this->rateInternal;
+ }
+
+ public function setInternalRate(float $rateInternal): InvoiceItem
+ {
+ $this->rateInternal = $rateInternal;
+
+ return $this;
+ }
+
public function getAmount(): float
{
return $this->amount;
diff --git a/src/Invoice/InvoiceItemInterface.php b/src/Invoice/InvoiceItemInterface.php
index c27ad021..efd82067 100644
--- a/src/Invoice/InvoiceItemInterface.php
+++ b/src/Invoice/InvoiceItemInterface.php
@@ -14,6 +14,9 @@ use App\Entity\MetaTableTypeInterface;
use App\Entity\Project;
use App\Entity\User;
+/**
+ * @method float|null getInternalRate()
+ */
interface InvoiceItemInterface
{
public function getActivity(): ?Activity;
@@ -26,6 +29,11 @@ interface InvoiceItemInterface
public function getRate(): float;
+ // will be activated with 2.0
+ /*
+ public function getInternalRate(): ?float;
+ */
+
public function getUser(): ?User;
public function getBegin(): ?\DateTime;
diff --git a/src/Invoice/NumberGenerator/ConfigurableNumberGenerator.php b/src/Invoice/NumberGenerator/ConfigurableNumberGenerator.php
index 73163231..d44dfdf0 100644
--- a/src/Invoice/NumberGenerator/ConfigurableNumberGenerator.php
+++ b/src/Invoice/NumberGenerator/ConfigurableNumberGenerator.php
@@ -25,14 +25,14 @@ final class ConfigurableNumberGenerator implements NumberGeneratorInterface
*/
private $repository;
/**
- * @var string
+ * @var SystemConfiguration
*/
- private $format;
+ private $configuration;
public function __construct(InvoiceRepository $repository, SystemConfiguration $configuration)
{
$this->repository = $repository;
- $this->format = $configuration->find('invoice.number_format');
+ $this->configuration = $configuration;
}
/**
@@ -56,9 +56,8 @@ final class ConfigurableNumberGenerator implements NumberGeneratorInterface
*/
public function getInvoiceNumber(): string
{
- $format = $this->format;
+ $format = $this->configuration->find('invoice.number_format');
$invoiceDate = $this->model->getInvoiceDate();
- $timestamp = $invoiceDate->getTimestamp();
$result = $format;
preg_match_all('/{[^}]*?}/', $format, $matches);
diff --git a/src/Migrations/Version20200323163038.php b/src/Migrations/Version20200323163038.php
new file mode 100644
index 00000000..fe489ade
--- /dev/null
+++ b/src/Migrations/Version20200323163038.php
@@ -0,0 +1,42 @@
+getTable('kimai2_activities_rates')->addColumn('internal_rate', 'float', ['notnull' => false]);
+ $schema->getTable('kimai2_projects_rates')->addColumn('internal_rate', 'float', ['notnull' => false]);
+ $schema->getTable('kimai2_customers_rates')->addColumn('internal_rate', 'float', ['notnull' => false]);
+ $schema->getTable('kimai2_timesheet')->addColumn('internal_rate', 'float', ['notnull' => false]);
+ }
+
+ public function down(Schema $schema): void
+ {
+ $schema->getTable('kimai2_timesheet')->dropColumn('internal_rate');
+ $schema->getTable('kimai2_activities_rates')->dropColumn('internal_rate');
+ $schema->getTable('kimai2_projects_rates')->dropColumn('internal_rate');
+ $schema->getTable('kimai2_customers_rates')->dropColumn('internal_rate');
+ }
+}
diff --git a/src/Migrations/Version20200323163039.php b/src/Migrations/Version20200323163039.php
new file mode 100644
index 00000000..3c1d33b4
--- /dev/null
+++ b/src/Migrations/Version20200323163039.php
@@ -0,0 +1,35 @@
+addSql('UPDATE kimai2_timesheet SET internal_rate = rate');
+ }
+
+ public function down(Schema $schema): void
+ {
+ }
+}
diff --git a/src/Model/TimesheetCountedStatistic.php b/src/Model/TimesheetCountedStatistic.php
index f4e9ac5e..0c7d1f45 100644
--- a/src/Model/TimesheetCountedStatistic.php
+++ b/src/Model/TimesheetCountedStatistic.php
@@ -23,6 +23,10 @@ class TimesheetCountedStatistic
* @var float
*/
protected $recordRate = 0.0;
+ /**
+ * @var float
+ */
+ protected $recordInternalRate = 0.0;
/**
* Returns the total amount of included timesheet records.
@@ -86,4 +90,25 @@ class TimesheetCountedStatistic
return $this;
}
+
+ /**
+ * Returns the total internal rate of all included timesheet records.
+ *
+ * @return float
+ */
+ public function getRecordInternalRate()
+ {
+ return $this->recordInternalRate;
+ }
+
+ /**
+ * @param float $recordInternalRate
+ * @return $this
+ */
+ public function setRecordInternalRate($recordInternalRate)
+ {
+ $this->recordInternalRate = (float) $recordInternalRate;
+
+ return $this;
+ }
}
diff --git a/src/Repository/ActivityRepository.php b/src/Repository/ActivityRepository.php
index cc04c452..0efb2519 100644
--- a/src/Repository/ActivityRepository.php
+++ b/src/Repository/ActivityRepository.php
@@ -97,6 +97,7 @@ class ActivityRepository extends EntityRepository
->addSelect('COUNT(t.id) as recordAmount')
->addSelect('SUM(t.duration) as recordDuration')
->addSelect('SUM(t.rate) as recordRate')
+ ->addSelect('SUM(t.internalRate) as recordInternalRate')
->from(Timesheet::class, 't')
->where('t.activity = :activity')
;
@@ -107,6 +108,7 @@ class ActivityRepository extends EntityRepository
$stats->setRecordAmount($timesheetResult[0]['recordAmount']);
$stats->setRecordDuration($timesheetResult[0]['recordDuration']);
$stats->setRecordRate($timesheetResult[0]['recordRate']);
+ $stats->setRecordInternalRate($timesheetResult[0]['recordInternalRate']);
}
return $stats;
diff --git a/src/Repository/CustomerRepository.php b/src/Repository/CustomerRepository.php
index ee252fed..2d197598 100644
--- a/src/Repository/CustomerRepository.php
+++ b/src/Repository/CustomerRepository.php
@@ -89,6 +89,7 @@ class CustomerRepository extends EntityRepository
->addSelect('COUNT(t.id) as recordAmount')
->addSelect('SUM(t.duration) as recordDuration')
->addSelect('SUM(t.rate) as recordRate')
+ ->addSelect('SUM(t.internalRate) as recordInternalRate')
->from(Timesheet::class, 't')
->join(Project::class, 'p', Query\Expr\Join::WITH, 't.project = p.id')
->andWhere('p.customer = :customer')
@@ -99,6 +100,7 @@ class CustomerRepository extends EntityRepository
$stats->setRecordAmount($timesheetResult[0]['recordAmount']);
$stats->setRecordDuration($timesheetResult[0]['recordDuration']);
$stats->setRecordRate($timesheetResult[0]['recordRate']);
+ $stats->setRecordInternalRate($timesheetResult[0]['recordInternalRate']);
}
$qb = $this->getEntityManager()->createQueryBuilder();
diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php
index 6f4ff0b2..0fb8c3c5 100644
--- a/src/Repository/ProjectRepository.php
+++ b/src/Repository/ProjectRepository.php
@@ -84,6 +84,7 @@ class ProjectRepository extends EntityRepository
->addSelect('COUNT(t.id) as recordAmount')
->addSelect('SUM(t.duration) as recordDuration')
->addSelect('SUM(t.rate) as recordRate')
+ ->addSelect('SUM(t.internalRate) as recordInternalRate')
->andWhere('t.project = :project')
->setParameter('project', $project)
;
@@ -103,6 +104,7 @@ class ProjectRepository extends EntityRepository
$stats->setRecordAmount($timesheetResult[0]['recordAmount']);
$stats->setRecordDuration($timesheetResult[0]['recordDuration']);
$stats->setRecordRate($timesheetResult[0]['recordRate']);
+ $stats->setRecordInternalRate($timesheetResult[0]['recordInternalRate']);
}
$qb = $this->getEntityManager()->createQueryBuilder();
diff --git a/src/Timesheet/Calculator/RateCalculator.php b/src/Timesheet/Calculator/RateCalculator.php
index 5771023d..e7a5aa3b 100644
--- a/src/Timesheet/Calculator/RateCalculator.php
+++ b/src/Timesheet/Calculator/RateCalculator.php
@@ -44,45 +44,70 @@ class RateCalculator implements CalculatorInterface
{
if (null === $record->getEnd()) {
$record->setRate(0);
+ $record->setInternalRate(0);
return;
}
$fixedRate = $record->getFixedRate();
$hourlyRate = $record->getHourlyRate();
+ $fixedInternalRate = null;
+ $internalRate = null;
- if (null === $fixedRate && null === $hourlyRate) {
- $rate = $this->getBestFittingRate($record);
+ $rate = $this->getBestFittingRate($record);
- if (null !== $rate) {
- if ($rate->isFixed()) {
- $fixedRate = $rate->getRate();
- } else {
- $hourlyRate = $rate->getRate();
+ if (null !== $rate) {
+ if ($rate->isFixed()) {
+ $fixedRate = $fixedRate ?? $rate->getRate();
+ $fixedInternalRate = $rate->getRate();
+ if (null !== $rate->getInternalRate()) {
+ $fixedInternalRate = $rate->getInternalRate();
+ }
+ } else {
+ $hourlyRate = $hourlyRate ?? $rate->getRate();
+ $internalRate = $rate->getRate();
+ if (null !== $rate->getInternalRate()) {
+ $internalRate = $rate->getInternalRate();
}
}
}
if (null !== $fixedRate) {
- $record->setRate($fixedRate);
$record->setFixedRate($fixedRate);
+ $record->setRate($fixedRate);
+ if (null === $fixedInternalRate) {
+ $fixedInternalRate = (float) $record->getUser()->getPreferenceValue(UserPreference::INTERNAL_RATE, $fixedRate);
+ }
+ $record->setInternalRate($fixedInternalRate);
return;
}
+ // user preferences => fallback if nothing else was configured
if (null === $hourlyRate) {
$hourlyRate = (float) $record->getUser()->getPreferenceValue(UserPreference::HOURLY_RATE, 0.00);
}
+ if (null === $internalRate) {
+ $internalRate = $record->getUser()->getPreferenceValue(UserPreference::INTERNAL_RATE, 0.00);
+ if (null === $internalRate) {
+ $internalRate = $hourlyRate;
+ } else {
+ $internalRate = (float) $internalRate;
+ }
+ }
$factor = $this->getRateFactor($record);
- $hourlyRate = (float) ($hourlyRate * $factor);
+ $factoredHourlyRate = (float) ($hourlyRate * $factor);
+ $factoredInternalRate = (float) ($internalRate * $factor);
$totalRate = 0;
+ $totalInternalRate = 0;
if (null !== $record->getDuration()) {
- $totalRate = Util::calculateRate($hourlyRate, $record->getDuration());
+ $totalRate = Util::calculateRate($factoredHourlyRate, $record->getDuration());
+ $totalInternalRate = Util::calculateRate($factoredInternalRate, $record->getDuration());
}
-
- $record->setHourlyRate($hourlyRate);
+ $record->setHourlyRate($factoredHourlyRate);
+ $record->setInternalRate($totalInternalRate);
$record->setRate($totalRate);
}
diff --git a/templates/activity/details.html.twig b/templates/activity/details.html.twig
index 78fd0f63..40422bcc 100644
--- a/templates/activity/details.html.twig
+++ b/templates/activity/details.html.twig
@@ -77,64 +77,7 @@
{% endif %}
{% if can_edit %}
- {% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
- {% import "macros/widgets.html.twig" as widgets %}
- {% block box_attributes %}id="activity_rates_box"{% endblock %}
- {% block box_title %}
- {{ 'rates.title'|trans }}
- {% endblock %}
- {% block box_tools %}
-
- {% endblock %}
- {% block box_body %}
- {% if rates is empty %}
- {{ 'rates.empty'|trans }}
- {% else %}
-
-
-
-
- {{ 'label.user'|trans }}
-
-
- {{ 'label.hourlyRate'|trans }}
-
-
- {{ 'label.fixedRate'|trans }}
-
-
-
-
-
- {% for rate in rates %}
-
-
- {% if rate.user is not null %}
- {{ widgets.user_avatar(rate.user) }}
- {% else %}
- –
- {% endif %}
-
-
- {% if not rate.fixed %}
- {{ rate.rate|money(currency) }}
- {% endif %}
-
-
- {% if rate.fixed %}
- {{ rate.rate|money(currency) }}
- {% endif %}
-
-
-
-
-
- {% endfor %}
-
-
- {% endif %}
- {% endblock %}
- {% endembed %}
+ {{ include('embeds/rates-table.html.twig', {'id': 'activity_rates_box', 'entity': activity, 'create_url': path('admin_activity_rate_add', {'id': activity.id}), 'delete_route': 'delete_activity_rate', 'currency': currency}) }}
{% endif %}
{% endblock %}
@@ -143,7 +86,7 @@
{{ parent() }}
{% endblock %}
diff --git a/templates/activity/embed_budget.html.twig b/templates/activity/embed_budget.html.twig
index 45439304..f3576db9 100644
--- a/templates/activity/embed_budget.html.twig
+++ b/templates/activity/embed_budget.html.twig
@@ -11,6 +11,11 @@
'%duration%': '' ~ stats.recordDuration|duration ~ ' '
} %}
+ {% set currency = null %}
+ {% if activity.project is not null %}
+ {% set currency = activity.project.customer.currency %}
+ {% endif %}
+
{% if activity.project is not null %}
{% set params = params|merge({
'%project%': '' ~ activity.project.name ~ ' ',
@@ -20,12 +25,9 @@
{{ 'admin_activity.short_stats'|trans(params)|raw }}
+ {{ 'label.rate_internal'|trans }}: {{ stats.recordInternalRate|money(currency) }}.
- {% set currency = null %}
- {% if activity.project is not null %}
- {% set currency = activity.project.customer.currency %}
- {% endif %}
{{ progress.progressbar(activity.budget, stats.recordRate, 'label.budget'|trans, stats.recordRate|money(currency) ~ ' / ' ~ activity.budget|money(currency) ) }}
{{ progress.progressbar(activity.timeBudget, stats.recordDuration, 'label.timeBudget'|trans, stats.recordDuration|duration ~ ' / ' ~ activity.timeBudget|duration ) }}
diff --git a/templates/customer/details.html.twig b/templates/customer/details.html.twig
index 52f907bc..d1111390 100644
--- a/templates/customer/details.html.twig
+++ b/templates/customer/details.html.twig
@@ -125,64 +125,7 @@
{% endif %}
{% if can_edit %}
- {% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
- {% import "macros/widgets.html.twig" as widgets %}
- {% block box_attributes %}id="customer_rates_box"{% endblock %}
- {% block box_title %}
- {{ 'rates.title'|trans }}
- {% endblock %}
- {% block box_tools %}
-
- {% endblock %}
- {% block box_body %}
- {% if rates is empty %}
- {{ 'rates.empty'|trans }}
- {% else %}
-
-
-
-
- {{ 'label.user'|trans }}
-
-
- {{ 'label.hourlyRate'|trans }}
-
-
- {{ 'label.fixedRate'|trans }}
-
-
-
-
-
- {% for rate in rates %}
-
-
- {% if rate.user is not null %}
- {{ widgets.user_avatar(rate.user) }}
- {% else %}
- –
- {% endif %}
-
-
- {% if not rate.fixed %}
- {{ rate.rate|money(customer.currency) }}
- {% endif %}
-
-
- {% if rate.fixed %}
- {{ rate.rate|money(customer.currency) }}
- {% endif %}
-
-
-
-
-
- {% endfor %}
-
-
- {% endif %}
- {% endblock %}
- {% endembed %}
+ {{ include('embeds/rates-table.html.twig', {'id': 'customer_rates_box', 'entity': customer, 'create_url': path('admin_customer_rate_add', {'id': customer.id}), 'delete_route': 'delete_customer_rate', 'currency': customer.currency}) }}
{% endif %}
{% if teams is not null %}
@@ -207,7 +150,7 @@
{{ parent() }}
{% endblock %}
diff --git a/templates/customer/embed_budget.html.twig b/templates/customer/embed_budget.html.twig
index 898c3102..c30511f6 100644
--- a/templates/customer/embed_budget.html.twig
+++ b/templates/customer/embed_budget.html.twig
@@ -12,11 +12,14 @@
'%rate%': '' ~ stats.recordRate|money ~ ' '
} %}
+ {% set currency = customer.currency %}
+
{{ 'admin_customer.short_stats'|trans(params)|raw }}
+ {{ 'label.rate_internal'|trans }}: {{ stats.recordInternalRate|money(currency) }}.
- {{ progress.progressbar(customer.budget, stats.recordRate, 'label.budget'|trans, stats.recordRate|money(customer.currency) ~ ' / ' ~ customer.budget|money(customer.currency) ) }}
+ {{ progress.progressbar(customer.budget, stats.recordRate, 'label.budget'|trans, stats.recordRate|money(currency) ~ ' / ' ~ customer.budget|money(currency) ) }}
{{ progress.progressbar(customer.timeBudget, stats.recordDuration, 'label.timeBudget'|trans, stats.recordDuration|duration ~ ' / ' ~ customer.timeBudget|duration ) }}
{% endblock %}
{% endembed %}
diff --git a/templates/embeds/rates-table.html.twig b/templates/embeds/rates-table.html.twig
new file mode 100644
index 00000000..589bfb2a
--- /dev/null
+++ b/templates/embeds/rates-table.html.twig
@@ -0,0 +1,60 @@
+{% embed '@AdminLTE/Widgets/box-widget.html.twig' with {'id': id, 'entity': entity, 'create_url': create_url, 'delete_route': delete_route, 'currency': currency} %}
+ {% import "macros/widgets.html.twig" as widgets %}
+ {% block box_attributes %}id="{{ id }}"{% endblock %}
+ {% block box_title %}
+ {{ 'rates.title'|trans }}
+ {% endblock %}
+ {% block box_tools %}
+
+ {% endblock %}
+ {% block box_body %}
+ {% if rates is empty %}
+ {{ 'rates.empty'|trans }}
+ {% else %}
+
+
+
+
+ {{ 'label.user'|trans }}
+
+
+ {{ 'label.hourlyRate'|trans }}
+
+
+ {{ 'label.rate_internal'|trans }}
+
+
+ {{ 'label.fixedRate'|trans }}
+
+
+
+
+
+ {% for rate in rates %}
+
+
+ {% if rate.user is not null %}
+ {{ widgets.user_avatar(rate.user) }}
+ {% else %}
+ –
+ {% endif %}
+
+
+ {{ rate.rate|money(currency) }}
+
+
+ {{ rate.internalRate|money(currency) }}
+
+
+ {{ widgets.label_boolean(rate.fixed) }}
+
+
+
+
+
+ {% endfor %}
+
+
+ {% endif %}
+ {% endblock %}
+{% endembed %}
\ No newline at end of file
diff --git a/templates/export/actions.html.twig b/templates/export/actions.html.twig
index ab935549..22aba17c 100644
--- a/templates/export/actions.html.twig
+++ b/templates/export/actions.html.twig
@@ -2,9 +2,10 @@
{% macro export(view) %}
{% import "macros/widgets.html.twig" as widgets %}
- {% set actions = {} %}
+ {% set actions = {'visibility': '#modal_export'} %}
+
{% if view == 'preview' %}
- {% set actions = {'off': {'id':'export-toggle-button'}} %}
+ {% set actions = actions|merge({'off': {'id':'export-toggle-button'}}) %}
{% endif %}
{% set actions = actions|merge({'help': {'url': 'export.html'|docu_link, 'target': '_blank'}}) %}
diff --git a/templates/export/index.html.twig b/templates/export/index.html.twig
index 05ec9565..bb0b6653 100644
--- a/templates/export/index.html.twig
+++ b/templates/export/index.html.twig
@@ -5,15 +5,16 @@
{% import "export/actions.html.twig" as actions %}
{% set columns = {
- 'date': 'alwaysVisible',
- 'user': 'hidden-xs hidden-sm',
- 'project': 'hidden-xs hidden-sm',
- 'activity': 'hidden-xs hidden-sm',
- 'description': 'hidden-xs hidden-sm',
- 'unit_price': 'hidden-xs',
- 'duration': '',
- 'total_rate': '',
- 'exported': 'alwaysVisible',
+ 'date': {'class': 'alwaysVisible text-nowrap', 'orderBy': false},
+ 'user': {'class': 'hidden-xs hidden-sm', 'orderBy': false},
+ 'project': {'class': 'hidden-xs hidden-sm', 'orderBy': false},
+ 'activity': {'class': 'hidden-xs hidden-sm hidden-md', 'orderBy': false},
+ 'description': {'class': 'hidden-xs hidden-sm hidden-md timesheet-description', 'orderBy': false},
+ 'unit_price': {'class': 'hidden hidden-xs text-nowrap', 'orderBy': false},
+ 'duration': {'class': 'text-nowrap', 'orderBy': false},
+ 'rate_internal': {'class': 'hidden-xs text-nowrap', 'orderBy': false},
+ 'total_rate': {'class': 'text-nowrap', 'orderBy': false},
+ 'exported': {'class': 'alwaysVisible', 'orderBy': false},
} %}
{% set tableName = 'export' %}
@@ -22,6 +23,10 @@
{% block page_subtitle %}{{ 'export.subtitle'|trans }}{% endblock %}
{% block page_actions %}{{ actions.export((preview_show ? 'preview' : 'index')) }}{% endblock %}
+{% block main_before %}
+ {{ tables.data_table_column_modal(tableName, columns) }}
+{% endblock %}
+
{% block main %}
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
@@ -84,24 +89,25 @@
{% else %}
{{ tables.datatable_header(tableName, columns, query, {}) }}
{% set totalAmount = {} %}
+ {% set totalInternalAmount = {} %}
{% set totalDuration = 0 %}
{% for entry in entries %}
{% set currency = entry.project.customer.currency %}
{% set duration = entry.duration|duration %}
{% if totalAmount[currency] is not defined %}
{% set totalAmount = totalAmount|merge({(currency): 0}) %}
+ {% set totalInternalAmount = totalInternalAmount|merge({(currency): 0}) %}
{% endif %}
{% if entry.fixedRate is not null %}
{% set rate = entry.fixedRate %}
- {% set duration = 1 %}
- {% set totalDuration = totalDuration + 1 %}
{% else %}
{% set rate = entry.hourlyRate %}
- {% set totalDuration = totalDuration + entry.duration %}
{% endif %}
+ {% set totalDuration = totalDuration + entry.duration %}
{% set totalAmount = totalAmount|merge({(currency): totalAmount[currency] + entry.rate}) %}
+ {% set totalInternalAmount = totalInternalAmount|merge({(currency): totalInternalAmount[currency] + entry.internalRate}) %}
- {{ entry.begin|date_short }}
+ {{ entry.begin|date_short }}
{{ widgets.label_user(entry.user) }}
{{ widgets.label_project(entry.project) }}
@@ -109,19 +115,22 @@
{{ widgets.label_customer(entry.project.customer) }}
{{ widgets.label_activity(entry.activity) }}
-
+
{{ entry.description|escape|desc2html }}
-
+
{{ rate|money(currency) }}
-
+
{{ duration }}
-
+
+ {{ entry.internalRate|money(currency) }}
+
+
{{ entry.rate|money(currency) }}
-
+
{% if is_granted('edit_export', entry) %}
{% if entry.exported %}
{% endfor %}
-
- {{ totalDuration|duration }}
-
+
+
+
+
+
+
+
+ {{ totalDuration|duration }}
+
+
+ {% for currency, amount in totalInternalAmount %}
+ {{ amount|money(currency) }}
+ {% if not loop.last %} {% endif %}
+ {% endfor %}
+
+
{% for currency, amount in totalAmount %}
{{ amount|money(currency) }}
{% if not loop.last %} {% endif %}
{% endfor %}
-
+
{{ tables.data_table_footer(entries) }}
{% endif %}
diff --git a/templates/export/pdf-layout.html.twig b/templates/export/pdf-layout.html.twig
new file mode 100644
index 00000000..087c6768
--- /dev/null
+++ b/templates/export/pdf-layout.html.twig
@@ -0,0 +1,305 @@
+{% set showUserColumn = showUserColumn|default(true) %}
+{% set showInternalRate = showInternalRate|default(false) %}
+{% set showRateColumn = showRateColumn|default(true) %}
+{% set showRateBudget = showRateBudget|default(false) %}
+{% set showTimeBudget = showTimeBudget|default(false) %}
+{% set decimal = decimal|default(false) %}
+{% if query.user %}
+ {# this is only triggered, if a user exports from his personal timesheet screen #}
+ {% set showUserColumn = false %}
+ {# if exporting via the admin screen, users without view_rate_own_timesheet might still see their own rates - maybe merge view_rate_own_timesheet and view_rate_other_timesheet into a new view_rate permission? #}
+ {% set showRateColumn = is_granted('view_rate_own_timesheet') %}
+{% endif %}
+
+
+ {% block styles %}
+
+ {% endblock %}
+
+
+{% block pdf_footer %}
+
+{% endblock %}
+{% block summary %}
+ {{ 'export.document_title'|trans }}
+
+ {{ 'export.period'|trans }}:
+ {{ query.begin|date_short }} - {{ query.end|date_short }}
+
+ {{ 'export.summary'|trans }}
+
+
+
+ {{ 'label.customer'|trans }}
+ {{ 'label.project'|trans }}
+ {% if showTimeBudget %}
+ {{ 'label.timeBudget'|trans }}
+ {% endif %}
+ {% if showRateBudget %}
+ {{ 'label.budget'|trans }}
+ {% endif %}
+ {{ 'label.duration'|trans }}
+ {% if showRateColumn %}
+ {% if showInternalRate %}
+ {{ 'label.rate_internal'|trans }}
+ {% endif %}
+ {{ 'label.rate'|trans }}
+ {% endif %}
+
+
+
+ {% set customer = null %}
+ {% set customerDuration = 0 %}
+ {% set customerRate = 0 %}
+ {% set customerInternalRate = 0 %}
+ {% set customerCurrency = null %}
+ {% set customerCount = 0 %}
+ {% for id, summary in summaries %}
+ {% if customer is same as(null) %}
+ {% set customer = summary.customer %}
+ {% set customerCurrency = summary.currency %}
+ {% endif %}
+ {% if customer is not same as(summary.customer) %}
+
+
+
+ {% if showTimeBudget %}
+
+ {% endif %}
+ {% if showRateBudget %}
+
+ {% endif %}
+ {{ customerDuration|duration(decimal) }}
+ {% if showRateColumn %}
+ {% if showInternalRate %}
+ {{ customerInternalRate|money(customerCurrency) }}
+ {% endif %}
+ {{ customerRate|money(customerCurrency) }}
+ {% endif %}
+
+ {% set customerCurrency = summary.currency %}
+ {% set customer = summary.customer %}
+ {% set customerDuration = 0 %}
+ {% set customerRate = 0 %}
+ {% set customerInternalRate = 0 %}
+ {% set customerCount = 0 %}
+ {% endif %}
+
+ {{ summary.customer }}
+ {{ summary.project }}
+ {% if showTimeBudget %}
+
+ {% if budgets[id] is defined and budgets[id].time_left > 0 %}
+ {{ budgets[id].time_left|duration(decimal) }}
+ {% endif %}
+
+ {% endif %}
+ {% if showRateBudget %}
+
+ {% if budgets[id] is defined and budgets[id].money_left > 0 %}
+ {{ budgets[id].money_left|money(summary.currency) }}
+ {% endif %}
+
+ {% endif %}
+ {{ summary.duration|duration(decimal) }}
+ {% if showRateColumn %}
+ {% if showInternalRate %}
+ {{ summary.rate_internal|money(summary.currency) }}
+ {% endif %}
+ {{ summary.rate|money(summary.currency) }}
+ {% endif %}
+
+ {% set customerDuration = customerDuration + summary.duration %}
+ {% set customerRate = customerRate + summary.rate %}
+ {% set customerInternalRate = customerInternalRate + summary.rate_internal %}
+ {% set customerCount = customerCount + 1 %}
+ {% endfor %}
+ {% if customer is not same as(null) %}
+
+
+ {% if showTimeBudget %}
+
+ {% endif %}
+ {% if showRateBudget %}
+
+ {% endif %}
+ {{ customerDuration|duration(decimal) }}
+ {% if showRateColumn %}
+ {% if showInternalRate %}
+ {{ customerInternalRate|money(customerCurrency) }}
+ {% endif %}
+ {{ customerRate|money(customerCurrency) }}
+ {% endif %}
+
+ {% endif %}
+
+
+
+
+{% endblock %}
+{% block items %}
+ {{ 'export.full_list'|trans }}
+
+ {% set duration = 0 %}
+ {% set rate = 0 %}
+ {% set rateInternal = 0 %}
+ {% set currency = false %}
+
+
+
+ {{ 'label.date'|trans }}
+ {% if showUserColumn %}
+ {{ 'label.user'|trans }}
+ {% endif %}
+ {{ 'label.description'|trans }}
+ {{ 'label.duration'|trans }}
+ {% if showRateColumn %}
+ {% if showInternalRate %}
+ {{ 'label.rate_internal'|trans }}
+ {% endif %}
+ {{ 'label.rate'|trans }}
+ {% endif %}
+
+
+
+ {% for entry in entries %}
+ {% set duration = duration + entry.duration %}
+ {% if currency is same as(false) %}
+ {% set currency = entry.project.customer.currency %}
+ {% endif %}
+ {% if currency is not same as(entry.project.customer.currency) %}
+ {% set currency = null %}
+ {% endif %}
+
+
+ {{ entry.begin|date_time }}
+ {% if entry.end %}
+
+ {{ entry.end|date_time }}
+ {% endif %}
+
+ {% if showUserColumn %}
+ {{ entry.user.displayName }}
+ {% endif %}
+
+ {{ entry.project.customer.name }} - {{ entry.project.name }}{% if entry.activity is not null %} - {{ entry.activity.name }}{% endif %}
+ {% if entry.description is not empty %}
+
+ {{ entry.description|escape|desc2html }}
+ {% endif %}
+
+ {{ entry.duration|duration(decimal) }}
+ {% if showRateColumn %}
+ {% if is_granted('view_rate', entry) %}
+ {% set rate = rate + entry.rate %}
+ {% set rateInternal = rateInternal + entry.internalRate %}
+ {% set entryRate = entry.rate|money(entry.project.customer.currency) %}
+ {% set entryRateInternal = entry.internalRate|money(entry.project.customer.currency) %}
+ {% else %}
+ {% set entryRate = '–' %}
+ {% set entryRateInternal = '–' %}
+ {% endif %}
+ {% if showInternalRate %}
+ {{ entryRate }}
+ {% endif %}
+ {{ entryRateInternal }}
+ {% endif %}
+
+ {% endfor %}
+
+ {% if showUserColumn %}
+
+ {% else %}
+
+ {% endif %}
+ {{ duration|duration(decimal) }}
+ {% if showRateColumn %}
+ {% if showInternalRate %}
+ {% if currency is not null %}{{ rateInternal|money(currency) }}{% endif %}
+ {% endif %}
+ {% if currency is not null %}{{ rate|money(currency) }}{% endif %}
+ {% endif %}
+
+
+
+{% endblock %}
+
+
diff --git a/templates/export/renderer/default-budget.pdf.twig b/templates/export/renderer/default-budget.pdf.twig
index b0d9b139..4a809656 100644
--- a/templates/export/renderer/default-budget.pdf.twig
+++ b/templates/export/renderer/default-budget.pdf.twig
@@ -1,12 +1,11 @@
-{% extends 'export/renderer/default.pdf.twig' %}
-{% block summary %}
- {% for budget in budgets %}
- {% if budget.money_left > 0 %}
- {% set showRateBudget = true %}
- {% endif %}
- {% if budget.time_left > 0 %}
- {% set showTimeBudget = true %}
- {% endif %}
- {% endfor %}
- {{ parent() }}
-{% endblock %}
\ No newline at end of file
+{% extends 'export/pdf-layout.html.twig' %}
+{% set showRateBudget = false %}
+{% set showTimeBudget = false %}
+{% for budget in budgets %}
+ {% if budget.money_left > 0 %}
+ {% set showRateBudget = true %}
+ {% endif %}
+ {% if budget.time_left > 0 %}
+ {% set showTimeBudget = true %}
+ {% endif %}
+{% endfor %}
diff --git a/templates/export/renderer/default-internal.pdf.twig b/templates/export/renderer/default-internal.pdf.twig
new file mode 100644
index 00000000..802b9db5
--- /dev/null
+++ b/templates/export/renderer/default-internal.pdf.twig
@@ -0,0 +1,2 @@
+{% extends 'export/pdf-layout.html.twig' %}
+{% set showInternalRate = true %}
diff --git a/templates/export/renderer/default.html.twig b/templates/export/renderer/default.html.twig
index af53e55e..c5cb3202 100644
--- a/templates/export/renderer/default.html.twig
+++ b/templates/export/renderer/default.html.twig
@@ -51,6 +51,7 @@
'hourlyRate': false,
'fixedRate': false,
'duration': 'label.duration',
+ 'rate_internal': 'label.rate_internal',
'rate': 'label.rate',
}) %}
@@ -63,38 +64,40 @@
document.getElementById('summary-show').addEventListener('click', function(event) {
document.getElementById('export-summary').style.display = event.target.checked ? 'block' : 'none';
});
+ {% if showTimeBudget %}
document.getElementById('summary-timeBudget').addEventListener('click', function(event) {
var cells = document.getElementsByClassName('export-timeBudget');
for (var columnCell of cells) {
columnCell.style.display = event.target.checked ? 'table-cell' : 'none';
}
});
+ {% endif %}
+ {% if showRateBudget %}
document.getElementById('summary-budget').addEventListener('click', function(event) {
var cells = document.getElementsByClassName('export-budget');
for (var columnCell of cells) {
columnCell.style.display = event.target.checked ? 'table-cell' : 'none';
}
});
+ {% endif %}
document.getElementById('summary-duration').addEventListener('click', function(event) {
- if (!document.getElementById('summary-rate').checked) {
- return;
- }
- document.getElementById('summary-rate').disabled = !event.target.checked;
var cells = document.getElementsByClassName('summary-duration');
for (var columnCell of cells) {
columnCell.style.display = event.target.checked ? 'table-cell' : 'none';
}
});
document.getElementById('summary-rate').addEventListener('click', function(event) {
- if (!document.getElementById('summary-duration').checked) {
- return;
- }
- document.getElementById('summary-duration').disabled = !event.target.checked;
var cells = document.getElementsByClassName('summary-rate');
for (var columnCell of cells) {
columnCell.style.display = event.target.checked ? 'table-cell' : 'none';
}
});
+ document.getElementById('summary-rate-internal').addEventListener('click', function(event) {
+ var cells = document.getElementsByClassName('summary-rate-internal');
+ for (var columnCell of cells) {
+ columnCell.style.display = event.target.checked ? 'table-cell' : 'none';
+ }
+ });
document.getElementById('summary-show').addEventListener('click', function(event) {
document.getElementById('export-summary').style.display = event.target.checked ? 'block' : 'none';
});
@@ -177,6 +180,12 @@
{{ 'label.duration'|trans }}
+
+
+
+ {{ 'label.rate_internal'|trans }}
+
+
@@ -232,6 +241,7 @@
{{ 'label.budget'|trans }}
{% endif %}
{{ 'label.duration'|trans }}
+ {{ 'label.rate_internal'|trans }}
{{ 'label.rate'|trans }}
@@ -239,6 +249,7 @@
{% set customer = null %}
{% set customerDuration = 0 %}
{% set customerRate = 0 %}
+ {% set customerInternalRate = 0 %}
{% set customerCurrency = null %}
{% for id, summary in summaries %}
{% if customer is same as(null) %}
@@ -247,7 +258,7 @@
{% endif %}
{% if customer is not same as(summary.customer) %}
-
+
{% if showTimeBudget %}
{% endif %}
@@ -255,12 +266,14 @@
{% endif %}
{{ customerDuration|duration(decimal) }}
+ {{ customerInternalRate|money(customerCurrency) }}
{{ customerRate|money(customerCurrency) }}
{% set customerCurrency = summary.currency %}
{% set customer = summary.customer %}
{% set customerDuration = 0 %}
{% set customerRate = 0 %}
+ {% set customerInternalRate = 0 %}
{% endif %}
{{ summary.customer }}
@@ -280,14 +293,16 @@
{% endif %}
{{ summary.duration|duration(decimal) }}
+ {{ summary.rate_internal|money(summary.currency) }}
{{ summary.rate|money(summary.currency) }}
{% set customerDuration = customerDuration + summary.duration %}
{% set customerRate = customerRate + summary.rate %}
+ {% set customerInternalRate = customerInternalRate + summary.rate_internal %}
{% endfor %}
{% if customer is not same as(null) %}
-
+
{% if showTimeBudget %}
{% endif %}
@@ -295,12 +310,19 @@
{% endif %}
{{ customerDuration|duration(decimal) }}
+ {{ customerInternalRate|money(customerCurrency) }}
{{ customerRate|money(customerCurrency) }}
{% endif %}
+ {#
+ ============================================
+ SUMMARY: BY ACTIVITY
+ ============================================
+ #}
+
@@ -308,6 +330,7 @@
{{ 'label.project'|trans }}
{{ 'label.activity'|trans }}
{{ 'label.duration'|trans }}
+ {{ 'label.rate_internal'|trans }}
{{ 'label.rate'|trans }}
@@ -315,39 +338,45 @@
{% set customer = null %}
{% set customerDuration = 0 %}
{% set customerRate = 0 %}
+ {% set customerInternalRate = 0 %}
{% set customerCurrency = null %}
{% for summary in summaries %}
- {% if customer is same as(null) %}
- {% set customer = summary.customer %}
- {% set customerCurrency = summary.currency %}
- {% endif %}
- {% if customer is not same as(summary.customer) %}
-
-
- {{ customerDuration|duration(decimal) }}
- {{ customerRate|money(customerCurrency) }}
-
- {% set customerCurrency = summary.currency %}
- {% set customer = summary.customer %}
- {% set customerDuration = 0 %}
- {% set customerRate = 0 %}
- {% endif %}
- {% for activitySummary in summary.activities %}
-
- {{ summary.customer }}
- {{ summary.project }}
- {{ activitySummary.activity }}
- {{ activitySummary.duration|duration(decimal) }}
- {{ activitySummary.rate|money(activitySummary.currency) }}
-
- {% endfor %}
- {% set customerDuration = customerDuration + summary.duration %}
- {% set customerRate = customerRate + summary.rate %}
+ {% if customer is same as(null) %}
+ {% set customer = summary.customer %}
+ {% set customerCurrency = summary.currency %}
+ {% endif %}
+ {% if customer is not same as(summary.customer) %}
+
+
+ {{ customerDuration|duration(decimal) }}
+ {{ customerInternalRate|money(customerCurrency) }}
+ {{ customerRate|money(customerCurrency) }}
+
+ {% set customerCurrency = summary.currency %}
+ {% set customer = summary.customer %}
+ {% set customerDuration = 0 %}
+ {% set customerRate = 0 %}
+ {% set customerInternalRate = 0 %}
+ {% endif %}
+ {% for activitySummary in summary.activities %}
+
+ {{ summary.customer }}
+ {{ summary.project }}
+ {{ activitySummary.activity }}
+ {{ activitySummary.duration|duration(decimal) }}
+ {{ activitySummary.rate_internal|money(activitySummary.currency) }}
+ {{ activitySummary.rate|money(activitySummary.currency) }}
+
+ {% endfor %}
+ {% set customerDuration = customerDuration + summary.duration %}
+ {% set customerRate = customerRate + summary.rate %}
+ {% set customerInternalRate = customerInternalRate + summary.rate_internal %}
{% endfor %}
{% if customer is not same as(null) %}
{{ customerDuration|duration(decimal) }}
+ {{ customerInternalRate|money(customerCurrency) }}
{{ customerRate|money(customerCurrency) }}
{% endif %}
@@ -372,10 +401,16 @@
{% set timeWorked = 0 %}
{% set rateTotal = 0 %}
+ {% set rateInternalTotal = 0 %}
{% set currency = false %}
{% for entry in entries %}
{% set timeWorked = timeWorked + entry.duration %}
{% set rateTotal = rateTotal + entry.rate %}
+ {% if entry.internalRate is defined %}
+ {% set rateInternalTotal = rateInternalTotal + entry.internalRate %}
+ {% else %}
+ {% set rateInternalTotal = rateInternalTotal + entry.rate %}
+ {% endif %}
{% if currency is same as(false) %}
{% set currency = entry.project.customer.currency %}
{% endif %}
@@ -459,6 +494,13 @@
{{ entry.duration|duration(decimal) }}
+
+ {% if entry.internalRate is defined %}
+ {{ entry.internalRate|money(entry.project.customer.currency) }}
+ {% else %}
+ {{ entry.rate|money(entry.project.customer.currency) }}
+ {% endif %}
+
{{ entry.rate|money(entry.project.customer.currency) }}
@@ -467,13 +509,20 @@
{# leave in tbody instead of adding it to tfoot, as tfoot will be repeated on each page when printing #}
{% for id, visibility in columns %}
- {% if id != 'duration' and id != 'rate' %}
+ {% if id not in ['duration', 'rate', 'rate_internal'] %}
{% endif %}
{% endfor %}
{{- timeWorked|duration(decimal) -}}
+
+ {%- if currency is not null and currency is not same as(false) %}
+ {{ rateInternalTotal|money(currency) }}
+ {% else %}
+ {{ rateInternalTotal|money }}
+ {% endif -%}
+
{%- if currency is not null and currency is not same as(false) %}
{{ rateTotal|money(currency) }}
diff --git a/templates/export/renderer/default.pdf.twig b/templates/export/renderer/default.pdf.twig
index 87224849..b2871579 100644
--- a/templates/export/renderer/default.pdf.twig
+++ b/templates/export/renderer/default.pdf.twig
@@ -1,274 +1 @@
-{% set showUserColumn = true %}
-{% set showRateColumn = true %}
-{% set decimal = decimal|default(false) %}
-{% set showRateBudget = false %}
-{% set showTimeBudget = false %}
-{% if query.user %}
- {# this is only triggered, if a user exports from his personal timesheet screen#}
- {% set showUserColumn = false %}
- {% set showRateColumn = is_granted('view_rate_own_timesheet') %}
- {# TODO if exporting via the admin screen, users without view_rate_own_timesheet might still see their own rates, maybe merge view_rate_own_timesheet and view_rate_other_timesheet into a new view_rate permission? #}
-{% endif %}
-
-
-
-
-
-
-{% block summary %}
- {{ 'export.document_title'|trans }}
-
- {{ 'export.period'|trans }}:
- {{ query.begin|date_short }} - {{ query.end|date_short }}
-
- {{ 'export.summary'|trans }}
-
-
-
- {{ 'label.customer'|trans }}
- {{ 'label.project'|trans }}
- {% if showTimeBudget %}
- {{ 'label.timeBudget'|trans }}
- {% endif %}
- {% if showRateBudget %}
- {{ 'label.budget'|trans }}
- {% endif %}
- {{ 'label.duration'|trans }}
- {% if showRateColumn %}
- {{ 'label.rate'|trans }}
- {% endif %}
-
-
-
- {% set customer = null %}
- {% set customerDuration = 0 %}
- {% set customerRate = 0 %}
- {% set customerCurrency = null %}
- {% set customerCount = 0 %}
- {% for id, summary in summaries %}
- {% if customer is same as(null) %}
- {% set customer = summary.customer %}
- {% set customerCurrency = summary.currency %}
- {% endif %}
- {% if customer is not same as(summary.customer) %}
-
-
-
- {% if showTimeBudget %}
-
- {% endif %}
- {% if showRateBudget %}
-
- {% endif %}
- {{ customerDuration|duration(decimal) }}
- {% if showRateColumn %}
- {{ customerRate|money(customerCurrency) }}
- {% endif %}
-
- {% set customerCurrency = summary.currency %}
- {% set customer = summary.customer %}
- {% set customerDuration = 0 %}
- {% set customerRate = 0 %}
- {% set customerCount = 0 %}
- {% endif %}
-
- {{ summary.customer }}
- {{ summary.project }}
- {% if showTimeBudget %}
-
- {% if budgets[id] is defined and budgets[id].time_left > 0 %}
- {{ budgets[id].time_left|duration(decimal) }}
- {% endif %}
-
- {% endif %}
- {% if showRateBudget %}
-
- {% if budgets[id] is defined and budgets[id].money_left > 0 %}
- {{ budgets[id].money_left|money(summary.currency) }}
- {% endif %}
-
- {% endif %}
- {{ summary.duration|duration(decimal) }}
- {% if showRateColumn %}
- {{ summary.rate|money(summary.currency) }}
- {% endif %}
-
- {% set customerDuration = customerDuration + summary.duration %}
- {% set customerRate = customerRate + summary.rate %}
- {% set customerCount = customerCount + 1 %}
- {% endfor %}
- {% if customer is not same as(null) %}
-
-
- {% if showTimeBudget %}
-
- {% endif %}
- {% if showRateBudget %}
-
- {% endif %}
- {{ customerDuration|duration(decimal) }}
- {% if showRateColumn %}
- {{ customerRate|money(customerCurrency) }}
- {% endif %}
-
- {% endif %}
-
-
-
-
-{% endblock %}
-
-{% block items %}
- {{ 'export.full_list'|trans }}
-
- {% set duration = 0 %}
- {% set rate = 0 %}
- {% set currency = false %}
-
-
-
- {{ 'label.date'|trans }}
- {% if showUserColumn %}
- {{ 'label.user'|trans }}
- {% endif %}
- {{ 'label.description'|trans }}
- {{ 'label.duration'|trans }}
- {% if showRateColumn %}
- {{ 'label.rate'|trans }}
- {% endif %}
-
-
-
- {% for entry in entries %}
- {% set duration = duration + entry.duration %}
- {% if currency is same as(false) %}
- {% set currency = entry.project.customer.currency %}
- {% endif %}
- {% if currency is not same as(entry.project.customer.currency) %}
- {% set currency = null %}
- {% endif %}
-
-
- {{ entry.begin|date_time }}
- {% if entry.end %}
-
- {{ entry.end|date_time }}
- {% endif %}
-
- {% if showUserColumn %}
- {{ entry.user.displayName }}
- {% endif %}
-
- {{ entry.project.customer.name }} - {{ entry.project.name }}{% if entry.activity is not null %} - {{ entry.activity.name }}{% endif %}
- {% if entry.description is not empty %}
-
- {{ entry.description|escape|desc2html }}
- {% endif %}
-
- {{ entry.duration|duration(decimal) }}
- {% if showRateColumn %}
-
- {% if is_granted('view_rate', entry) %}
- {% set rate = rate + entry.rate %}
- {{ entry.rate|money(entry.project.customer.currency) }}
- {% else %}
- –
- {% endif %}
-
- {% endif %}
-
- {% endfor %}
-
- {% if showUserColumn %}
-
- {% else %}
-
- {% endif %}
- {{ duration|duration(decimal) }}
- {% if showRateColumn %}
- {% if currency is not null %}{{ rate|money(currency) }}{% endif %}
- {% endif %}
-
-
-
-{% endblock %}
-
-
-
+{% extends 'export/pdf-layout.html.twig' %}
diff --git a/templates/invoice/index.html.twig b/templates/invoice/index.html.twig
index aa0c5fbd..1779beee 100644
--- a/templates/invoice/index.html.twig
+++ b/templates/invoice/index.html.twig
@@ -75,6 +75,7 @@
{% else %}
{% set isDecimal = model.template.decimalDuration|default(false) %}
{% set entries = model.calculator.entries %}
+ {% set currency = model.currency %}
{{ tables.datatable_header(tableName, columns, query, {}) }}
{% for entry in entries %}
{% set amount = entry.amount %}
@@ -102,10 +103,10 @@
{{ entry.description|escape|desc2html }}
{% endif %}
- {{ rate|money(model.calculator.currency) }}
+ {{ rate|money(currency) }}
{{ amount }}
{{ duration }}
- {{ entry.rate|money(model.calculator.currency) }}
+ {{ entry.rate|money(currency) }}
{% endfor %}
@@ -116,7 +117,7 @@
{{ model.calculator.timeWorked|duration(isDecimal) }}
- {{ model.calculator.total|money(model.calculator.currency) }}
+ {{ model.calculator.total|money(currency) }}
{{ tables.data_table_footer(entries) }}
{% endif %}
diff --git a/templates/invoice/renderer/default.html.twig b/templates/invoice/renderer/default.html.twig
index 62491f6e..87ce55b9 100644
--- a/templates/invoice/renderer/default.html.twig
+++ b/templates/invoice/renderer/default.html.twig
@@ -2,6 +2,7 @@
{% set fallback = app.request is not null ? app.request.locale : 'en' %}
{% set language = model.template.language|default(fallback) %}
{% set isDecimal = model.template.decimalDuration|default(false) %}
+{% set currency = model.currency %}
{% block invoice %}
@@ -92,9 +93,9 @@
{% if entry.activity is not null %}{{ entry.activity.name }} / {% endif %}{{ entry.project.name }}
{% endif %}
-
{{ rate|money(model.calculator.currency) }}
+ {{ rate|money(currency) }}
{{ duration }}
- {{ entry.rate|money(model.calculator.currency) }}
+ {{ entry.rate|money(currency) }}
{% endfor %}
@@ -103,20 +104,20 @@
{{ 'invoice.subtotal'|trans({}, 'messages', language) }}
- {{ model.calculator.subtotal|money(model.calculator.currency) }}
+ {{ model.calculator.subtotal|money(currency) }}
{{ 'invoice.tax'|trans({}, 'messages', language) }} ({{ model.calculator.vat }}%)
- {{ model.calculator.tax|money(model.calculator.currency) }}
+ {{ model.calculator.tax|money(currency) }}
{{ 'invoice.total'|trans({}, 'messages', language) }}
- {{ model.calculator.total|money(model.calculator.currency) }}
+ {{ model.calculator.total|money(currency) }}
diff --git a/templates/invoice/renderer/freelancer.html.twig b/templates/invoice/renderer/freelancer.html.twig
index 0d3e1b70..5ab8a25a 100644
--- a/templates/invoice/renderer/freelancer.html.twig
+++ b/templates/invoice/renderer/freelancer.html.twig
@@ -3,6 +3,7 @@
{% set fallback = app.request is not null ? app.request.locale : 'en' %}
{% set language = model.template.language|default(fallback) %}
{% set isDecimal = model.template.decimalDuration|default(false) %}
+{% set currency = model.currency %}
{% block invoice %}
@@ -84,9 +85,9 @@
{% if entry.activity is not null %}{{ entry.activity.name }} / {% endif %}{{ entry.project.name }}
{% endif %}
-
{{ rate|money(model.calculator.currency) }}
+
{{ rate|money(currency) }}
{{ duration }}
-
{{ entry.rate|money(model.calculator.currency) }}
+
{{ entry.rate|money(currency) }}
{% endfor %}
@@ -94,15 +95,15 @@
{{ 'invoice.subtotal'|trans({}, 'messages', language) }}
- {{ model.calculator.subtotal|money(model.calculator.currency) }}
+ {{ model.calculator.subtotal|money(currency) }}
{{ 'invoice.tax'|trans({}, 'messages', language) }} ({{ model.calculator.vat }}%)
- {{ model.calculator.tax|money(model.calculator.currency) }}
+ {{ model.calculator.tax|money(currency) }}
{{ 'invoice.total'|trans({}, 'messages', language) }}
- {{ model.calculator.total|money(model.calculator.currency) }}
+ {{ model.calculator.total|money(currency) }}
diff --git a/templates/project/details.html.twig b/templates/project/details.html.twig
index c7730c0d..b701c9a8 100644
--- a/templates/project/details.html.twig
+++ b/templates/project/details.html.twig
@@ -99,64 +99,7 @@
{% endif %}
{% if can_edit %}
- {% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
- {% import "macros/widgets.html.twig" as widgets %}
- {% block box_attributes %}id="project_rates_box"{% endblock %}
- {% block box_title %}
- {{ 'rates.title'|trans }}
- {% endblock %}
- {% block box_tools %}
-
- {% endblock %}
- {% block box_body %}
- {% if rates is empty %}
- {{ 'rates.empty'|trans }}
- {% else %}
-
-
-
-
- {{ 'label.user'|trans }}
-
-
- {{ 'label.hourlyRate'|trans }}
-
-
- {{ 'label.fixedRate'|trans }}
-
-
-
-
-
- {% for rate in rates %}
-
-
- {% if rate.user is not null %}
- {{ widgets.user_avatar(rate.user) }}
- {% else %}
- –
- {% endif %}
-
-
- {% if not rate.fixed %}
- {{ rate.rate|money(project.customer.currency) }}
- {% endif %}
-
-
- {% if rate.fixed %}
- {{ rate.rate|money(project.customer.currency) }}
- {% endif %}
-
-
-
-
-
- {% endfor %}
-
-
- {% endif %}
- {% endblock %}
- {% endembed %}
+ {{ include('embeds/rates-table.html.twig', {'id': 'project_rates_box', 'entity': project, 'create_url': path('admin_project_rate_add', {'id': project.id}), 'delete_route': 'delete_project_rate', 'currency': project.customer.currency}) }}
{% endif %}
{% if teams is not null%}
@@ -184,7 +127,7 @@
{{ parent() }}
{% endblock %}
diff --git a/templates/project/embed_budget.html.twig b/templates/project/embed_budget.html.twig
index 7de2fabd..41b8e0d9 100644
--- a/templates/project/embed_budget.html.twig
+++ b/templates/project/embed_budget.html.twig
@@ -10,12 +10,15 @@
'%activities%': '
' ~ stats.activityAmount ~ ' ',
'%duration%': '
' ~ stats.recordDuration|duration ~ ' '
} %}
-
+
+ {% set currency = project.customer.currency %}
+
{{ 'admin_project.short_stats'|trans(params)|raw }}
+ {{ 'label.rate_internal'|trans }}: {{ stats.recordInternalRate|money(currency) }}.
- {{ progress.progressbar(project.budget, stats.recordRate, 'label.budget'|trans, stats.recordRate|money(project.customer.currency) ~ ' / ' ~ project.budget|money(project.customer.currency) ) }}
+ {{ progress.progressbar(project.budget, stats.recordRate, 'label.budget'|trans, stats.recordRate|money(currency) ~ ' / ' ~ project.budget|money(currency) ) }}
{{ progress.progressbar(project.timeBudget, stats.recordDuration, 'label.timeBudget'|trans, stats.recordDuration|duration ~ ' / ' ~ project.timeBudget|duration ) }}
{% endblock %}
{% endembed %}
diff --git a/tests/API/APIControllerBaseTest.php b/tests/API/APIControllerBaseTest.php
index 5d0abfef..c5ab7983 100644
--- a/tests/API/APIControllerBaseTest.php
+++ b/tests/API/APIControllerBaseTest.php
@@ -149,6 +149,22 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
);
}
+ protected function assertNotFoundForDelete(HttpKernelBrowser $client, string $url)
+ {
+ return $this->assertExceptionForMethod($client, $url, 'DELETE', [], [
+ 'code' => 404,
+ 'message' => 'Not found'
+ ]);
+ }
+
+ protected function assertEntityNotFoundForDelete(string $role, string $url)
+ {
+ return $this->assertExceptionForDeleteAction($role, $url, [], [
+ 'code' => 404,
+ 'message' => 'Not found'
+ ]);
+ }
+
protected function assertEntityNotFoundForPatch(string $role, string $url, array $data)
{
return $this->assertExceptionForPatchAction($role, $url, $data, [
@@ -157,11 +173,32 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
]);
}
+ protected function assertEntityNotFoundForPost(string $role, string $url, array $data, ?string $message = null)
+ {
+ return $this->assertExceptionForPostAction($role, $url, $data, [
+ 'code' => 404,
+ 'message' => $message ?? 'Not found'
+ ]);
+ }
+
+ protected function assertExceptionForDeleteAction(string $role, string $url, array $data, array $expectedErrors)
+ {
+ $this->assertExceptionForRole($role, $url, 'DELETE', $data, $expectedErrors);
+ }
+
protected function assertExceptionForPatchAction(string $role, string $url, array $data, array $expectedErrors)
{
- $client = $this->getClientForAuthenticatedUser($role);
+ $this->assertExceptionForRole($role, $url, 'PATCH', $data, $expectedErrors);
+ }
- $this->request($client, $url, 'PATCH', [], json_encode($data));
+ protected function assertExceptionForPostAction(string $role, string $url, array $data, array $expectedErrors)
+ {
+ $this->assertExceptionForRole($role, $url, 'POST', $data, $expectedErrors);
+ }
+
+ protected function assertExceptionForMethod(HttpKernelBrowser $client, string $url, string $method, array $data, array $expectedErrors)
+ {
+ $this->request($client, $url, $method, [], json_encode($data));
$response = $client->getResponse();
self::assertFalse($response->isSuccessful());
@@ -173,25 +210,10 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
);
}
- protected function assertEntityNotFoundForDelete(string $role, string $url, array $data)
+ protected function assertExceptionForRole(string $role, string $url, string $method, array $data, array $expectedErrors)
{
$client = $this->getClientForAuthenticatedUser($role);
-
- $this->request($client, $url, 'DELETE', [], json_encode($data));
- $response = $client->getResponse();
- self::assertFalse($response->isSuccessful());
-
- $expected = [
- 'code' => 404,
- 'message' => 'Not found'
- ];
-
- self::assertEquals(404, $client->getResponse()->getStatusCode());
-
- self::assertEquals(
- $expected,
- json_decode($client->getResponse()->getContent(), true)
- );
+ $this->assertExceptionForMethod($client, $url, $method, $data, $expectedErrors);
}
protected function assertApiException(Response $response, string $message)
diff --git a/tests/API/ActivityControllerTest.php b/tests/API/ActivityControllerTest.php
index 515092b5..661d616f 100644
--- a/tests/API/ActivityControllerTest.php
+++ b/tests/API/ActivityControllerTest.php
@@ -9,10 +9,14 @@
namespace App\Tests\API;
+use App\DataFixtures\UserFixtures;
use App\Entity\Activity;
+use App\Entity\ActivityRate;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\User;
+use App\Repository\ActivityRateRepository;
+use App\Repository\ActivityRepository;
use App\Tests\Mocks\ActivityTestMetaFieldSubscriberMock;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelBrowser;
@@ -22,6 +26,51 @@ use Symfony\Component\HttpKernel\HttpKernelBrowser;
*/
class ActivityControllerTest extends APIControllerBaseTest
{
+ use RateControllerTestTrait;
+
+ protected function getRateUrl(string $id = '1', ?string $rateId = null): string
+ {
+ if (null !== $rateId) {
+ return sprintf('/api/activities/%s/rates/%s', $id, $rateId);
+ }
+
+ return sprintf('/api/activities/%s/rates', $id);
+ }
+
+ protected function importTestRates(string $id): array
+ {
+ /** @var ActivityRateRepository $rateRepository */
+ $rateRepository = $this->getEntityManager()->getRepository(ActivityRate::class);
+ /** @var ActivityRepository $repository */
+ $repository = $this->getEntityManager()->getRepository(Activity::class);
+ /** @var Activity|null $activity */
+ $activity = $repository->find($id);
+
+ if (null === $activity) {
+ $activity = new Activity();
+ $activity->setName('foooo');
+ $repository->saveActivity($activity);
+ }
+
+ $rate1 = new ActivityRate();
+ $rate1->setActivity($activity);
+ $rate1->setRate(17.45);
+ $rate1->setIsFixed(false);
+
+ $rateRepository->saveRate($rate1);
+
+ $rate2 = new ActivityRate();
+ $rate2->setActivity($activity);
+ $rate2->setRate(99);
+ $rate2->setInternalRate(9);
+ $rate2->setIsFixed(true);
+ $rate2->setUser($this->getUserByName(UserFixtures::USERNAME_USER));
+
+ $rateRepository->saveRate($rate2);
+
+ return [$rate1, $rate2];
+ }
+
public function testIsSecure()
{
$this->assertUrlIsSecured('/api/activities');
@@ -29,9 +78,11 @@ class ActivityControllerTest extends APIControllerBaseTest
protected function loadActivityTestData(HttpKernelBrowser $client)
{
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
+ /** @var Project $project */
$project = $em->getRepository(Project::class)->find(1);
+ /** @var Customer $customer */
$customer = $em->getRepository(Customer::class)->find(1);
$project2 = new Project();
@@ -104,7 +155,6 @@ class ActivityControllerTest extends APIControllerBaseTest
$this->loadActivityTestData($client);
$query = ['order' => 'ASC', 'orderBy' => 'project'];
- $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/activities', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
@@ -277,7 +327,7 @@ class ActivityControllerTest extends APIControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Activity $activity */
$activity = $em->getRepository(Activity::class)->find(1);
$this->assertEquals('another,testing,bar', $activity->getMetaField('metatestmock')->getValue());
diff --git a/tests/API/CustomerControllerTest.php b/tests/API/CustomerControllerTest.php
index 2624e565..d97f9c51 100644
--- a/tests/API/CustomerControllerTest.php
+++ b/tests/API/CustomerControllerTest.php
@@ -9,8 +9,12 @@
namespace App\Tests\API;
+use App\DataFixtures\UserFixtures;
use App\Entity\Customer;
+use App\Entity\CustomerRate;
use App\Entity\User;
+use App\Repository\CustomerRateRepository;
+use App\Repository\CustomerRepository;
use App\Tests\Mocks\CustomerTestMetaFieldSubscriberMock;
use Symfony\Component\HttpFoundation\Response;
@@ -19,6 +23,53 @@ use Symfony\Component\HttpFoundation\Response;
*/
class CustomerControllerTest extends APIControllerBaseTest
{
+ use RateControllerTestTrait;
+
+ protected function getRateUrl(string $id = '1', ?string $rateId = null): string
+ {
+ if (null !== $rateId) {
+ return sprintf('/api/customers/%s/rates/%s', $id, $rateId);
+ }
+
+ return sprintf('/api/customers/%s/rates', $id);
+ }
+
+ protected function importTestRates(string $id): array
+ {
+ /** @var CustomerRateRepository $rateRepository */
+ $rateRepository = $this->getEntityManager()->getRepository(CustomerRate::class);
+ /** @var CustomerRepository $repository */
+ $repository = $this->getEntityManager()->getRepository(Customer::class);
+ /** @var Customer|null $customer */
+ $customer = $repository->find($id);
+
+ if (null === $customer) {
+ $customer = new Customer();
+ $customer->setCountry('DE');
+ $customer->setTimezone('Europre/Paris');
+ $customer->setName('foooo');
+ $repository->saveCustomer($customer);
+ }
+
+ $rate1 = new CustomerRate();
+ $rate1->setCustomer($customer);
+ $rate1->setRate(17.45);
+ $rate1->setIsFixed(false);
+
+ $rateRepository->saveRate($rate1);
+
+ $rate2 = new CustomerRate();
+ $rate2->setCustomer($customer);
+ $rate2->setRate(99);
+ $rate2->setInternalRate(9);
+ $rate2->setIsFixed(true);
+ $rate2->setUser($this->getUserByName(UserFixtures::USERNAME_USER));
+
+ $rateRepository->saveRate($rate2);
+
+ return [$rate1, $rate2];
+ }
+
public function testIsSecure()
{
$this->assertUrlIsSecured('/api/customers');
@@ -221,7 +272,7 @@ class CustomerControllerTest extends APIControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Customer $customer */
$customer = $em->getRepository(Customer::class)->find(1);
$this->assertEquals('another,testing,bar', $customer->getMetaField('metatestmock')->getValue());
diff --git a/tests/API/ProjectControllerTest.php b/tests/API/ProjectControllerTest.php
index e4b8a24b..f3473fc7 100644
--- a/tests/API/ProjectControllerTest.php
+++ b/tests/API/ProjectControllerTest.php
@@ -9,9 +9,13 @@
namespace App\Tests\API;
+use App\DataFixtures\UserFixtures;
use App\Entity\Customer;
use App\Entity\Project;
+use App\Entity\ProjectRate;
use App\Entity\User;
+use App\Repository\ProjectRateRepository;
+use App\Repository\ProjectRepository;
use App\Repository\Query\VisibilityInterface;
use App\Tests\Mocks\ProjectTestMetaFieldSubscriberMock;
use Symfony\Component\HttpFoundation\Response;
@@ -22,6 +26,52 @@ use Symfony\Component\HttpKernel\HttpKernelBrowser;
*/
class ProjectControllerTest extends APIControllerBaseTest
{
+ use RateControllerTestTrait;
+
+ protected function getRateUrl(string $id = '1', ?string $rateId = null): string
+ {
+ if (null !== $rateId) {
+ return sprintf('/api/projects/%s/rates/%s', $id, $rateId);
+ }
+
+ return sprintf('/api/projects/%s/rates', $id);
+ }
+
+ protected function importTestRates(string $id): array
+ {
+ /** @var ProjectRateRepository $rateRepository */
+ $rateRepository = $this->getEntityManager()->getRepository(ProjectRate::class);
+ /** @var ProjectRepository $repository */
+ $repository = $this->getEntityManager()->getRepository(Project::class);
+ /** @var Project|null $project */
+ $project = $repository->find($id);
+
+ if (null === $project) {
+ $project = new Project();
+ $project->setName('foooo');
+ $project->setCustomer($this->getEntityManager()->getRepository(Customer::class)->find(1));
+ $repository->saveProject($project);
+ }
+
+ $rate1 = new ProjectRate();
+ $rate1->setProject($project);
+ $rate1->setRate(17.45);
+ $rate1->setIsFixed(false);
+
+ $rateRepository->saveRate($rate1);
+
+ $rate2 = new ProjectRate();
+ $rate2->setProject($project);
+ $rate2->setRate(99);
+ $rate2->setInternalRate(9);
+ $rate2->setIsFixed(true);
+ $rate2->setUser($this->getUserByName(UserFixtures::USERNAME_USER));
+
+ $rateRepository->saveRate($rate2);
+
+ return [$rate1, $rate2];
+ }
+
public function testIsSecure()
{
$this->assertUrlIsSecured('/api/projects');
@@ -41,7 +91,7 @@ class ProjectControllerTest extends APIControllerBaseTest
protected function loadProjectTestData(HttpKernelBrowser $client)
{
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$customer = $em->getRepository(Customer::class)->find(1);
@@ -278,7 +328,7 @@ class ProjectControllerTest extends APIControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Project $project */
$project = $em->getRepository(Project::class)->find(1);
$this->assertEquals('another,testing,bar', $project->getMetaField('metatestmock')->getValue());
diff --git a/tests/API/RateControllerTestTrait.php b/tests/API/RateControllerTestTrait.php
new file mode 100644
index 00000000..1efab167
--- /dev/null
+++ b/tests/API/RateControllerTestTrait.php
@@ -0,0 +1,205 @@
+ 1,
+ 'rate' => 12.34,
+ 'internal_rate' => 6.66,
+ 'is_fixed' => false
+ ];
+ $this->assertEntityNotFoundForPost(User::ROLE_ADMIN, $this->getRateUrl(99), $data, 'Not found');
+ }
+
+ public function testAddRateMissingUserAction()
+ {
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $data = [
+ 'user' => 33,
+ 'rate' => 12.34,
+ 'internal_rate' => 6.66,
+ 'is_fixed' => false
+ ];
+ $this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data));
+
+ $response = $client->getResponse();
+ $this->assertEquals(400, $response->getStatusCode());
+ $this->assertApiCallValidationError($response, ['user']);
+ }
+
+ public function testAddRateActionWithInvalidUser()
+ {
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $data = [
+ 'user' => null,
+ 'rate' => 12.34,
+ 'internal_rate' => 6.66,
+ 'is_fixed' => false
+ ];
+ $this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data));
+ $response = $client->getResponse();
+ $this->assertFalse($response->isSuccessful());
+ $this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
+ $json = json_decode($response->getContent(), true);
+ $this->assertEquals('Access denied.', $json['message']);
+ }
+
+ public function testAddRateAction()
+ {
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $data = [
+ 'user' => null,
+ 'rate' => 12.34,
+ 'internal_rate' => 6.66,
+ 'is_fixed' => false
+ ];
+ $this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data));
+ $this->assertTrue($client->getResponse()->isSuccessful());
+
+ $result = json_decode($client->getResponse()->getContent(), true);
+ $this->assertIsArray($result);
+ $this->assertRateStructure($result, null);
+ $this->assertNotEmpty($result['id']);
+ $this->assertEquals(12.34, $result['rate']);
+ $this->assertEquals(6.66, $result['internalRate']);
+ $this->assertFalse($result['isFixed']);
+ }
+
+ public function testAddFixedRateAction()
+ {
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $data = [
+ 'user' => 1,
+ 'rate' => 12.34,
+ 'internal_rate' => 6.66,
+ 'is_fixed' => true
+ ];
+ $this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data));
+ $this->assertTrue($client->getResponse()->isSuccessful());
+
+ $result = json_decode($client->getResponse()->getContent(), true);
+ $this->assertIsArray($result);
+ $this->assertRateStructure($result, 1);
+ $this->assertNotEmpty($result['id']);
+ $this->assertEquals(12.34, $result['rate']);
+ $this->assertEquals(6.66, $result['internalRate']);
+ $this->assertTrue($result['isFixed']);
+ }
+
+ public function testGetRatesEmptyResult()
+ {
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $this->request($client, $this->getRateUrl(1));
+ $this->assertTrue($client->getResponse()->isSuccessful());
+
+ $result = json_decode($client->getResponse()->getContent(), true);
+ $this->assertIsArray($result);
+ $this->assertEmpty($result);
+ }
+
+ public function testGetRates()
+ {
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $expectedRates = $this->importTestRates(1);
+
+ $this->request($client, $this->getRateUrl(1));
+ $this->assertTrue($client->getResponse()->isSuccessful());
+
+ $result = json_decode($client->getResponse()->getContent(), true);
+ $this->assertIsArray($result);
+ $this->assertNotEmpty($result);
+ $this->assertEquals(count($expectedRates), count($result));
+
+ foreach ($result as $rate) {
+ $this->assertRateStructure($rate, ($rate['user'] === null ? null : $rate['user']['id']));
+ }
+ }
+
+ public function testGetRatesEntityNotFound()
+ {
+ $this->assertEntityNotFound(User::ROLE_ADMIN, $this->getRateUrl(99));
+ }
+
+ public function testGetRatesIsSecured()
+ {
+ $this->assertUrlIsSecuredForRole(User::ROLE_USER, $this->getRateUrl(1));
+ }
+
+ public function testDeleteRate()
+ {
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $expectedRates = $this->importTestRates(1);
+
+ $this->request($client, $this->getRateUrl(1, 1), 'DELETE');
+ $this->assertTrue($client->getResponse()->isSuccessful());
+ $this->assertEmpty($client->getResponse()->getContent());
+
+ // fetch rates to validate that one was removed
+ $this->request($client, $this->getRateUrl(1));
+ $this->assertTrue($client->getResponse()->isSuccessful());
+
+ $result = json_decode($client->getResponse()->getContent(), true);
+ $this->assertIsArray($result);
+ $this->assertEquals(count($expectedRates) - 1, count($result));
+ }
+
+ public function testDeleteRateEntityNotFound()
+ {
+ $this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, $this->getRateUrl(99, 1));
+ }
+
+ public function testDeleteRateRateNotFound()
+ {
+ $this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, $this->getRateUrl(1, 99));
+ }
+
+ public function testDeleteRateWithInvalidAssignment()
+ {
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $this->importTestRates(1);
+ $this->importTestRates(2);
+
+ $this->assertNotFoundForDelete($client, $this->getRateUrl(2, 1));
+ }
+
+ protected function assertRateStructure(array $result, $user = null)
+ {
+ $expectedKeys = [
+ 'id', 'rate', 'internalRate', 'isFixed', 'user'
+ ];
+
+ $actual = array_keys($result);
+ sort($actual);
+ sort($expectedKeys);
+
+ $this->assertEquals($expectedKeys, $actual, 'Rate structure does not match');
+
+ if (null !== $user) {
+ self::assertIsArray($result['user'], 'Rate user is not an array');
+ self::assertEquals($user, $result['user']['id'], 'Rate user does not match');
+ } else {
+ self::assertNull($result['user']);
+ }
+ }
+}
diff --git a/tests/API/StatusControllerTest.php b/tests/API/StatusControllerTest.php
index 5451d0b5..d1720444 100644
--- a/tests/API/StatusControllerTest.php
+++ b/tests/API/StatusControllerTest.php
@@ -17,9 +17,13 @@ use App\Entity\User;
*/
class StatusControllerTest extends APIControllerBaseTest
{
- public function testIsSecure()
+ public function testIsSecurePing()
{
$this->assertUrlIsSecured('/api/ping');
+ }
+
+ public function testIsSecureVersion()
+ {
$this->assertUrlIsSecured('/api/version');
}
diff --git a/tests/API/TagControllerTest.php b/tests/API/TagControllerTest.php
index 7badedda..5f528b66 100644
--- a/tests/API/TagControllerTest.php
+++ b/tests/API/TagControllerTest.php
@@ -12,16 +12,15 @@ namespace App\Tests\API;
use App\Entity\User;
use App\Tests\DataFixtures\TagFixtures;
use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\HttpKernelBrowser;
/**
* @group integration
*/
class TagControllerTest extends APIControllerBaseTest
{
- protected function setUp(): void
+ protected function importTagFixtures(HttpKernelBrowser $client): void
{
- $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
-
$tagList = ['Test', 'Administration', 'Support', '#2018-001', '#2018-002', '#2018-003', 'Development',
'Marketing', 'First Level Support', 'Bug Fixing'];
@@ -38,6 +37,7 @@ class TagControllerTest extends APIControllerBaseTest
public function testGetCollection()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importTagFixtures($client);
$this->assertAccessIsGranted($client, '/api/tags');
$result = json_decode($client->getResponse()->getContent(), true);
@@ -50,6 +50,7 @@ class TagControllerTest extends APIControllerBaseTest
public function testEmptyCollection()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importTagFixtures($client);
$query = ['name' => 'nothing'];
$this->assertAccessIsGranted($client, '/api/tags', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
@@ -62,6 +63,7 @@ class TagControllerTest extends APIControllerBaseTest
public function testPostAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $this->importTagFixtures($client);
$data = [
'name' => 'foo',
];
@@ -77,6 +79,7 @@ class TagControllerTest extends APIControllerBaseTest
public function testPostActionWithInvalidUser()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importTagFixtures($client);
$data = [
'name' => 'foo',
];
@@ -91,6 +94,7 @@ class TagControllerTest extends APIControllerBaseTest
public function testPartOfEntries()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importTagFixtures($client);
$query = ['name' => 'in'];
$this->assertAccessIsGranted($client, '/api/tags', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
@@ -107,6 +111,7 @@ class TagControllerTest extends APIControllerBaseTest
public function testDeleteAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $this->importTagFixtures($client);
$this->request($client, '/api/tags/1', 'DELETE');
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -121,7 +126,7 @@ class TagControllerTest extends APIControllerBaseTest
public function testDeleteActionWithUnknownTimesheet()
{
- $this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/tags/255', []);
+ $this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/tags/255');
}
protected function assertStructure(array $result, $full = true)
diff --git a/tests/API/TeamControllerTest.php b/tests/API/TeamControllerTest.php
index e674bc77..0a8b35ba 100644
--- a/tests/API/TeamControllerTest.php
+++ b/tests/API/TeamControllerTest.php
@@ -31,8 +31,22 @@ class TeamControllerTest extends APIControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/api/teams');
- $this->assertUrlIsSecuredForRole(User::ROLE_USER, '/api/teams');
- $this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/api/teams');
+ }
+
+ public function getRoleTestData()
+ {
+ return [
+ [User::ROLE_USER],
+ [User::ROLE_TEAMLEAD],
+ ];
+ }
+
+ /**
+ * @dataProvider getRoleTestData
+ */
+ public function testIsSecureForRole(string $role)
+ {
+ $this->assertUrlIsSecuredForRole($role, '/api/teams');
}
public function testGetCollection()
@@ -66,7 +80,7 @@ class TeamControllerTest extends APIControllerBaseTest
public function testDeleteActionWithUnknownTeam()
{
- $this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/teams/255', []);
+ $this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/teams/255');
}
public function testPostAction()
@@ -142,8 +156,6 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
self::assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode());
$this->assertEmpty($client->getResponse()->getContent());
-
- $this->assertEntityNotFound(User::ROLE_ADMIN, '/api/teams/' . $id);
}
public function testPostMemberAction()
@@ -334,7 +346,7 @@ class TeamControllerTest extends APIControllerBaseTest
$customer->setVisible(false);
$customer->setCountry('DE');
$customer->setTimezone('Europe/Berlin');
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$em->persist($customer);
$em->flush();
@@ -472,7 +484,7 @@ class TeamControllerTest extends APIControllerBaseTest
$project->setName('foooo');
$project->setVisible(false);
$project->setCustomer($customer);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$em->persist($customer);
$em->persist($project);
$em->flush();
diff --git a/tests/API/TimesheetControllerTest.php b/tests/API/TimesheetControllerTest.php
index 5d31c761..e8186280 100644
--- a/tests/API/TimesheetControllerTest.php
+++ b/tests/API/TimesheetControllerTest.php
@@ -19,7 +19,6 @@ use App\Entity\User;
use App\Tests\DataFixtures\TimesheetFixtures;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Tests\Mocks\TimesheetTestMetaFieldSubscriberMock;
-use App\Timesheet\UserDateTimeFactory;
use Symfony\Component\HttpFoundation\Response;
/**
@@ -31,21 +30,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
public const DATE_FORMAT_HTML5 = 'Y-m-d\TH:i:s';
public const TEST_TIMEZONE = 'Europe/London';
- /**
- * @var UserDateTimeFactory
- */
- protected $dateTime;
-
- protected function setUp(): void
- {
- $this->importFixtureForUser(User::ROLE_USER);
- $this->dateTime = (new UserDateTimeFactoryFactory($this))->create(self::TEST_TIMEZONE);
- }
-
protected function importFixtureForUser(string $role)
{
- $client = $this->getClientForAuthenticatedUser($role);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture
@@ -56,7 +43,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
->setStartDate((new \DateTime('first day of this month'))->setTime(0, 0, 1))
->setAllowEmptyDescriptions(false)
;
- $this->importFixture($client, $fixture);
+ $this->importFixture($this, $fixture);
}
public function testIsSecure()
@@ -67,6 +54,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testGetCollection()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/timesheets');
$result = json_decode($client->getResponse()->getContent(), true);
@@ -79,6 +67,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testGetCollectionFull()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', ['full' => 'true']);
$result = json_decode($client->getResponse()->getContent(), true);
@@ -92,7 +81,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testGetCollectionForOtherUser()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $this->importFixtureForUser(User::ROLE_USER);
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture
@@ -117,7 +107,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testGetCollectionForAllUser()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $this->importFixtureForUser(User::ROLE_USER);
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture
@@ -172,6 +163,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
];
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
@@ -181,7 +173,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
$this->assertDefaultStructure($result[0], false);
}
- public function testGetCollectionWithDeprecatedQuery()
+ public function testGetCollectionWithSingleParamsQuery()
{
$begin = new \DateTime('first day of this month');
$begin->setTime(0, 0, 0);
@@ -203,6 +195,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
];
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
@@ -215,7 +208,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testExportedFilter()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $this->importFixtureForUser(User::ROLE_USER);
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture
@@ -240,7 +234,6 @@ class TimesheetControllerTest extends APIControllerBaseTest
'exported' => 1,
];
- $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
@@ -257,7 +250,6 @@ class TimesheetControllerTest extends APIControllerBaseTest
'exported' => 0,
];
- $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
@@ -272,7 +264,6 @@ class TimesheetControllerTest extends APIControllerBaseTest
'begin' => $begin->format(self::DATE_FORMAT_HTML5),
'end' => $end->format(self::DATE_FORMAT_HTML5),
];
- $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
@@ -285,6 +276,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testGetEntity()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/timesheets/1');
$result = json_decode($client->getResponse()->getContent(), true);
@@ -294,15 +286,17 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testGetEntityAccessDenied()
{
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
$this->importFixtureForUser(User::ROLE_ADMIN);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertApiAccessDenied($client, '/api/timesheets/15', 'You are not allowed to view this timesheet');
}
public function testGetEntityAccessAllowedForAdmin()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $this->importFixtureForUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/timesheets/1');
$result = json_decode($client->getResponse()->getContent(), true);
@@ -317,12 +311,13 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testPostAction()
{
+ $dateTime = (new UserDateTimeFactoryFactory($this))->create(self::TEST_TIMEZONE);
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'activity' => 1,
'project' => 1,
- 'begin' => ($this->dateTime->createDateTime('- 16 hours'))->format('Y-m-d H:m:0'),
- 'end' => ($this->dateTime->createDateTime())->format('Y-m-d H:m:0'),
+ 'begin' => ($dateTime->createDateTime('- 16 hours'))->format('Y-m-d H:m:0'),
+ 'end' => ($dateTime->createDateTime())->format('Y-m-d H:m:0'),
'description' => 'foo',
'fixedRate' => 2016,
'hourlyRate' => 127
@@ -344,7 +339,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$customer = (new Customer())->setName('foo-bar-1')->setVisible(false)->setCountry('DE')->setTimezone('Europe/Berlin');
$em->persist($customer);
$project = (new Project())->setName('foo-bar-2')->setVisible(true)->setCustomer($customer);
@@ -372,7 +367,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$customer = (new Customer())->setName('foo-bar-1')->setVisible(true)->setCountry('DE')->setTimezone('Europe/Berlin');
$em->persist($customer);
$project = (new Project())->setName('foo-bar-2')->setVisible(true)->setCustomer($customer);
@@ -396,12 +391,14 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testPatchAction()
{
+ $dateTime = (new UserDateTimeFactoryFactory($this))->create(self::TEST_TIMEZONE);
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
+ $this->importFixtureForUser(User::ROLE_USER);
$data = [
'activity' => 1,
'project' => 1,
- 'begin' => ($this->dateTime->createDateTime('- 7 hours'))->format('Y-m-d\TH:m:0'),
- 'end' => ($this->dateTime->createDateTime())->format('Y-m-d\TH:m:0'),
+ 'begin' => ($dateTime->createDateTime('- 7 hours'))->format('Y-m-d\TH:m:0'),
+ 'end' => ($dateTime->createDateTime())->format('Y-m-d\TH:m:0'),
'description' => 'foo',
'exported' => true,
];
@@ -419,7 +416,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testPatchActionWithInvalidUser()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $this->importFixtureForUser(User::ROLE_USER);
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture
@@ -456,6 +454,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testInvalidPatchAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
+
$data = [
'activity' => 10,
'project' => 1,
@@ -475,6 +475,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testDeleteAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/timesheets/1');
$result = json_decode($client->getResponse()->getContent(), true);
@@ -487,32 +488,31 @@ class TimesheetControllerTest extends APIControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode());
$this->assertEmpty($client->getResponse()->getContent());
-
- $this->assertEntityNotFound(User::ROLE_USER, '/api/timesheets/' . $id);
}
public function testDeleteActionWithUnknownTimesheet()
{
- $this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/timesheets/255', []);
+ $this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/timesheets/255');
}
public function testDeleteActionForDifferentUser()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $this->importFixtureForUser(User::ROLE_USER);
+
$id = 1;
$this->request($client, '/api/timesheets/' . $id, 'DELETE');
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode());
$this->assertEmpty($client->getResponse()->getContent());
-
- $this->assertEntityNotFound(User::ROLE_USER, '/api/timesheets/' . $id);
}
public function testDeleteActionWithoutAuthorization()
{
- $this->importFixtureForUser(User::ROLE_ADMIN);
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_ADMIN);
$this->request($client, '/api/timesheets/15', 'DELETE');
@@ -526,8 +526,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testDeleteActionForExportedRecordIsNotAllowed()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$timesheet->setExported(true);
@@ -541,8 +542,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testDeleteActionForExportedRecordIsAllowedForAdmin()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $this->importFixtureForUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$timesheet->setExported(true);
@@ -556,7 +558,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testGetRecentCollectionWithSubresources()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$start = new \DateTime('-10 days');
@@ -589,7 +591,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testActiveAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$start = new \DateTime('-10 days');
@@ -617,7 +619,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testStopAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $this->importFixtureForUser(User::ROLE_USER);
+ $em = $this->getEntityManager();
$start = new \DateTime('-10 days');
@@ -635,7 +638,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
$this->request($client, '/api/timesheets/11/stop', 'PATCH');
$this->assertTrue($client->getResponse()->isSuccessful());
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$this->assertInstanceOf(\DateTime::class, $timesheet->getEnd());
@@ -644,6 +647,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testStopActionFailsOnStoppedEntry()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
$this->request($client, '/api/timesheets/1/stop', 'PATCH');
$this->assertApiException($client->getResponse(), 'Timesheet entry already stopped');
@@ -657,7 +661,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testStopNotAllowedForUser()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $this->importFixtureForUser(User::ROLE_USER);
+ $em = $this->getEntityManager();
$start = new \DateTime('-10 days');
@@ -679,7 +684,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testGetCollectionWithTags()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $this->importFixtureForUser(User::ROLE_USER);
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture
@@ -724,6 +730,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testRestartAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
$data = [
'description' => 'foo',
@@ -739,7 +746,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
$this->assertEmpty($result['description']);
$this->assertEmpty($result['tags']);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find($result['id']);
$this->assertInstanceOf(\DateTime::class, $timesheet->getBegin());
@@ -753,8 +760,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testRestartActionWithCopyData()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$timesheet->setDescription('foo');
@@ -779,7 +787,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
$this->assertEquals([['name' => 'sdfsdf', 'value' => 'nnnnn'], ['name' => '1234567890', 'value' => '1234567890']], $result['metaFields']);
$this->assertEquals(['another', 'testing', 'bar'], $result['tags']);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find($result['id']);
$this->assertInstanceOf(\DateTime::class, $timesheet->getBegin());
@@ -793,7 +801,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testRestartNotAllowedForUser()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+
+ $em = $this->getEntityManager();
$start = new \DateTime('-10 days');
@@ -808,7 +817,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
;
$this->importFixture($em, $fixture);
- $this->request($client, '/api/timesheets/12/restart', 'PATCH');
+ $this->request($client, '/api/timesheets/2/restart', 'PATCH');
$this->assertApiResponseAccessDenied($client->getResponse(), 'You are not allowed to re-start this timesheet');
}
@@ -819,12 +828,13 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testDuplicateAction()
{
+ $dateTime = (new UserDateTimeFactoryFactory($this))->create(self::TEST_TIMEZONE);
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'activity' => 1,
'project' => 1,
- 'begin' => ($this->dateTime->createDateTime('- 16 hours'))->format('Y-m-d H:m:0'),
- 'end' => ($this->dateTime->createDateTime())->format('Y-m-d H:m:0'),
+ 'begin' => ($dateTime->createDateTime('- 16 hours'))->format('Y-m-d H:m:0'),
+ 'end' => ($dateTime->createDateTime())->format('Y-m-d H:m:0'),
'description' => 'foo',
'fixedRate' => 2016,
'hourlyRate' => 127
@@ -858,32 +868,34 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testExportAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
+ $this->importFixtureForUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
- $this->assertEquals(false, $timesheet->isExported());
+ $this->assertFalse($timesheet->isExported());
$this->request($client, '/api/timesheets/1/export', 'PATCH');
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertDefaultStructure(json_decode($client->getResponse()->getContent(), true), true);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em->clear();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
- $this->assertEquals(true, $timesheet->isExported());
+ $this->assertTrue($timesheet->isExported());
$this->request($client, '/api/timesheets/1/export', 'PATCH');
$this->assertTrue($client->getResponse()->isSuccessful());
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em->clear();
$timesheet = $em->getRepository(Timesheet::class)->find(1);
- $this->assertEquals(false, $timesheet->isExported());
+ $this->assertFalse($timesheet->isExported());
}
public function testExportNotAllowedForUser()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
$this->request($client, '/api/timesheets/1/export', 'PATCH');
$this->assertApiResponseAccessDenied($client->getResponse(), 'You are not allowed to lock this timesheet');
@@ -901,7 +913,10 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testMetaActionThrowsExceptionOnMissingName()
{
- return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/timesheets/1/meta', ['value' => 'X'], [
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
+
+ return $this->assertExceptionForMethod($client, '/api/timesheets/1/meta', 'PATCH', ['value' => 'X'], [
'code' => 400,
'message' => 'Parameter "name" of value "NULL" violated a constraint "This value should not be null."'
]);
@@ -909,7 +924,10 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testMetaActionThrowsExceptionOnMissingValue()
{
- return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/timesheets/1/meta', ['name' => 'X'], [
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
+
+ return $this->assertExceptionForMethod($client, '/api/timesheets/1/meta', 'PATCH', ['name' => 'X'], [
'code' => 400,
'message' => 'Parameter "value" of value "NULL" violated a constraint "This value should not be null."'
]);
@@ -917,7 +935,10 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testMetaActionThrowsExceptionOnMissingMetafield()
{
- return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/timesheets/1/meta', ['name' => 'X', 'value' => 'Y'], [
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
+ $this->importFixtureForUser(User::ROLE_USER);
+
+ return $this->assertExceptionForMethod($client, '/api/timesheets/1/meta', 'PATCH', ['name' => 'X', 'value' => 'Y'], [
'code' => 500,
'message' => 'Unknown meta-field requested'
]);
@@ -926,7 +947,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
public function testMetaAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new TimesheetTestMetaFieldSubscriberMock());
+ $this->importFixtureForUser(User::ROLE_USER);
+ static::$container->get('event_dispatcher')->addSubscriber(new TimesheetTestMetaFieldSubscriberMock());
$data = [
'name' => 'metatestmock',
@@ -936,7 +958,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$this->assertEquals('another,testing,bar', $timesheet->getMetaField('metatestmock')->getValue());
@@ -945,7 +967,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
protected function assertDefaultStructure(array $result, $full = true)
{
$expectedKeys = [
- 'id', 'begin', 'end', 'duration', 'description', 'rate', 'activity', 'project', 'tags', 'user', 'metaFields'
+ 'id', 'begin', 'end', 'duration', 'description', 'rate', 'activity', 'project', 'tags', 'user', 'metaFields', 'internalRate'
];
if ($full) {
diff --git a/tests/API/UserControllerTest.php b/tests/API/UserControllerTest.php
index 64c73982..b8741b31 100644
--- a/tests/API/UserControllerTest.php
+++ b/tests/API/UserControllerTest.php
@@ -20,9 +20,23 @@ class UserControllerTest extends APIControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/api/users');
- $this->assertUrlIsSecuredForRole(User::ROLE_USER, '/api/users');
- $this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/api/users');
- $this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/api/users');
+ }
+
+ public function getRoleTestData()
+ {
+ return [
+ [User::ROLE_USER],
+ [User::ROLE_TEAMLEAD],
+ [User::ROLE_ADMIN],
+ ];
+ }
+
+ /**
+ * @dataProvider getRoleTestData
+ */
+ public function testIsSecureForRole(string $role)
+ {
+ $this->assertUrlIsSecuredForRole($role, '/api/users');
}
public function testGetCollection()
@@ -187,7 +201,6 @@ class UserControllerTest extends APIControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
$result = json_decode($client->getResponse()->getContent(), true);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$data = [
'avatar' => 'test321',
'title' => 'qwertzui',
diff --git a/tests/Command/InvoiceCreateCommandTest.php b/tests/Command/InvoiceCreateCommandTest.php
index e7343bda..59a8d509 100644
--- a/tests/Command/InvoiceCreateCommandTest.php
+++ b/tests/Command/InvoiceCreateCommandTest.php
@@ -179,7 +179,7 @@ class InvoiceCreateCommandTest extends KernelTestCase
protected function prepareFixtures(\DateTime $start)
{
- $em = self::$container->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new CustomerFixtures();
$fixture->setAmount(1);
@@ -197,7 +197,7 @@ class InvoiceCreateCommandTest extends KernelTestCase
$this->importFixture($em, $fixture);
$fixture = new TimesheetFixtures();
- $fixture->setUser($this->getUserByName($em, UserFixtures::USERNAME_SUPER_ADMIN));
+ $fixture->setUser($this->getUserByName(UserFixtures::USERNAME_SUPER_ADMIN));
$fixture->setAmount(20);
$fixture->setStartDate($start);
$fixture->setProjects([$em->getRepository(Project::class)->find(2)]);
diff --git a/tests/Controller/ActivityControllerTest.php b/tests/Controller/ActivityControllerTest.php
index cad26821..04fbff05 100644
--- a/tests/Controller/ActivityControllerTest.php
+++ b/tests/Controller/ActivityControllerTest.php
@@ -28,6 +28,10 @@ class ActivityControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/activity/');
+ }
+
+ public function testIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/admin/activity/');
}
@@ -52,7 +56,6 @@ class ActivityControllerTest extends ControllerBaseTest
});
$this->importFixture($client, $fixture);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/activity/');
$form = $client->getCrawler()->filter('form.header-search')->form();
@@ -74,7 +77,7 @@ class ActivityControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setAmount(10);
@@ -88,7 +91,6 @@ class ActivityControllerTest extends ControllerBaseTest
$fixture->setProjects([$project]);
$this->importFixture($client, $fixture);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/activity/1/details');
self::assertHasProgressbar($client);
@@ -120,34 +122,6 @@ class ActivityControllerTest extends ControllerBaseTest
self::assertStringContainsString('123.45', $node->text(null, true));
}
- public function testDeleteRateAction()
- {
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $this->assertAccessIsGranted($client, '/admin/activity/1/rate');
- $form = $client->getCrawler()->filter('form[name=activity_rate_form]')->form();
- $client->submit($form, [
- 'activity_rate_form' => [
- 'user' => null,
- 'rate' => 123.45,
- ]
- ]);
- $this->assertIsRedirect($client, $this->createUrl('/admin/activity/1/details'));
- $client->followRedirect();
- $node = $client->getCrawler()->filter('div.box#activity_rates_box');
- self::assertEquals(1, $node->count());
- $node = $client->getCrawler()->filter('div.box#activity_rates_box table.dataTable tbody tr:not(.summary)');
- self::assertEquals(1, $node->count());
- self::assertStringContainsString('123.45', $node->text(null, true));
-
- $node = $client->getCrawler()->filter('div.box#activity_rates_box table.dataTable tbody tr td.actions a');
- self::assertEquals(1, $node->count());
- $url = $node->attr('href');
- $client->request('GET', $url);
- $this->assertIsRedirect($client, $this->createUrl('/admin/activity/1/details'));
- $node = $client->getCrawler()->filter('div.box#activity_rates_box table.dataTable tbody tr:not(.summary)');
- self::assertEquals(0, $node->count());
- }
-
public function testCreateAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
@@ -285,7 +259,7 @@ class ActivityControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
@@ -326,7 +300,7 @@ class ActivityControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
diff --git a/tests/Controller/CustomerControllerTest.php b/tests/Controller/CustomerControllerTest.php
index 5a5483f6..ac78e80c 100644
--- a/tests/Controller/CustomerControllerTest.php
+++ b/tests/Controller/CustomerControllerTest.php
@@ -29,6 +29,10 @@ class CustomerControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/customer/');
+ }
+
+ public function testIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/admin/customer/');
}
@@ -53,7 +57,6 @@ class CustomerControllerTest extends ControllerBaseTest
});
$this->importFixture($client, $fixture);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/customer/');
$form = $client->getCrawler()->filter('form.header-search')->form();
@@ -111,34 +114,6 @@ class CustomerControllerTest extends ControllerBaseTest
self::assertStringContainsString('123.45', $node->text(null, true));
}
- public function testDeleteRateAction()
- {
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $this->assertAccessIsGranted($client, '/admin/customer/1/rate');
- $form = $client->getCrawler()->filter('form[name=customer_rate_form]')->form();
- $client->submit($form, [
- 'customer_rate_form' => [
- 'user' => null,
- 'rate' => 123.45,
- ]
- ]);
- $this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
- $client->followRedirect();
- $node = $client->getCrawler()->filter('div.box#customer_rates_box');
- self::assertEquals(1, $node->count());
- $node = $client->getCrawler()->filter('div.box#customer_rates_box table.dataTable tbody tr:not(.summary)');
- self::assertEquals(1, $node->count());
- self::assertStringContainsString('123.45', $node->text(null, true));
-
- $node = $client->getCrawler()->filter('div.box#customer_rates_box table.dataTable tbody tr td.actions a');
- self::assertEquals(1, $node->count());
- $url = $node->attr('href');
- $client->request('GET', $url);
- $this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
- $node = $client->getCrawler()->filter('div.box#customer_rates_box table.dataTable tbody tr:not(.summary)');
- self::assertEquals(0, $node->count());
- }
-
public function testAddCommentAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
@@ -172,7 +147,6 @@ class CustomerControllerTest extends ControllerBaseTest
$node = $client->getCrawler()->filter('div.box#comments_box .box-comment a.confirmation-link');
self::assertEquals($this->createUrl('/admin/customer/1/comment_delete'), $node->attr('href'));
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->request($client, '/admin/customer/1/comment_delete');
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
$client->followRedirect();
@@ -197,7 +171,6 @@ class CustomerControllerTest extends ControllerBaseTest
$node = $client->getCrawler()->filter('div.box#comments_box .box-comment a.btn.active');
self::assertEquals(0, $node->count());
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->request($client, '/admin/customer/1/comment_pin');
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
$client->followRedirect();
@@ -213,7 +186,6 @@ class CustomerControllerTest extends ControllerBaseTest
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-body');
self::assertStringContainsString('Visible to everyone, as no team was assigned yet.', $node->text(null, true));
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->request($client, '/admin/customer/1/create_team');
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
$client->followRedirect();
@@ -231,7 +203,7 @@ class CustomerControllerTest extends ControllerBaseTest
self::assertEquals(1, $node->count());
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$customer = $em->getRepository(Customer::class)->find(1);
$fixture = new ProjectFixtures();
@@ -239,7 +211,6 @@ class CustomerControllerTest extends ControllerBaseTest
$fixture->setCustomers([$customer]);
$this->importFixture($client, $fixture);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/customer/1/projects/1');
$node = $client->getCrawler()->filter('div.box#project_list_box .box-tools ul.pagination li');
@@ -309,7 +280,7 @@ class CustomerControllerTest extends ControllerBaseTest
public function testTeamPermissionAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Customer $customer */
$customer = $em->getRepository(Customer::class)->find(1);
@@ -368,7 +339,7 @@ class CustomerControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
$fixture->setAmount(10);
@@ -407,7 +378,7 @@ class CustomerControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
$fixture->setAmount(10);
diff --git a/tests/Controller/DoctorControllerTest.php b/tests/Controller/DoctorControllerTest.php
index 5e181f13..1fa40970 100644
--- a/tests/Controller/DoctorControllerTest.php
+++ b/tests/Controller/DoctorControllerTest.php
@@ -19,6 +19,10 @@ class DoctorControllerTest extends ControllerBaseTest
public function testDoctorIsSecure()
{
$this->assertUrlIsSecured('/doctor');
+ }
+
+ public function testDoctorIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/doctor');
}
diff --git a/tests/Controller/ExportControllerTest.php b/tests/Controller/ExportControllerTest.php
index 9c3d29ac..b2adedca 100644
--- a/tests/Controller/ExportControllerTest.php
+++ b/tests/Controller/ExportControllerTest.php
@@ -23,6 +23,10 @@ class ExportControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/export/');
+ }
+
+ public function testIsSecureForrole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/export/');
}
@@ -39,7 +43,7 @@ class ExportControllerTest extends ControllerBaseTest
public function testIndexActionWithEntriesAndTeams()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$teamlead = $this->getUserByRole($em, User::ROLE_TEAMLEAD);
$user = $this->getUserByRole($em, User::ROLE_USER);
@@ -88,7 +92,7 @@ class ExportControllerTest extends ControllerBaseTest
$this->assertDataTableRowCount($client, 'datatable_export', 23);
// assert export type buttons are available
- $expected = ['csv', 'default.html.twig', 'default-budget.pdf.twig', 'default.pdf.twig', 'xlsx'];
+ $expected = ['csv', 'default.html.twig', 'default-budget.pdf.twig', 'default-internal.pdf.twig', 'default.pdf.twig', 'xlsx'];
$node = $client->getCrawler()->filter('#export-buttons .startExportBtn');
$this->assertEquals(count($expected), $node->count());
/** @var \DOMElement $button */
@@ -101,7 +105,7 @@ class ExportControllerTest extends ControllerBaseTest
public function testIndexActionWithEntriesForTeamleadDoesNotShowUserWithoutTeam()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$begin = new \DateTime('first day of this month');
$user = $this->getUserByRole($em, User::ROLE_USER);
@@ -140,7 +144,7 @@ class ExportControllerTest extends ControllerBaseTest
$this->assertDataTableRowCount($client, 'datatable_export', 3);
// assert export type buttons are available
- $expected = ['csv', 'default.html.twig', 'default-budget.pdf.twig', 'default.pdf.twig', 'xlsx'];
+ $expected = ['csv', 'default.html.twig', 'default-budget.pdf.twig', 'default-internal.pdf.twig', 'default.pdf.twig', 'xlsx'];
$node = $client->getCrawler()->filter('#export-buttons .startExportBtn');
$this->assertEquals(count($expected), $node->count());
/** @var \DOMElement $button */
@@ -187,7 +191,7 @@ class ExportControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$begin = new \DateTime('first day of this month');
$fixture = new TimesheetFixtures();
diff --git a/tests/Controller/HomepageControllerTest.php b/tests/Controller/HomepageControllerTest.php
index ee2fb596..25b823c4 100644
--- a/tests/Controller/HomepageControllerTest.php
+++ b/tests/Controller/HomepageControllerTest.php
@@ -35,7 +35,7 @@ class HomepageControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$pref = (new UserPreference())
diff --git a/tests/Controller/InvoiceControllerTest.php b/tests/Controller/InvoiceControllerTest.php
index 4f706ea3..7dcba0c1 100644
--- a/tests/Controller/InvoiceControllerTest.php
+++ b/tests/Controller/InvoiceControllerTest.php
@@ -50,6 +50,10 @@ class InvoiceControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/invoice/');
+ }
+
+ public function testIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/invoice/');
}
@@ -113,7 +117,7 @@ class InvoiceControllerTest extends ControllerBaseTest
public function testCopyTemplateAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new InvoiceFixtures();
$this->importFixture($client, $fixture);
@@ -141,7 +145,7 @@ class InvoiceControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new InvoiceFixtures();
$this->importFixture($client, $fixture);
@@ -207,7 +211,7 @@ class InvoiceControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new InvoiceFixtures();
$this->importFixture($client, $fixture);
@@ -266,7 +270,7 @@ class InvoiceControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new InvoiceFixtures();
$this->importFixture($client, $fixture);
@@ -378,7 +382,7 @@ class InvoiceControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new InvoiceFixtures();
$this->importFixture($client, $fixture);
@@ -396,7 +400,7 @@ class InvoiceControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new InvoiceFixtures();
$this->importFixture($client, $fixture);
diff --git a/tests/Controller/LayoutControllerTest.php b/tests/Controller/LayoutControllerTest.php
index bd40d69b..a12f8566 100644
--- a/tests/Controller/LayoutControllerTest.php
+++ b/tests/Controller/LayoutControllerTest.php
@@ -21,7 +21,7 @@ class LayoutControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$this->request($client, '/dashboard/');
@@ -77,7 +77,7 @@ class LayoutControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$this->request($client, '/layou/active_entries');
diff --git a/tests/Controller/PermissionControllerTest.php b/tests/Controller/PermissionControllerTest.php
index bff62694..244587be 100644
--- a/tests/Controller/PermissionControllerTest.php
+++ b/tests/Controller/PermissionControllerTest.php
@@ -11,16 +11,19 @@ namespace App\Tests\Controller;
use App\Entity\RolePermission;
use App\Entity\User;
-use Doctrine\ORM\EntityManager;
/**
* @group integration
*/
class PermissionControllerTest extends ControllerBaseTest
{
- public function testPermissionsIsSecure()
+ public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/permissions');
+ }
+
+ public function testIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/admin/permissions');
}
@@ -47,6 +50,10 @@ class PermissionControllerTest extends ControllerBaseTest
public function testCreateRoleIsSecured()
{
$this->assertUrlIsSecured('/admin/permissions/roles/create');
+ }
+
+ public function testCreateRoleIsSecuredForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/admin/permissions');
}
@@ -75,6 +82,10 @@ class PermissionControllerTest extends ControllerBaseTest
public function testDeleteRoleIsSecured()
{
$this->assertUrlIsSecured('/admin/permissions/roles/1/delete');
+ }
+
+ public function testDeleteRoleIsSecuredForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/admin/permissions');
}
@@ -106,6 +117,10 @@ class PermissionControllerTest extends ControllerBaseTest
public function testSavePermissionIsSecured()
{
$this->assertUrlIsSecured('/admin/permissions/roles/1/view_user/1');
+ }
+
+ public function testSavePermissionIsSecuredForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/admin/permissions');
}
@@ -121,8 +136,7 @@ class PermissionControllerTest extends ControllerBaseTest
]);
$this->assertIsRedirect($client, $this->createUrl('/admin/permissions'));
- /** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$rolePermissions = $em->getRepository(RolePermission::class)->findAll();
$this->assertEquals(0, count($rolePermissions));
diff --git a/tests/Controller/PluginControllerTest.php b/tests/Controller/PluginControllerTest.php
index a505313d..cd95e5d3 100644
--- a/tests/Controller/PluginControllerTest.php
+++ b/tests/Controller/PluginControllerTest.php
@@ -21,6 +21,10 @@ class PluginControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/plugins/');
+ }
+
+ public function testIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/admin/plugins/');
}
diff --git a/tests/Controller/ProfileControllerTest.php b/tests/Controller/ProfileControllerTest.php
index cbb112ac..08da5a18 100644
--- a/tests/Controller/ProfileControllerTest.php
+++ b/tests/Controller/ProfileControllerTest.php
@@ -48,7 +48,7 @@ class ProfileControllerTest extends ControllerBaseTest
new \DateTime('-1 year'),
];
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
foreach ($dates as $start) {
$fixture = new TimesheetFixtures();
@@ -133,7 +133,7 @@ class ProfileControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/edit');
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var User $user */
$user = $this->getUserByRole($em, User::ROLE_USER);
@@ -160,7 +160,7 @@ class ProfileControllerTest extends ControllerBaseTest
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUsername());
@@ -193,7 +193,7 @@ class ProfileControllerTest extends ControllerBaseTest
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUsername());
@@ -209,7 +209,7 @@ class ProfileControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/password');
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var User $user */
$user = $this->getUserByRole($em, User::ROLE_USER);
@@ -236,7 +236,7 @@ class ProfileControllerTest extends ControllerBaseTest
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$this->assertFalse($passwordEncoder->getEncoder($user)->isPasswordValid($user->getPassword(), UserFixtures::DEFAULT_PASSWORD, $user->getSalt()));
@@ -248,7 +248,7 @@ class ProfileControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/api-token');
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var User $user */
$user = $this->getUserByRole($em, User::ROLE_USER);
/** @var EncoderFactoryInterface $passwordEncoder */
@@ -274,7 +274,7 @@ class ProfileControllerTest extends ControllerBaseTest
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$this->assertFalse($passwordEncoder->getEncoder($user)->isPasswordValid($user->getApiToken(), UserFixtures::DEFAULT_API_TOKEN, $user->getSalt()));
@@ -293,7 +293,7 @@ class ProfileControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/roles');
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var User $user */
$user = $this->getUserByRole($em, User::ROLE_USER);
@@ -313,7 +313,7 @@ class ProfileControllerTest extends ControllerBaseTest
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$this->assertEquals(['ROLE_TEAMLEAD', 'ROLE_SUPER_ADMIN', 'ROLE_USER'], $user->getRoles());
@@ -322,13 +322,17 @@ class ProfileControllerTest extends ControllerBaseTest
public function testTeamsActionIsSecured()
{
$this->assertUrlIsSecured('/profile/' . UserFixtures::USERNAME_USER . '/teams');
+ }
+
+ public function testTeamsActionIsSecuredForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/profile/' . UserFixtures::USERNAME_USER . '/teams');
}
public function testTeamsAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var User $user */
$user = $this->getUserByRole($em, User::ROLE_USER);
@@ -359,7 +363,7 @@ class ProfileControllerTest extends ControllerBaseTest
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$this->assertEquals(1, $user->getTeams()->count());
@@ -369,27 +373,27 @@ class ProfileControllerTest extends ControllerBaseTest
{
return [
// assert that the user doesn't have the "hourly-rate_own_profile" permission
- [User::ROLE_USER, UserFixtures::USERNAME_USER, 82, 82, 'ar'],
+ [User::ROLE_USER, UserFixtures::USERNAME_USER, 82, 82, 'ar', null],
// admins are allowed to update their own hourly rate
- [User::ROLE_ADMIN, UserFixtures::USERNAME_ADMIN, 81, 37.5, 'ar'],
+ [User::ROLE_ADMIN, UserFixtures::USERNAME_ADMIN, 81, 37.5, 'ar', 19.54],
// admins are allowed to update other peoples hourly rate
- [User::ROLE_SUPER_ADMIN, UserFixtures::USERNAME_USER, 82, 37.5, 'en'],
+ [User::ROLE_SUPER_ADMIN, UserFixtures::USERNAME_USER, 82, 37.5, 'en', 19.54],
];
}
/**
* @dataProvider getPreferencesTestData
*/
- public function testPreferencesAction($role, $username, $hourlyRateOriginal, $hourlyRate, $expectedLocale)
+ public function testPreferencesAction($role, $username, $hourlyRateOriginal, $hourlyRate, $expectedLocale, $expectedInternalRate)
{
$client = $this->getClientForAuthenticatedUser($role);
$this->request($client, '/profile/' . $username . '/prefs');
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
/** @var User $user */
- $user = $this->getUserByName($em, $username);
+ $user = $this->getUserByName($username);
$this->assertEquals($hourlyRateOriginal, $user->getPreferenceValue(UserPreference::HOURLY_RATE));
+ $this->assertNull($user->getPreferenceValue(UserPreference::INTERNAL_RATE));
$this->assertNull($user->getPreferenceValue(UserPreference::SKIN));
$this->assertEquals(false, $user->getPreferenceValue('theme.collapsed_sidebar'));
$this->assertEquals('month', $user->getPreferenceValue('calendar.initial_view'));
@@ -399,6 +403,7 @@ class ProfileControllerTest extends ControllerBaseTest
'user_preferences_form' => [
'preferences' => [
['name' => UserPreference::HOURLY_RATE, 'value' => 37.5],
+ ['name' => UserPreference::INTERNAL_RATE, 'value' => 19.54],
['name' => 'timezone', 'value' => 'America/Creston'],
['name' => 'language', 'value' => 'ar'],
['name' => UserPreference::SKIN, 'value' => 'blue'],
@@ -417,10 +422,10 @@ class ProfileControllerTest extends ControllerBaseTest
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
- $user = $this->getUserByName($em, $username);
+ $user = $this->getUserByName($username);
$this->assertEquals($hourlyRate, $user->getPreferenceValue(UserPreference::HOURLY_RATE));
+ $this->assertEquals($expectedInternalRate, $user->getPreferenceValue(UserPreference::INTERNAL_RATE));
$this->assertEquals('', $user->getPreferenceValue('America/Creston'));
$this->assertEquals('ar', $user->getPreferenceValue('language'));
$this->assertEquals('blue', $user->getPreferenceValue(UserPreference::SKIN));
diff --git a/tests/Controller/ProjectControllerTest.php b/tests/Controller/ProjectControllerTest.php
index d22edb41..2aa6fa13 100644
--- a/tests/Controller/ProjectControllerTest.php
+++ b/tests/Controller/ProjectControllerTest.php
@@ -36,6 +36,10 @@ class ProjectControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/project/');
+ }
+
+ public function testIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/admin/project/');
}
@@ -60,7 +64,6 @@ class ProjectControllerTest extends ControllerBaseTest
});
$this->importFixture($client, $fixture);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/project/');
$form = $client->getCrawler()->filter('form.header-search')->form();
@@ -81,7 +84,7 @@ class ProjectControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$project = $em->getRepository(Project::class)->find(1);
@@ -97,7 +100,6 @@ class ProjectControllerTest extends ControllerBaseTest
$fixture->setProjects([$project]);
$this->importFixture($client, $fixture);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/project/1/details');
self::assertHasProgressbar($client);
@@ -146,7 +148,7 @@ class ProjectControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$project = $em->find(Project::class, 1);
$project->setMetaField((new ProjectMeta())->setName('foo')->setValue('bar'));
$project->setEnd(new \DateTime());
@@ -180,34 +182,6 @@ class ProjectControllerTest extends ControllerBaseTest
self::assertStringContainsString('123.45', $node->text(null, true));
}
- public function testDeleteRateAction()
- {
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $this->assertAccessIsGranted($client, '/admin/project/1/rate');
- $form = $client->getCrawler()->filter('form[name=project_rate_form]')->form();
- $client->submit($form, [
- 'project_rate_form' => [
- 'user' => null,
- 'rate' => 123.45,
- ]
- ]);
- $this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
- $client->followRedirect();
- $node = $client->getCrawler()->filter('div.box#project_rates_box');
- self::assertEquals(1, $node->count());
- $node = $client->getCrawler()->filter('div.box#project_rates_box table.dataTable tbody tr:not(.summary)');
- self::assertEquals(1, $node->count());
- self::assertStringContainsString('123.45', $node->text(null, true));
-
- $node = $client->getCrawler()->filter('div.box#project_rates_box table.dataTable tbody tr td.actions a');
- self::assertEquals(1, $node->count());
- $url = $node->attr('href');
- $client->request('GET', $url);
- $this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
- $node = $client->getCrawler()->filter('div.box#project_rates_box table.dataTable tbody tr:not(.summary)');
- self::assertEquals(0, $node->count());
- }
-
public function testAddCommentAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
@@ -241,7 +215,6 @@ class ProjectControllerTest extends ControllerBaseTest
$node = $client->getCrawler()->filter('div.box#comments_box .box-comment a.confirmation-link');
self::assertEquals($this->createUrl('/admin/project/1/comment_delete'), $node->attr('href'));
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->request($client, '/admin/project/1/comment_delete');
$this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
$client->followRedirect();
@@ -266,7 +239,6 @@ class ProjectControllerTest extends ControllerBaseTest
$node = $client->getCrawler()->filter('div.box#comments_box .box-comment a.btn.active');
self::assertEquals(0, $node->count());
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->request($client, '/admin/project/1/comment_pin');
$this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
$client->followRedirect();
@@ -282,7 +254,6 @@ class ProjectControllerTest extends ControllerBaseTest
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-body');
self::assertStringContainsString('Visible to everyone, as no team was assigned yet.', $node->text(null, true));
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->request($client, '/admin/project/1/create_team');
$this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
$client->followRedirect();
@@ -299,14 +270,13 @@ class ProjectControllerTest extends ControllerBaseTest
self::assertEquals('', $client->getResponse()->getContent());
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$project = $em->getRepository(Project::class)->find(1);
$fixture = new ActivityFixtures();
$fixture->setAmount(9); // to trigger a second page (every third activity is hidden)
$fixture->setProjects([$project]);
$this->importFixture($client, $fixture);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/project/1/activities/1');
$node = $client->getCrawler()->filter('div.box#activity_list_box .box-tools ul.pagination li');
@@ -398,7 +368,7 @@ class ProjectControllerTest extends ControllerBaseTest
public function testTeamPermissionAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Project $project */
$project = $em->getRepository(Project::class)->find(1);
@@ -457,7 +427,7 @@ class ProjectControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
$fixture->setAmount(10);
@@ -496,7 +466,7 @@ class ProjectControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
$fixture->setAmount(10);
diff --git a/tests/Controller/SystemConfigurationControllerTest.php b/tests/Controller/SystemConfigurationControllerTest.php
index 2bd17f61..1c555fbf 100644
--- a/tests/Controller/SystemConfigurationControllerTest.php
+++ b/tests/Controller/SystemConfigurationControllerTest.php
@@ -20,6 +20,10 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/system-config/');
+ }
+
+ public function testIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/admin/system-config/');
}
diff --git a/tests/Controller/TagControllerTest.php b/tests/Controller/TagControllerTest.php
index 1917407c..089d47f1 100644
--- a/tests/Controller/TagControllerTest.php
+++ b/tests/Controller/TagControllerTest.php
@@ -29,7 +29,7 @@ class TagControllerTest extends ControllerBaseTest
$this->importFixture($client, $fixture);
}
- public function testDebugIsSecure()
+ public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/tags/');
}
@@ -110,7 +110,7 @@ class TagControllerTest extends ControllerBaseTest
$node = $form->getFormNode();
$node->setAttribute('action', $this->createUrl('/admin/tags/multi-delete'));
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Tag[] $tags */
$tags = $em->getRepository(Tag::class)->findAll();
self::assertCount(10, $tags);
diff --git a/tests/Controller/TeamControllerTest.php b/tests/Controller/TeamControllerTest.php
index 86dd7217..375b7800 100644
--- a/tests/Controller/TeamControllerTest.php
+++ b/tests/Controller/TeamControllerTest.php
@@ -24,18 +24,21 @@ class TeamControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/teams/');
+ }
+
+ public function testIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/admin/teams/');
}
public function testIndexAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TeamFixtures();
$fixture->setAmount(5);
$this->importFixture($em, $fixture);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/teams/');
$this->assertPageActions($client, [
'search search-toggle visible-xs-inline' => '#',
@@ -49,7 +52,7 @@ class TeamControllerTest extends ControllerBaseTest
public function testIndexActionWithSearchTermQuery()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TeamFixtures();
$fixture->setAmount(5);
$fixture->setCallback(function (Team $team) {
@@ -57,7 +60,6 @@ class TeamControllerTest extends ControllerBaseTest
});
$this->importFixture($em, $fixture);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/teams/');
$form = $client->getCrawler()->filter('form.header-search')->form();
@@ -104,7 +106,7 @@ class TeamControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$this->importFixture($em, $fixture);
@@ -127,7 +129,7 @@ class TeamControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$this->importFixture($em, $fixture);
@@ -151,7 +153,7 @@ class TeamControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TeamFixtures();
$fixture->setAmount(2);
@@ -180,7 +182,7 @@ class TeamControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TeamFixtures();
$fixture->setAmount(2);
diff --git a/tests/Controller/TimesheetControllerTest.php b/tests/Controller/TimesheetControllerTest.php
index e1004693..cd10d6af 100644
--- a/tests/Controller/TimesheetControllerTest.php
+++ b/tests/Controller/TimesheetControllerTest.php
@@ -51,7 +51,7 @@ class TimesheetControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$start = new \DateTime('first day of this month');
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setAmount(5);
$fixture->setAmountRunning(2);
@@ -88,7 +88,7 @@ class TimesheetControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$start = new \DateTime('first day of this month');
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setAmount(5);
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
@@ -125,7 +125,7 @@ class TimesheetControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser();
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setAmount(5);
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
@@ -182,7 +182,7 @@ class TimesheetControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$this->assertInstanceOf(\DateTime::class, $timesheet->getBegin());
@@ -236,7 +236,7 @@ class TimesheetControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$this->assertInstanceOf(\DateTime::class, $timesheet->getBegin());
@@ -270,7 +270,7 @@ class TimesheetControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$this->assertInstanceOf(\DateTime::class, $timesheet->getBegin());
@@ -290,7 +290,7 @@ class TimesheetControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser();
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setAmount(10);
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
@@ -321,7 +321,7 @@ class TimesheetControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertHasFlashSaveSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$this->assertEquals('foo-bar', $timesheet->getDescription());
@@ -331,7 +331,7 @@ class TimesheetControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$fixture = new TimesheetFixtures();
$fixture->setAmount(10);
@@ -344,7 +344,7 @@ class TimesheetControllerTest extends ControllerBaseTest
$node = $form->getFormNode();
$node->setAttribute('action', $this->createUrl('/timesheet/multi-delete'));
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet[] $timesheets */
$timesheets = $em->getRepository(Timesheet::class)->findAll();
self::assertCount(10, $timesheets);
@@ -370,7 +370,7 @@ class TimesheetControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_SUPER_ADMIN);
$fixture = new TimesheetFixtures();
$fixture->setAmount(10);
@@ -383,7 +383,7 @@ class TimesheetControllerTest extends ControllerBaseTest
$node = $form->getFormNode();
$node->setAttribute('action', $this->createUrl('/timesheet/multi-update'));
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet[] $timesheets */
$timesheets = $em->getRepository(Timesheet::class)->findAll();
self::assertCount(10, $timesheets);
diff --git a/tests/Controller/TimesheetTeamControllerTest.php b/tests/Controller/TimesheetTeamControllerTest.php
index 2ad0211f..22e623d7 100644
--- a/tests/Controller/TimesheetTeamControllerTest.php
+++ b/tests/Controller/TimesheetTeamControllerTest.php
@@ -24,6 +24,10 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/team/timesheet/');
+ }
+
+ public function testIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/team/timesheet/');
}
@@ -54,7 +58,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$start = new \DateTime('first day of this month');
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$fixture = new TimesheetFixtures();
$fixture->setAmount(10);
@@ -91,7 +95,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$start = new \DateTime('first day of this month');
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setAmount(5);
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
@@ -128,7 +132,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$fixture = new TimesheetFixtures();
$fixture->setAmount(7);
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
@@ -185,7 +189,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertHasFlashSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$this->assertInstanceOf(\DateTime::class, $timesheet->getBegin());
@@ -198,9 +202,9 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
public function testEditAction()
{
- $client = $this->getClientForAuthenticatedUser();
+ $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$teamlead = $this->getUserByRole($em, User::ROLE_TEAMLEAD);
$fixture = new TimesheetFixtures();
@@ -209,7 +213,6 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$fixture->setStartDate('2017-05-01');
$this->importFixture($em, $fixture);
- $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->request($client, '/team/timesheet/1/edit');
$response = $client->getResponse();
@@ -235,7 +238,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertHasFlashSaveSuccess($client);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet $timesheet */
$timesheet = $em->getRepository(Timesheet::class)->find(1);
$this->assertEquals('foo-bar', $timesheet->getDescription());
@@ -246,7 +249,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_TEAMLEAD);
$fixture = new TimesheetFixtures();
$fixture->setAmount(10);
@@ -259,7 +262,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$node = $form->getFormNode();
$node->setAttribute('action', $this->createUrl('/team/timesheet/multi-delete'));
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet[] $timesheets */
$timesheets = $em->getRepository(Timesheet::class)->findAll();
self::assertCount(10, $timesheets);
@@ -285,7 +288,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_TEAMLEAD);
$fixture = new TimesheetFixtures();
$fixture->setAmount(10);
@@ -298,7 +301,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$node = $form->getFormNode();
$node->setAttribute('action', $this->createUrl('/team/timesheet/multi-update'));
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
/** @var Timesheet[] $timesheets */
$timesheets = $em->getRepository(Timesheet::class)->findAll();
self::assertCount(10, $timesheets);
diff --git a/tests/Controller/UserControllerTest.php b/tests/Controller/UserControllerTest.php
index 7ba53411..606732e7 100644
--- a/tests/Controller/UserControllerTest.php
+++ b/tests/Controller/UserControllerTest.php
@@ -21,6 +21,10 @@ class UserControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/user/');
+ }
+
+ public function testIsSecureForRole()
+ {
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/admin/user/');
}
@@ -141,7 +145,7 @@ class UserControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
+ $em = $this->getEntityManager();
$user = $this->getUserByRole($em, User::ROLE_USER);
$fixture = new TimesheetFixtures();
diff --git a/tests/Entity/ActivityRateTest.php b/tests/Entity/ActivityRateTest.php
index 027953ea..81c4b859 100644
--- a/tests/Entity/ActivityRateTest.php
+++ b/tests/Entity/ActivityRateTest.php
@@ -25,6 +25,7 @@ class ActivityRateTest extends TestCase
$sut = new ActivityRate();
self::assertNull($sut->getId());
self::assertEquals(0.00, $sut->getRate());
+ self::assertNull($sut->getInternalRate());
self::assertNull($sut->getActivity());
self::assertNull($sut->getUser());
self::assertEquals(5, $sut->getScore());
@@ -41,11 +42,18 @@ class ActivityRateTest extends TestCase
self::assertInstanceOf(ActivityRate::class, $sut->setRate(12.34));
self::assertEquals(12.34, $sut->getRate());
+ self::assertInstanceOf(ActivityRate::class, $sut->setInternalRate(7.12));
+ self::assertEquals(7.12, $sut->getInternalRate());
+ $sut->setInternalRate(null);
+ self::assertNull($sut->getInternalRate());
+
$user = new User();
$user->setAlias('foo');
$user->setUsername('bar');
self::assertInstanceOf(ActivityRate::class, $sut->setUser($user));
self::assertSame($user, $sut->getUser());
+ $sut->setUser(null);
+ self::assertNull($sut->getUser());
$entity = new Activity();
$entity->setName('foo');
diff --git a/tests/Entity/CustomerRateTest.php b/tests/Entity/CustomerRateTest.php
index 45c5c309..2b8d1025 100644
--- a/tests/Entity/CustomerRateTest.php
+++ b/tests/Entity/CustomerRateTest.php
@@ -25,6 +25,7 @@ class CustomerRateTest extends TestCase
$sut = new CustomerRate();
self::assertNull($sut->getId());
self::assertEquals(0.00, $sut->getRate());
+ self::assertNull($sut->getInternalRate());
self::assertNull($sut->getCustomer());
self::assertNull($sut->getUser());
self::assertEquals(1, $sut->getScore());
@@ -41,11 +42,18 @@ class CustomerRateTest extends TestCase
self::assertInstanceOf(CustomerRate::class, $sut->setRate(12.34));
self::assertEquals(12.34, $sut->getRate());
+ self::assertInstanceOf(CustomerRate::class, $sut->setInternalRate(7.12));
+ self::assertEquals(7.12, $sut->getInternalRate());
+ $sut->setInternalRate(null);
+ self::assertNull($sut->getInternalRate());
+
$user = new User();
$user->setAlias('foo');
$user->setUsername('bar');
self::assertInstanceOf(CustomerRate::class, $sut->setUser($user));
self::assertSame($user, $sut->getUser());
+ $sut->setUser(null);
+ self::assertNull($sut->getUser());
$entity = new Customer();
$entity->setName('foo');
diff --git a/tests/Entity/ProjectRateTest.php b/tests/Entity/ProjectRateTest.php
index a12f29af..423703f4 100644
--- a/tests/Entity/ProjectRateTest.php
+++ b/tests/Entity/ProjectRateTest.php
@@ -25,6 +25,7 @@ class ProjectRateTest extends TestCase
$sut = new ProjectRate();
self::assertNull($sut->getId());
self::assertEquals(0.00, $sut->getRate());
+ self::assertNull($sut->getInternalRate());
self::assertNull($sut->getProject());
self::assertNull($sut->getUser());
self::assertEquals(3, $sut->getScore());
@@ -41,11 +42,18 @@ class ProjectRateTest extends TestCase
self::assertInstanceOf(ProjectRate::class, $sut->setRate(12.34));
self::assertEquals(12.34, $sut->getRate());
+ self::assertInstanceOf(ProjectRate::class, $sut->setInternalRate(7.12));
+ self::assertEquals(7.12, $sut->getInternalRate());
+ $sut->setInternalRate(null);
+ self::assertNull($sut->getInternalRate());
+
$user = new User();
$user->setAlias('foo');
$user->setUsername('bar');
self::assertInstanceOf(ProjectRate::class, $sut->setUser($user));
self::assertSame($user, $sut->getUser());
+ $sut->setUser(null);
+ self::assertNull($sut->getUser());
$entity = new Project();
$entity->setName('foo');
diff --git a/tests/Entity/TimesheetTest.php b/tests/Entity/TimesheetTest.php
index f3adb00a..8d6c2f07 100644
--- a/tests/Entity/TimesheetTest.php
+++ b/tests/Entity/TimesheetTest.php
@@ -38,11 +38,14 @@ class TimesheetTest extends TestCase
self::assertNull($sut->getDescription());
self::assertSame(0.00, $sut->getRate());
self::assertNull($sut->getFixedRate());
+ self::assertNull($sut->getInternalRate());
self::assertNull($sut->getHourlyRate());
self::assertEquals(new ArrayCollection(), $sut->getTags());
self::assertEquals([], $sut->getTagsAsArray());
self::assertInstanceOf(Timesheet::class, $sut->setFixedRate(13.47));
self::assertEquals(13.47, $sut->getFixedRate());
+ self::assertInstanceOf(Timesheet::class, $sut->setInternalRate(999.99));
+ self::assertEquals(999.99, $sut->getInternalRate());
self::assertInstanceOf(Timesheet::class, $sut->setHourlyRate(99));
self::assertEquals(99, $sut->getHourlyRate());
self::assertInstanceOf(Collection::class, $sut->getMetaFields());
@@ -50,7 +53,7 @@ class TimesheetTest extends TestCase
self::assertNull($sut->getMetaField('foo'));
}
- public function testDurationCanBeNull()
+ public function testValueCanBeNull()
{
$sut = new Timesheet();
self::assertEquals(0, $sut->getDuration());
@@ -58,6 +61,11 @@ class TimesheetTest extends TestCase
self::assertNull($sut->getDuration());
$sut->setDuration(-1);
self::assertEquals(-1, $sut->getDuration());
+
+ $sut->setInternalRate(1);
+ self::assertEquals(1, $sut->getInternalRate());
+ $sut->setInternalRate(null);
+ self::assertNull($sut->getInternalRate());
}
protected function getEntity()
diff --git a/tests/EventSubscriber/DashboardSubscriberTest.php b/tests/EventSubscriber/DashboardSubscriberTest.php
index bc8067fb..331dd36b 100644
--- a/tests/EventSubscriber/DashboardSubscriberTest.php
+++ b/tests/EventSubscriber/DashboardSubscriberTest.php
@@ -27,8 +27,8 @@ class DashboardSubscriberTest extends TestCase
public function testGetSubscribedEvents()
{
$events = DashboardSubscriber::getSubscribedEvents();
- $this->assertArrayHasKey(DashboardEvent::DASHBOARD, $events);
- $methodName = $events[DashboardEvent::DASHBOARD][0];
+ $this->assertArrayHasKey(DashboardEvent::class, $events);
+ $methodName = $events[DashboardEvent::class][0];
$this->assertTrue(method_exists(DashboardSubscriber::class, $methodName));
}
diff --git a/tests/EventSubscriber/RegistrationSubscriberTest.php b/tests/EventSubscriber/RegistrationSubscriberTest.php
index 984f5693..94b26fd3 100644
--- a/tests/EventSubscriber/RegistrationSubscriberTest.php
+++ b/tests/EventSubscriber/RegistrationSubscriberTest.php
@@ -16,7 +16,9 @@ use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Model\UserManagerInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\FormInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* @covers \App\EventSubscriber\RegistrationSubscriber
@@ -29,13 +31,14 @@ class RegistrationSubscriberTest extends TestCase
$this->assertArrayHasKey(FOSUserEvents::REGISTRATION_SUCCESS, $events);
$methodName = $events[FOSUserEvents::REGISTRATION_SUCCESS][0];
$this->assertTrue(method_exists(RegistrationSubscriber::class, $methodName));
+
+ $this->assertArrayHasKey(FOSUserEvents::RESETTING_RESET_SUCCESS, $events);
+ $methodName = $events[FOSUserEvents::RESETTING_RESET_SUCCESS][0];
+ $this->assertTrue(method_exists(RegistrationSubscriber::class, $methodName));
}
/**
- * Legacy test becuase the user bundle won't update to a new release
- *
* @dataProvider getTestData
- * @group legacy
*/
public function testRoleAssignmentForNewUser(array $existingUsers, $expectedRoles)
{
@@ -54,7 +57,7 @@ class RegistrationSubscriberTest extends TestCase
$event = new FormEvent($form, $request);
- $sut = new RegistrationSubscriber($userManager);
+ $sut = new RegistrationSubscriber($userManager, $this->createMock(UrlGeneratorInterface::class));
$sut->onRegistrationSuccess($event);
$this->assertEquals($expectedRoles, $user->getRoles());
@@ -70,4 +73,27 @@ class RegistrationSubscriberTest extends TestCase
[[new User()], [User::ROLE_USER]],
];
}
+
+ public function testResetting()
+ {
+ $userManager = $this->createMock(UserManagerInterface::class);
+ $form = $this->createMock(FormInterface::class);
+ $request = $this->createMock(Request::class);
+ $router = $this->createMock(UrlGeneratorInterface::class);
+
+ $router->expects($this->any())->method('generate')->willReturnArgument(0);
+ $request->expects($this->any())->method('getLocale')->willReturn('ru');
+
+ $event = new FormEvent($form, $request);
+ self::assertNull($event->getResponse());
+
+ $sut = new RegistrationSubscriber($userManager, $router);
+ $sut->onResettingSuccess($event);
+
+ self::assertInstanceOf(RedirectResponse::class, $event->getResponse());
+
+ /** @var RedirectResponse $response */
+ $response = $event->getResponse();
+ self::assertEquals('my_profile', $response->getTargetUrl());
+ }
}
diff --git a/tests/EventSubscriber/ResetPasswordSubscriberTest.php b/tests/EventSubscriber/ResetPasswordSubscriberTest.php
index 6a03f325..974158b3 100644
--- a/tests/EventSubscriber/ResetPasswordSubscriberTest.php
+++ b/tests/EventSubscriber/ResetPasswordSubscriberTest.php
@@ -32,6 +32,9 @@ class ResetPasswordSubscriberTest extends TestCase
$this->assertTrue(method_exists(ResetPasswordSubscriber::class, $methodName));
}
+ /**
+ * @group legacy
+ */
public function testUnknownUserTypeIsIgnored()
{
$user = new TestUserEntity();
diff --git a/tests/EventSubscriber/UserPreferenceSubscriberTest.php b/tests/EventSubscriber/UserPreferenceSubscriberTest.php
new file mode 100644
index 00000000..ccd6fa38
--- /dev/null
+++ b/tests/EventSubscriber/UserPreferenceSubscriberTest.php
@@ -0,0 +1,96 @@
+assertArrayHasKey(PrepareUserEvent::class, $events);
+ $methodName = $events[PrepareUserEvent::class][0];
+ $this->assertTrue(method_exists(UserPreferenceSubscriber::class, $methodName));
+ }
+
+ public function testWithHourlyRateAllowed()
+ {
+ $sut = $this->getSubscriber(true);
+ $user = new User();
+ $event = new PrepareUserEvent($user);
+
+ self::assertSame($user, $event->getUser());
+
+ $prefs = $sut->getDefaultPreferences($user);
+ self::assertCount(11, $prefs);
+
+ foreach ($prefs as $pref) {
+ switch ($pref->getName()) {
+ case UserPreference::HOURLY_RATE:
+ case UserPreference::INTERNAL_RATE:
+ self::assertTrue($pref->isEnabled());
+ break;
+
+ default:
+ self::assertTrue($pref->isEnabled());
+ }
+ }
+ }
+
+ public function testWithHourlyRateNotAllowed()
+ {
+ $sut = $this->getSubscriber(false);
+ $user = new User();
+ $event = new PrepareUserEvent($user);
+
+ self::assertSame($user, $event->getUser());
+
+ // TODO test merging values
+ $sut->loadUserPreferences($event);
+ $prefs = $event->getUser()->getPreferences();
+ self::assertCount(11, $prefs);
+
+ foreach ($prefs as $pref) {
+ switch ($pref->getName()) {
+ case UserPreference::HOURLY_RATE:
+ case UserPreference::INTERNAL_RATE:
+ self::assertFalse($pref->isEnabled());
+ break;
+
+ default:
+ self::assertTrue($pref->isEnabled());
+ }
+ }
+ }
+
+ protected function getSubscriber(bool $seeHourlyRate)
+ {
+ $authMock = $this->createMock(AuthorizationCheckerInterface::class);
+ $authMock->expects($this->once())->method('isGranted')->willReturn($seeHourlyRate);
+
+ $eventMock = $this->createMock(EventDispatcherInterface::class);
+ $tokenMock = $this->createMock(TokenStorageInterface::class);
+ $formConfigMock = $this->createMock(FormConfiguration::class);
+
+ return new UserPreferenceSubscriber($eventMock, $tokenMock, $authMock, $formConfigMock);
+ }
+}
diff --git a/tests/Export/Renderer/CsvRendererTest.php b/tests/Export/Renderer/CsvRendererTest.php
index c509dc57..a935443e 100644
--- a/tests/Export/Renderer/CsvRendererTest.php
+++ b/tests/Export/Renderer/CsvRendererTest.php
@@ -83,26 +83,27 @@ class CsvRendererTest extends AbstractRendererTest
2 => '12:06',
3 => '400',
4 => '0',
- 5 => 'kevin',
- 6 => 'Customer Name',
- 7 => 'project name',
- 8 => 'activity description',
- 9 => '',
+ 5 => '',
+ 6 => 'kevin',
+ 7 => 'Customer Name',
+ 8 => 'project name',
+ 9 => 'activity description',
10 => '',
- 11 => 'foo,bar',
- 12 => '',
- 13 => ' EUR 84.00 ',
- 14 => 'meta-bar',
- 15 => 'meta-bar2',
- 16 => 'customer-bar',
- 17 => '',
- 18 => 'project-foo2',
- 19 => 'activity-bar',
+ 11 => '',
+ 12 => 'foo,bar',
+ 13 => '',
+ 14 => ' EUR 84.00 ',
+ 15 => 'meta-bar',
+ 16 => 'meta-bar2',
+ 17 => 'customer-bar',
+ 18 => '',
+ 19 => 'project-foo2',
+ 20 => 'activity-bar',
];
self::assertEquals(7, count($all));
self::assertEquals($expected, $all[5]);
self::assertEquals(count($expected), count($all[0]));
- self::assertEquals('foo', $all[4][11]);
+ self::assertEquals('foo', $all[4][12]);
}
}
diff --git a/tests/Export/Timesheet/CsvRendererTest.php b/tests/Export/Timesheet/CsvRendererTest.php
index a8e2d715..3ee4c536 100644
--- a/tests/Export/Timesheet/CsvRendererTest.php
+++ b/tests/Export/Timesheet/CsvRendererTest.php
@@ -80,26 +80,27 @@ class CsvRendererTest extends AbstractRendererTest
2 => '12:06',
3 => '400',
4 => '0',
- 5 => 'kevin',
- 6 => 'Customer Name',
- 7 => 'project name',
- 8 => 'activity description',
- 9 => '',
+ 5 => '',
+ 6 => 'kevin',
+ 7 => 'Customer Name',
+ 8 => 'project name',
+ 9 => 'activity description',
10 => '',
- 11 => 'foo,bar',
- 12 => '',
- 13 => ' EUR 84.00 ',
- 14 => 'meta-bar',
- 15 => 'meta-bar2',
- 16 => 'customer-bar',
- 17 => '',
- 18 => 'project-foo2',
- 19 => 'activity-bar',
+ 11 => '',
+ 12 => 'foo,bar',
+ 13 => '',
+ 14 => ' EUR 84.00 ',
+ 15 => 'meta-bar',
+ 16 => 'meta-bar2',
+ 17 => 'customer-bar',
+ 18 => '',
+ 19 => 'project-foo2',
+ 20 => 'activity-bar',
];
self::assertEquals(7, count($all));
self::assertEquals($expected, $all[5]);
self::assertEquals(count($expected), count($all[0]));
- self::assertEquals('foo', $all[4][11]);
+ self::assertEquals('foo', $all[4][12]);
}
}
diff --git a/tests/Invoice/Hydrator/InvoiceItemDefaultHydratorTest.php b/tests/Invoice/Hydrator/InvoiceItemDefaultHydratorTest.php
index e7030ffb..496f4cd9 100644
--- a/tests/Invoice/Hydrator/InvoiceItemDefaultHydratorTest.php
+++ b/tests/Invoice/Hydrator/InvoiceItemDefaultHydratorTest.php
@@ -45,6 +45,9 @@ class InvoiceItemDefaultHydratorTest extends TestCase
'entry.rate',
'entry.rate_nc',
'entry.rate_plain',
+ 'entry.rate_internal',
+ 'entry.rate_internal_nc',
+ 'entry.rate_internal_plain',
'entry.total',
'entry.total_nc',
'entry.total_plain',
diff --git a/tests/Invoice/InvoiceItemTest.php b/tests/Invoice/InvoiceItemTest.php
index a556d00d..5ddb969d 100644
--- a/tests/Invoice/InvoiceItemTest.php
+++ b/tests/Invoice/InvoiceItemTest.php
@@ -26,6 +26,7 @@ class InvoiceItemTest extends TestCase
self::assertNull($sut->getFixedRate());
self::assertNull($sut->getEnd());
self::assertEquals(0.00, $sut->getRate());
+ self::assertEquals(0.00, $sut->getInternalRate());
self::assertNull($sut->getProject());
self::assertIsArray($sut->getAdditionalFields());
self::assertEmpty($sut->getAdditionalFields());
diff --git a/tests/Invoice/Renderer/DebugRendererTest.php b/tests/Invoice/Renderer/DebugRendererTest.php
index 18a44b75..0ad49b0c 100644
--- a/tests/Invoice/Renderer/DebugRendererTest.php
+++ b/tests/Invoice/Renderer/DebugRendererTest.php
@@ -199,6 +199,9 @@ class DebugRendererTest extends TestCase
'entry.rate',
'entry.rate_nc',
'entry.rate_plain',
+ 'entry.rate_internal',
+ 'entry.rate_internal_nc',
+ 'entry.rate_internal_plain',
'entry.total',
'entry.total_nc',
'entry.total_plain',
diff --git a/tests/Invoice/templates/default.pdf.twig b/tests/Invoice/templates/default.pdf.twig
index 3c55b4b0..a79b9a74 100644
--- a/tests/Invoice/templates/default.pdf.twig
+++ b/tests/Invoice/templates/default.pdf.twig
@@ -1,6 +1,7 @@
{% extends 'invoice/layout.html.twig' %}
{% set language = model.template.language|default(app.request.locale) %}
{% set isDecimal = model.template.decimalDuration|default(false) %}
+{% set currency = model.currency %}
{% block invoice %}
@@ -91,9 +92,9 @@
{% if entry.activity is not null %}{{ entry.activity.name }} / {% endif %}{{ entry.project.name }}
{% endif %}
-
{{ rate|money(model.calculator.currency) }}
+ {{ rate|money(currency) }}
{{ duration }}
- {{ entry.rate|money(model.calculator.currency) }}
+ {{ entry.rate|money(currency) }}
{% endfor %}
@@ -102,20 +103,20 @@
{{ 'invoice.subtotal'|trans({}, 'messages', language) }}
- {{ model.calculator.subtotal|money(model.calculator.currency) }}
+ {{ model.calculator.subtotal|money(currency) }}
{{ 'invoice.tax'|trans({}, 'messages', language) }} ({{ model.calculator.vat }}%)
- {{ model.calculator.tax|money(model.calculator.currency) }}
+ {{ model.calculator.tax|money(currency) }}
{{ 'invoice.total'|trans({}, 'messages', language) }}
- {{ model.calculator.total|money(model.calculator.currency) }}
+ {{ model.calculator.total|money(currency) }}
diff --git a/tests/KernelTestTrait.php b/tests/KernelTestTrait.php
index 8eb4b6ee..d97293b3 100644
--- a/tests/KernelTestTrait.php
+++ b/tests/KernelTestTrait.php
@@ -15,6 +15,7 @@ use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\ORM\EntityManager;
+use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpKernel\HttpKernelBrowser;
@@ -23,20 +24,27 @@ use Symfony\Component\HttpKernel\HttpKernelBrowser;
*/
trait KernelTestTrait
{
+ public function getEntityManager(): EntityManagerInterface
+ {
+ if (!$this instanceof KernelTestCase) {
+ throw new \Exception('KernelTestTrait can only be used in a KernelTestCase');
+ }
+
+ return $this::$container->get('doctrine.orm.entity_manager');
+ }
+
/**
* @param HttpKernelBrowser|EntityManager|KernelTestCase $client
* @param Fixture $fixture
*/
protected function importFixture($client, Fixture $fixture)
{
- if ($client instanceof HttpKernelBrowser) {
- $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
- } elseif ($client instanceof EntityManager) {
+ if ($client instanceof EntityManager) {
$em = $client;
- } elseif ($client instanceof KernelTestCase) {
- $em = $client::$container->get('doctrine.orm.entity_manager');
+ } elseif ($this instanceof KernelTestCase) {
+ $em = $this::$container->get('doctrine.orm.entity_manager');
} else {
- throw new \InvalidArgumentException('Fixtures need an EntityManager to be imported');
+ throw new \InvalidArgumentException('Need an EntityManager to import fixtures');
}
$loader = new Loader();
@@ -46,9 +54,9 @@ trait KernelTestTrait
$executor->execute($loader->getFixtures(), true);
}
- protected function getUserByName(EntityManager $em, string $username): ?User
+ protected function getUserByName(string $username): ?User
{
- return $em->getRepository(User::class)->findOneBy(['username' => $username]);
+ return $this->getEntityManager()->getRepository(User::class)->findOneBy(['username' => $username]);
}
/**
@@ -81,6 +89,6 @@ trait KernelTestTrait
return null;
}
- return $this->getUserByName($em, $name);
+ return $this->getUserByName($name);
}
}
diff --git a/tests/Model/TimesheetCountedStatisticTest.php b/tests/Model/TimesheetCountedStatisticTest.php
new file mode 100644
index 00000000..cb2ba0c3
--- /dev/null
+++ b/tests/Model/TimesheetCountedStatisticTest.php
@@ -0,0 +1,42 @@
+getRecordRate());
+ self::assertEquals(0, $sut->getRecordDuration());
+ self::assertEquals(0, $sut->getRecordAmount());
+ self::assertEquals(0.0, $sut->getRecordInternalRate());
+ }
+
+ public function testSetter()
+ {
+ $sut = new TimesheetCountedStatistic();
+ self::assertInstanceOf(TimesheetCountedStatistic::class, $sut->setRecordRate(23.97));
+ self::assertInstanceOf(TimesheetCountedStatistic::class, $sut->setRecordDuration(21));
+ self::assertInstanceOf(TimesheetCountedStatistic::class, $sut->setRecordAmount(5));
+ self::assertInstanceOf(TimesheetCountedStatistic::class, $sut->setRecordInternalRate(99.09));
+
+ self::assertEquals(23.97, $sut->getRecordRate());
+ self::assertEquals(21, $sut->getRecordDuration());
+ self::assertEquals(5, $sut->getRecordAmount());
+ self::assertEquals(99.09, $sut->getRecordInternalRate());
+ }
+}
diff --git a/tests/Timesheet/Calculator/RateCalculatorTest.php b/tests/Timesheet/Calculator/RateCalculatorTest.php
index 06a70d0c..ab13b81b 100644
--- a/tests/Timesheet/Calculator/RateCalculatorTest.php
+++ b/tests/Timesheet/Calculator/RateCalculatorTest.php
@@ -44,6 +44,7 @@ class RateCalculatorTest extends TestCase
$record->setDuration(1800);
$record->setHourlyRate(100);
$record->setActivity(new Activity());
+ $record->setUser($this->getTestUser());
$sut = new RateCalculator([], $this->getRateRepositoryMock());
$sut->calculate($record);
@@ -59,6 +60,7 @@ class RateCalculatorTest extends TestCase
// make sure that fixed rate is always applied, even if hourly rate is set
$record->setHourlyRate(99);
$record->setActivity(new Activity());
+ $record->setUser($this->getTestUser());
$sut = new RateCalculator([], $this->getRateRepositoryMock());
$sut->calculate($record);
@@ -66,45 +68,59 @@ class RateCalculatorTest extends TestCase
}
public function getRateTestData()
- {
- yield 'a0' => [0.0, 0, 0, null, null, null, null, null, null, null, null];
- yield 'a1' => [0.0, 1800, 0, null, null, null, null, null, null, null, null];
- yield 'a2' => [0.5, 1800, 1, null, null, null, null, null, null, null, null];
- yield 'a3' => [0.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
- yield 'b1' => [1.5, 1800, 1, 3, null, 5, null, 7, null, 9, null];
- yield 'b2' => [2.5, 1800, 1, null, null, 5, null, 7, null, 9, null];
- yield 'b3' => [3.5, 1800, 1, null, null, null, null, 7, null, 9, null];
- yield 'b4' => [4.5, 1800, 1, null, null, null, null, null, null, 9, null];
- yield 'b5' => [2.0, 1800, 1, null, 2, null, 3, null, 4, null, 5];
- yield 'b6' => [3.0, 1800, 1, null, null, null, 3, null, 4, null, 5];
- yield 'b7' => [4.0, 1800, 1, null, null, null, null, null, 4, null, 5];
- yield 'b8' => [3.0, 1800, 1, null, null, null, 3, null, null, null, 5];
- yield 'b9' => [2.0, 1800, 1, null, 2, null, null, null, null, null, 5];
- yield 'c0' => [5.0, 1800, 100, 10, null, null, null, null, null, null, null];
- yield 'd0' => [10, 1800, 100, null, 10, null, null, null, null, null, null];
- yield 'e0' => [10, 1800, 100, null, null, 20, null, null, null, null, null];
- yield 'f0' => [20, 1800, 100, null, null, null, 20, null, null, null, null];
- yield 'g0' => [15, 1800, 100, null, null, null, null, 30, null, null, null];
- yield 'h0' => [30, 1800, 100, null, null, null, null, null, 30, null, null];
- yield 'i0' => [20, 1800, 100, null, null, null, null, null, null, 40, null];
- yield 'j0' => [40, 1800, 100, null, null, null, null, null, null, null, 40];
+ { // expected, expInt, durat, userH, userIn, timeH, timeF, actH, actIn, actF, proH, proIn, proFi, custH, custIn, custF
+ yield 'a0' => [0.0, 0.0, 0, 0, 0, null, null, null, null, false, null, null, false, null, null, false];
+ yield 'a2' => [0.0, 0.0, 0, 0, null, null, null, null, null, false, null, null, false, null, null, false];
+ yield 'a4' => [0.0, 0.0, 1800, 0, 0, null, null, null, null, false, null, null, false, null, null, false];
+ yield 'a6' => [0.5, 6.72, 1800, 1, 13.44, null, null, null, null, false, null, null, false, null, null, false];
+ yield 'a8' => [0.0, 1, 0, 0, 0, 0, 0, 0, 1, true, 0, null, true, 0, null, true];
+ // rate: 1.5 => timesheet hourly rate , internal: 2.5 => activity hourly rate (30 min)
+ yield 'b1' => [1.5, 2.5, 1800, 1, 1, 3, null, 5, null, false, 7, null, false, 9, null, false];
+ yield 'b2' => [2.5, 2.5, 1800, 1, 1, null, null, 5, null, false, 7, null, false, 9, null, false];
+ yield 'b3' => [3.5, 6.5, 1800, 1, 1, null, null, null, null, false, 7, 13, false, 9, 9, false];
+ yield 'b4' => [4.5, 6.5, 1800, 1, 15, null, null, null, null, false, null, null, false, 9, 13, false];
+ // rate: 2.0 => timesheet fixed rate , internal: 3.0 => activity fixed rate
+ yield 'b5' => [2.0, 3.0, 1800, 1, 1, null, 2, 3, null, true, 4, null, true, 5, null, true];
+ yield 'b6' => [3.0, 3.0, 1800, 1, 1, null, null, 3, null, true, 4, null, true, 5, null, true];
+ yield 'b7' => [4.0, 4.0, 1800, 1, 1, null, null, null, null, false, 4, null, true, 5, null, true];
+ yield 'b8' => [3.0, 3.0, 1800, 1, 1, null, null, 3, null, true, null, null, false, 5, null, true];
+ // rate: 2.0 => timesheet fixed rate , internal: 5.0 => customer hourly rate
+ yield 'b9' => [2.0, 5.0, 1800, 1, 1, null, 2, null, null, false, null, null, false, 5, null, true];
+ // rate: 5.0 => timesheet hourly rate , internal: 7.5 => user internal rate (30 min)
+ yield 'c0' => [5.0, 7.5, 1800, 100, 15, 10, null, null, null, false, null, null, false, null, null, false];
+ // internal: 10 because no rule applies and as fallback the users internal rate is used
+ yield 'd0' => [10, 100, 1800, 100, 100, null, 10, null, null, false, null, null, false, null, null, false];
+ yield 'e0' => [10, 10, 1800, 100, 100, null, null, 20, null, false, null, null, false, null, null, false];
+ yield 'f0' => [20, 78, 1800, 100, 100, null, null, 20, 78, true, null, null, false, null, null, false];
+ yield 'g0' => [15, 11.5, 1800, 100, 100, null, null, null, null, false, 30, 23, false, null, null, false];
+ yield 'h0' => [30, 30, 1800, 100, 100, null, null, null, null, false, 30, null, true, null, null, false];
+ yield 'i0' => [20, 13.5, 1800, 100, 100, null, null, null, null, false, null, null, false, 40, 27, false];
+ yield 'j0' => [40, 84, 1800, 100, 45, null, null, null, null, false, null, null, false, 40, 84, true];
+ // make sure the last fallback for the internal rate is the users hourly rate
+ yield 'k0' => [8.82, 6, 1800, 17.64, 12, null, null, null, null, false, null, null, false, null, null, true];
+ yield 'k1' => [8.82, 8.82, 1800, 17.64, null, null, null, null, null, false, null, null, false, null, null, true];
}
/**
* @dataProvider getRateTestData
*/
public function testRates(
- $exptectedRate,
+ $expectedRate,
+ $expectedInternalRate,
$duration,
$userRate,
+ $userInternalRate,
$timesheetHourly,
$timesheetFixed,
- $activityHourly,
- $activityFixed,
- $projectHourly,
- $projectFixed,
- $customerHourly,
- $customerFixed
+ $activityRate,
+ $activityInternal,
+ $activityIsFixed,
+ $projectRate,
+ $projectInternal,
+ $projectIsFixed,
+ $customerRate,
+ $customerInternal,
+ $customerIsFixed
) {
$customer = new Customer();
@@ -122,42 +138,60 @@ class RateCalculatorTest extends TestCase
->setActivity($activity)
->setProject($project)
->setDuration($duration)
- ->setUser($this->getTestUser($userRate))
+ ->setUser($this->getTestUser($userRate, $userInternalRate))
;
$rates = [];
- if (null !== $customerFixed) {
- $rates[] = (new CustomerRate())->setRate($customerFixed)->setIsFixed(true);
- } elseif (null !== $customerHourly) {
- $rates[] = (new CustomerRate())->setRate($customerHourly);
+ if (null !== $customerRate) {
+ $rate = new CustomerRate();
+ $rate->setRate($customerRate);
+ $rate->setIsFixed($customerIsFixed);
+ if (null !== $customerInternal) {
+ $rate->setInternalRate($customerInternal);
+ }
+ $rates[] = $rate;
}
- if (null !== $projectFixed) {
- $rates[] = (new ProjectRate())->setRate($projectFixed)->setIsFixed(true);
- } elseif (null !== $projectHourly) {
- $rates[] = (new ProjectRate())->setRate($projectHourly);
+ if (null !== $projectRate) {
+ $rate = new ProjectRate();
+ $rate->setRate($projectRate);
+ $rate->setIsFixed($projectIsFixed);
+ if (null !== $projectInternal) {
+ $rate->setInternalRate($projectInternal);
+ }
+ $rates[] = $rate;
}
- if (null !== $activityFixed) {
- $rates[] = (new ActivityRate())->setRate($activityFixed)->setIsFixed(true);
- } elseif (null !== $activityHourly) {
- $rates[] = (new ActivityRate())->setRate($activityHourly);
+ if (null !== $activityRate) {
+ $rate = new ActivityRate();
+ $rate->setRate($activityRate);
+ $rate->setIsFixed($activityIsFixed);
+ if (null !== $activityInternal) {
+ $rate->setInternalRate($activityInternal);
+ }
+ $rates[] = $rate;
}
$sut = new RateCalculator([], $this->getRateRepositoryMock($rates));
$sut->calculate($timesheet);
- $this->assertEquals($exptectedRate, $timesheet->getRate());
+ $this->assertEquals($expectedRate, $timesheet->getRate());
+ $this->assertEquals($expectedInternalRate, $timesheet->getInternalRate());
}
- protected function getTestUser($rate = 75)
+ protected function getTestUser($rate = 75, $internalRate = 75)
{
+ $user = new User();
+
$pref = new UserPreference();
$pref->setName(UserPreference::HOURLY_RATE);
$pref->setValue($rate);
- $user = new User();
- $user->setPreferences([$pref]);
+ $prefInt = new UserPreference();
+ $prefInt->setName(UserPreference::INTERNAL_RATE);
+ $prefInt->setValue($internalRate);
+
+ $user->setPreferences([$pref, $prefInt]);
return $user;
}
diff --git a/tests/phpstan.neon b/tests/phpstan.neon
index 3dcde60c..fc982aa0 100644
--- a/tests/phpstan.neon
+++ b/tests/phpstan.neon
@@ -5,8 +5,6 @@ includes:
parameters:
tmpDir: %rootDir%/../../../var/cache/phpstan
- symfony:
- container_xml_path: '%rootDir%/../../../var/cache/dev/srcApp_KernelDevDebugContainer.xml'
ignoreErrors:
- '#Access to an undefined property Faker\\Generator::\$stateAbbr.#'
- '#Access to an undefined property Faker\\Generator::\$catchPhrase.#'
diff --git a/translations/export.de.xlf b/translations/export.de.xlf
index 20703bd2..3dc4b8d8 100644
--- a/translations/export.de.xlf
+++ b/translations/export.de.xlf
@@ -10,6 +10,10 @@
default-budget.pdf.twig
Inkl. Restbudget
+
+ default-internal.pdf.twig
+ Inkl. interner Kosten
+