Release 2.0.11 (#3932)

- added "today" as selector in date-range dropdown
- added feature to prevent auto-select of dropdowns with only one entry
- added hint that no changes were detected in batch update
- added negative invoice sums are possible (e.g. for credit notes)
- fix project list is expanded after submission
- fix invalid date parsing causes 500
- fix: prevent auto-select of activities in export and invoice form (in case only one global activity exists)
- fix team assignments for customer and project were not saved (using API now)
- fix form fieldset with legend styling (e.g. team project assignment)
- fix required meta-field were forced to have a value in batch update
- fix tomselect meta-field was not disabled in batch update
- fix unset internal rate is shown as 0
- fix one minute rounding problem in duration-only mode  with "now" being default time
- fix column width and label for duration-only mode
- tech debt: cleanup invoice template (remove invoice layout)
- tech debt: reorder for simpler comparison with invoice form
- possible BC for devs: remove unused methods from form trait
- bump composer packages (includes new translations for auth screens)
This commit is contained in:
Kevin Papst
2023-03-21 12:42:18 +01:00
committed by GitHub
parent 252652082d
commit 3a5d7a62de
41 changed files with 590 additions and 1288 deletions

View File

@@ -278,7 +278,7 @@ export default class KimaiFormSelect extends KimaiFormPlugin {
const optionLength = allOptions.length;
let selectOption = '';
if (optionLength === 1) {
if (optionLength === 1 && node.dataset['autoselect'] === undefined) {
selectOption = allOptions[0].value;
} else if (optionLength === 2 && emptyOption !== null) {
selectOption = allOptions[1].value;

View File

@@ -4,3 +4,9 @@
fieldset legend.col-form-label {
padding-top: 0;
}
/* See project assignment to teams */
fieldset.form-fieldset legend {
font-size: 1rem;
font-weight: bold;
}

View File

@@ -34,7 +34,7 @@
"friendsofsymfony/rest-bundle": "^3.0",
"gedmo/doctrine-extensions": "^3.6",
"jms/serializer-bundle": "^5.0",
"kevinpapst/tabler-bundle": "^0.12",
"kevinpapst/tabler-bundle": "^0.13",
"league/csv": "^9.4",
"mpdf/mpdf": "^8.0",
"nelmio/api-doc-bundle": "^4.0",

100
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "56bca57f2bb60cae0ec13012925f7b89",
"content-hash": "42733cc81b93bcae0a70454796d6c486",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -2415,16 +2415,16 @@
},
{
"name": "kevinpapst/tabler-bundle",
"version": "0.12",
"version": "0.13",
"source": {
"type": "git",
"url": "https://github.com/kevinpapst/TablerBundle.git",
"reference": "b2643be7f670f07764bac35af498f09ee93258dd"
"reference": "28287dc810186ef3188811af071cb67832e7c7f0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kevinpapst/TablerBundle/zipball/b2643be7f670f07764bac35af498f09ee93258dd",
"reference": "b2643be7f670f07764bac35af498f09ee93258dd",
"url": "https://api.github.com/repos/kevinpapst/TablerBundle/zipball/28287dc810186ef3188811af071cb67832e7c7f0",
"reference": "28287dc810186ef3188811af071cb67832e7c7f0",
"shasum": ""
},
"require": {
@@ -2473,7 +2473,7 @@
"description": "Admin/Backend theme bundle for Symfony based on Tabler.io",
"support": {
"issues": "https://github.com/kevinpapst/TablerBundle/issues",
"source": "https://github.com/kevinpapst/TablerBundle/tree/0.12"
"source": "https://github.com/kevinpapst/TablerBundle/tree/0.13"
},
"funding": [
{
@@ -2485,7 +2485,7 @@
"type": "github"
}
],
"time": "2023-03-04T23:55:06+00:00"
"time": "2023-03-21T09:35:32+00:00"
},
{
"name": "laminas/laminas-escaper",
@@ -2551,34 +2551,37 @@
},
{
"name": "league/csv",
"version": "9.8.0",
"version": "9.9.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/csv.git",
"reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47"
"reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/9d2e0265c5d90f5dd601bc65ff717e05cec19b47",
"reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/b4418ede47fbd88facc34e40a16c8ce9153b961b",
"reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-mbstring": "*",
"php": "^7.4 || ^8.0"
"php": "^8.1.2"
},
"require-dev": {
"ext-curl": "*",
"doctrine/collections": "^2.1.2",
"ext-dom": "*",
"friendsofphp/php-cs-fixer": "^v3.4.0",
"phpstan/phpstan": "^1.3.0",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.1.0",
"phpunit/phpunit": "^9.5.11"
"ext-xdebug": "*",
"friendsofphp/php-cs-fixer": "^v3.14.3",
"phpbench/phpbench": "^1.2.8",
"phpstan/phpstan": "^1.10.4",
"phpstan/phpstan-deprecation-rules": "^1.1.2",
"phpstan/phpstan-phpunit": "^1.3.10",
"phpstan/phpstan-strict-rules": "^1.5.0",
"phpunit/phpunit": "^10.0.14"
},
"suggest": {
"ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes",
"ext-dom": "Required to use the XMLConverter and the HTMLConverter classes",
"ext-iconv": "Needed to ease transcoding CSV using iconv stream filters"
},
"type": "library",
@@ -2631,7 +2634,7 @@
"type": "github"
}
],
"time": "2022-01-04T00:13:07+00:00"
"time": "2023-03-11T15:57:12+00:00"
},
{
"name": "lorenzo/pinky",
@@ -10312,16 +10315,16 @@
},
{
"name": "zircote/swagger-php",
"version": "4.7.3",
"version": "4.7.8",
"source": {
"type": "git",
"url": "https://github.com/zircote/swagger-php.git",
"reference": "0299edc47eb4813a2c598a0348eaf205704adc92"
"reference": "4a1d1272c2dd3afd8d96e92f02dc3565d5cc3c18"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/0299edc47eb4813a2c598a0348eaf205704adc92",
"reference": "0299edc47eb4813a2c598a0348eaf205704adc92",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/4a1d1272c2dd3afd8d96e92f02dc3565d5cc3c18",
"reference": "4a1d1272c2dd3afd8d96e92f02dc3565d5cc3c18",
"shasum": ""
},
"require": {
@@ -10384,9 +10387,9 @@
],
"support": {
"issues": "https://github.com/zircote/swagger-php/issues",
"source": "https://github.com/zircote/swagger-php/tree/4.7.3"
"source": "https://github.com/zircote/swagger-php/tree/4.7.8"
},
"time": "2023-03-08T02:27:27+00:00"
"time": "2023-03-19T21:25:02+00:00"
}
],
"packages-dev": [
@@ -10829,16 +10832,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.14.4",
"version": "v3.15.1",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "1b3d9dba63d93b8a202c31e824748218781eae6b"
"reference": "d48755372a113bddb99f749e34805d83f3acfe04"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/1b3d9dba63d93b8a202c31e824748218781eae6b",
"reference": "1b3d9dba63d93b8a202c31e824748218781eae6b",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/d48755372a113bddb99f749e34805d83f3acfe04",
"reference": "d48755372a113bddb99f749e34805d83f3acfe04",
"shasum": ""
},
"require": {
@@ -10905,9 +10908,15 @@
}
],
"description": "A tool to automatically fix PHP code style",
"keywords": [
"Static code analysis",
"fixer",
"standards",
"static analysis"
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.14.4"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.15.1"
},
"funding": [
{
@@ -10915,7 +10924,7 @@
"type": "github"
}
],
"time": "2023-02-09T21:49:13+00:00"
"time": "2023-03-13T23:26:30+00:00"
},
{
"name": "masterminds/html5",
@@ -11155,16 +11164,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.10.5",
"version": "1.10.7",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "1fb6f494d82455151ecf15c5c191923f5d84324e"
"reference": "b10ceb526d9607903c5b2673f1fc8775dbe48975"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/1fb6f494d82455151ecf15c5c191923f5d84324e",
"reference": "1fb6f494d82455151ecf15c5c191923f5d84324e",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b10ceb526d9607903c5b2673f1fc8775dbe48975",
"reference": "b10ceb526d9607903c5b2673f1fc8775dbe48975",
"shasum": ""
},
"require": {
@@ -11193,8 +11202,11 @@
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.10.5"
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
@@ -11210,20 +11222,20 @@
"type": "tidelift"
}
],
"time": "2023-03-07T16:48:45+00:00"
"time": "2023-03-16T15:24:20+00:00"
},
{
"name": "phpstan/phpstan-doctrine",
"version": "1.3.33",
"version": "1.3.37",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-doctrine.git",
"reference": "fcb67c2a747300ff8e6ae278191f6ff79fc90370"
"reference": "62bd362b432fe29e175168689510ddd927b698f8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/fcb67c2a747300ff8e6ae278191f6ff79fc90370",
"reference": "fcb67c2a747300ff8e6ae278191f6ff79fc90370",
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/62bd362b432fe29e175168689510ddd927b698f8",
"reference": "62bd362b432fe29e175168689510ddd927b698f8",
"shasum": ""
},
"require": {
@@ -11278,9 +11290,9 @@
"description": "Doctrine extensions for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
"source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.33"
"source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.37"
},
"time": "2023-02-21T08:52:52+00:00"
"time": "2023-03-17T14:57:03+00:00"
},
{
"name": "phpstan/phpstan-phpunit",

View File

@@ -1560,109 +1560,9 @@ parameters:
count: 1
path: src/Controller/TagController.php
-
message: "#^Method App\\\\Controller\\\\TagController\\:\\:createAction\\(\\) has no return type specified\\.$#"
count: 1
path: src/Controller/TagController.php
-
message: "#^Method App\\\\Controller\\\\TagController\\:\\:editAction\\(\\) has no return type specified\\.$#"
count: 1
path: src/Controller/TagController.php
-
message: "#^Method App\\\\Controller\\\\TagController\\:\\:multiDelete\\(\\) has no return type specified\\.$#"
count: 1
path: src/Controller/TagController.php
-
message: "#^Parameter \\#1 \\$tags of method App\\\\Repository\\\\TagRepository\\:\\:multiDelete\\(\\) expects iterable\\<App\\\\Entity\\\\Tag\\>, iterable\\<object\\> given\\.$#"
count: 1
path: src/Controller/TagController.php
-
message: "#^Method App\\\\Controller\\\\TeamController\\:\\:duplicateTeam\\(\\) has no return type specified\\.$#"
count: 1
path: src/Controller/TeamController.php
-
message: "#^Method App\\\\Controller\\\\TeamController\\:\\:editAction\\(\\) has no return type specified\\.$#"
count: 1
path: src/Controller/TeamController.php
-
message: "#^Method App\\\\Controller\\\\TeamController\\:\\:editMemberAction\\(\\) has no return type specified\\.$#"
count: 1
path: src/Controller/TeamController.php
-
message: "#^Call to an undefined method object\\:\\:addTag\\(\\)\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:getMetaField\\(\\)\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:getTags\\(\\)\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:removeTag\\(\\)\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:setActivity\\(\\)\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:setBillable\\(\\)\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:setExported\\(\\)\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:setFixedRate\\(\\)\\.$#"
count: 3
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:setHourlyRate\\(\\)\\.$#"
count: 3
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:setInternalRate\\(\\)\\.$#"
count: 3
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:setMetaField\\(\\)\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:setProject\\(\\)\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Call to an undefined method object\\:\\:setUser\\(\\)\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Cannot call method getEntities\\(\\) on mixed\\.$#"
count: 3
count: 2
path: src/Controller/TimesheetAbstractController.php
-
@@ -1675,11 +1575,6 @@ parameters:
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Method App\\\\Controller\\\\TimesheetAbstractController\\:\\:multiUpdate\\(\\) has no return type specified\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Method App\\\\Controller\\\\TimesheetAbstractController\\:\\:prepareQuery\\(\\) has no return type specified\\.$#"
count: 1
@@ -1690,16 +1585,6 @@ parameters:
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Parameter \\#1 \\$timesheets of method App\\\\Timesheet\\\\TimesheetService\\:\\:updateMultipleTimesheets\\(\\) expects array, iterable\\<object\\> given\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<object\\> given\\.$#"
count: 1
path: src/Controller/TimesheetAbstractController.php
-
message: "#^Parameter \\#2 \\$page of method App\\\\Controller\\\\TimesheetAbstractController\\:\\:getEditForm\\(\\) expects int, mixed given\\.$#"
count: 1
@@ -3620,11 +3505,6 @@ parameters:
count: 1
path: src/Form/TimesheetPreCreateForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3635,11 +3515,6 @@ parameters:
count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3665,26 +3540,11 @@ parameters:
count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3710,11 +3570,6 @@ parameters:
count: 2
path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3725,11 +3580,6 @@ parameters:
count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3755,26 +3605,11 @@ parameters:
count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3800,11 +3635,6 @@ parameters:
count: 2
path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3815,11 +3645,6 @@ parameters:
count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3845,26 +3670,11 @@ parameters:
count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3890,11 +3700,6 @@ parameters:
count: 2
path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3905,11 +3710,6 @@ parameters:
count: 1
path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3935,26 +3735,11 @@ parameters:
count: 1
path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3980,11 +3765,6 @@ parameters:
count: 2
path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -3995,11 +3775,6 @@ parameters:
count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4025,26 +3800,11 @@ parameters:
count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4070,11 +3830,6 @@ parameters:
count: 2
path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4085,11 +3840,6 @@ parameters:
count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4115,26 +3865,11 @@ parameters:
count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4160,11 +3895,6 @@ parameters:
count: 2
path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4175,11 +3905,6 @@ parameters:
count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4205,26 +3930,11 @@ parameters:
count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4250,11 +3960,6 @@ parameters:
count: 2
path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4265,11 +3970,6 @@ parameters:
count: 1
path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4295,26 +3995,11 @@ parameters:
count: 1
path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4340,11 +4025,6 @@ parameters:
count: 2
path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4355,11 +4035,6 @@ parameters:
count: 1
path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4385,26 +4060,11 @@ parameters:
count: 1
path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4430,11 +4090,6 @@ parameters:
count: 2
path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4445,11 +4100,6 @@ parameters:
count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4475,26 +4125,11 @@ parameters:
count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4520,11 +4155,6 @@ parameters:
count: 2
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4535,11 +4165,6 @@ parameters:
count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4565,26 +4190,11 @@ parameters:
count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4610,11 +4220,6 @@ parameters:
count: 2
path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addActivityChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/UserToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addActivityMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4625,11 +4230,6 @@ parameters:
count: 1
path: src/Form/Toolbar/UserToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addCustomerChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/UserToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addCustomerMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -4655,26 +4255,11 @@ parameters:
count: 1
path: src/Form/Toolbar/UserToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/UserToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addDateRangeChoice\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/UserToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/UserToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addProjectChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
path: src/Form/Toolbar/UserToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addProjectMultiChoice\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1
@@ -5320,11 +4905,6 @@ parameters:
count: 6
path: src/Invoice/ServiceInvoice.php
-
message: "#^Cannot call method getTotal\\(\\) on App\\\\Invoice\\\\CalculatorInterface\\|null\\.$#"
count: 1
path: src/Invoice/ServiceInvoice.php
-
message: "#^Cannot call method hasInvoiceTemplate\\(\\) on App\\\\Entity\\\\Customer\\|null\\.$#"
count: 1
@@ -6950,26 +6530,6 @@ parameters:
count: 2
path: src/Timesheet/TrackingMode/AbstractTrackingMode.php
-
message: "#^Cannot call method getTimezone\\(\\) on DateTime\\|null\\.$#"
count: 1
path: src/Timesheet/TrackingMode/DurationFixedBeginMode.php
-
message: "#^Cannot call method modify\\(\\) on DateTime\\|null\\.$#"
count: 1
path: src/Timesheet/TrackingMode/DurationFixedBeginMode.php
-
message: "#^Cannot clone DateTime\\|null\\.$#"
count: 1
path: src/Timesheet/TrackingMode/DurationFixedBeginMode.php
-
message: "#^Parameter \\#1 \\$begin of method App\\\\Entity\\\\Timesheet\\:\\:setBegin\\(\\) expects DateTime, DateTime\\|null given\\.$#"
count: 1
path: src/Timesheet/TrackingMode/DurationFixedBeginMode.php
-
message: "#^Method App\\\\Twig\\\\Configuration\\:\\:__call\\(\\) has no return type specified\\.$#"
count: 1
@@ -7964,3 +7524,18 @@ parameters:
message: "#^Property App\\\\Widget\\\\WidgetService\\:\\:\\$widgets type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Widget/WidgetService.php
-
message: "#^Property App\\\\Form\\\\MultiUpdate\\\\MultiUpdateTableDTO\\:\\:\\$entities with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection does not specify its types\\: TKey, T$#"
count: 1
path: src/Form/MultiUpdate/MultiUpdateTableDTO.php
-
message: "#^Method App\\\\Form\\\\MultiUpdate\\\\MultiUpdateTableDTO\\:\\:getEntities\\(\\) return type with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection does not specify its types\\: TKey, T$#"
count: 1
path: src/Form/MultiUpdate/MultiUpdateTableDTO.php
-
message: "#^Method App\\\\Form\\\\MultiUpdate\\\\MultiUpdateTableDTO\\:\\:setEntities\\(\\) has parameter \\$entities with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection but does not specify its types\\: TKey, T$#"
count: 1
path: src/Form/MultiUpdate/MultiUpdateTableDTO.php

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3,10 +3,10 @@
"app": {
"js": [
"/build/runtime.f0079159.js",
"/build/app.454ad090.js"
"/build/app.4ca3f16e.js"
],
"css": [
"/build/app.c429ff5b.css"
"/build/app.b10b16e5.css"
]
},
"export-pdf": {
@@ -63,8 +63,8 @@
},
"integrity": {
"/build/runtime.f0079159.js": "sha384-H22sAW1aTvyIPqvHOvGXWSWTxf0y6mptp+MsVmyXCfjx/WJjBbhX9gbUZ+qIuihV",
"/build/app.454ad090.js": "sha384-5EqTkHqGv38S0Z8pXK2h0tHtGoCxbBkAYeSg1S0pe5eTWItPP4fiIfgJkD6eVH9U",
"/build/app.c429ff5b.css": "sha384-m7RJ9g2fW3tENrf+oi/FsH8wTu6fsjuhvpDztfqr+YjNN0AvCapER6yvmGkqwp+I",
"/build/app.4ca3f16e.js": "sha384-4VmfUWQxO4OK+j7bPm7p+B8b/krMYs5T+JLrU6DPiTep/LNoMLq11DGP7QL0j7Pu",
"/build/app.b10b16e5.css": "sha384-dwEgmHAQRm4PP7XTYkJfIoB+Iumd+0WuH5FyoC/+qC3WQ/e1gCTmlx9M2mIBxPuK",
"/build/export-pdf.587575e7.js": "sha384-J50GStmmfVwUTN4dIRQ02eg9hyzGFPSzpTtpPody92j0V6zCqw+s5l8+ZhVTugeW",
"/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg",
"/build/invoice.c8ae95ad.js": "sha384-2eVY7MBiMQxo1vhfizU+fYfEZbz9bYUdzqxnpTQhxpYiLaxeSV4WnaObq2G7/Pks",

View File

@@ -1,6 +1,6 @@
{
"build/app.css": "/build/app.c429ff5b.css",
"build/app.js": "/build/app.454ad090.js",
"build/app.css": "/build/app.b10b16e5.css",
"build/app.js": "/build/app.4ca3f16e.js",
"build/export-pdf.css": "/build/export-pdf.d8a6c23b.css",
"build/export-pdf.js": "/build/export-pdf.587575e7.js",
"build/invoice.css": "/build/invoice.3c80ee80.css",

View File

@@ -15,6 +15,9 @@ use App\Entity\Project;
use App\Entity\Team;
use App\Entity\User;
use App\Form\API\TeamApiEditForm;
use App\Repository\ActivityRepository;
use App\Repository\CustomerRepository;
use App\Repository\ProjectRepository;
use App\Repository\TeamRepository;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\View\View;
@@ -230,15 +233,14 @@ final class TeamController extends BaseApiController
#[Rest\Post(path: '/{id}/customers/{customerId}', name: 'post_team_customer', requirements: ['id' => '\d+', 'customerId' => '\d+'])]
#[ApiSecurity(name: 'apiUser')]
#[ApiSecurity(name: 'apiToken')]
public function postCustomerAction(Team $team, #[MapEntity(mapping: ['customerId' => 'id'])] Customer $customer): Response
public function postCustomerAction(Team $team, #[MapEntity(mapping: ['customerId' => 'id'])] Customer $customer, CustomerRepository $customerRepository): Response
{
if ($team->hasCustomer($customer)) {
throw new BadRequestHttpException('Team has already access to customer');
}
$team->addCustomer($customer);
$this->repository->saveTeam($team);
$customerRepository->saveCustomer($customer);
$view = new View($team, Response::HTTP_OK);
$view->getContext()->setGroups(self::GROUPS_ENTITY);
@@ -256,15 +258,14 @@ final class TeamController extends BaseApiController
#[ApiSecurity(name: 'apiUser')]
#[ApiSecurity(name: 'apiToken')]
#[Rest\Delete(path: '/{id}/customers/{customerId}', name: 'delete_team_customer', requirements: ['id' => '\d+', 'customerId' => '\d+'])]
public function deleteCustomerAction(Team $team, #[MapEntity(mapping: ['customerId' => 'id'])] Customer $customer): Response
public function deleteCustomerAction(Team $team, #[MapEntity(mapping: ['customerId' => 'id'])] Customer $customer, CustomerRepository $customerRepository): Response
{
if (!$team->hasCustomer($customer)) {
throw new BadRequestHttpException('Customer is not assigned to the team');
}
$team->removeCustomer($customer);
$this->repository->saveTeam($team);
$customerRepository->saveCustomer($customer);
$view = new View($team, Response::HTTP_OK);
$view->getContext()->setGroups(self::GROUPS_ENTITY);
@@ -282,15 +283,14 @@ final class TeamController extends BaseApiController
#[Rest\Post(path: '/{id}/projects/{projectId}', name: 'post_team_project', requirements: ['id' => '\d+', 'projectId' => '\d+'])]
#[ApiSecurity(name: 'apiUser')]
#[ApiSecurity(name: 'apiToken')]
public function postProjectAction(Team $team, #[MapEntity(mapping: ['projectId' => 'id'])] Project $project): Response
public function postProjectAction(Team $team, #[MapEntity(mapping: ['projectId' => 'id'])] Project $project, ProjectRepository $projectRepository): Response
{
if ($team->hasProject($project)) {
throw new BadRequestHttpException('Team has already access to project');
}
$team->addProject($project);
$this->repository->saveTeam($team);
$projectRepository->saveProject($project);
$view = new View($team, Response::HTTP_OK);
$view->getContext()->setGroups(self::GROUPS_ENTITY);
@@ -308,15 +308,14 @@ final class TeamController extends BaseApiController
#[ApiSecurity(name: 'apiUser')]
#[ApiSecurity(name: 'apiToken')]
#[Rest\Delete(path: '/{id}/projects/{projectId}', name: 'delete_team_project', requirements: ['id' => '\d+', 'projectId' => '\d+'])]
public function deleteProjectAction(Team $team, #[MapEntity(mapping: ['projectId' => 'id'])] Project $project): Response
public function deleteProjectAction(Team $team, #[MapEntity(mapping: ['projectId' => 'id'])] Project $project, ProjectRepository $projectRepository): Response
{
if (!$team->hasProject($project)) {
throw new BadRequestHttpException('Project is not assigned to the team');
}
$team->removeProject($project);
$this->repository->saveTeam($team);
$projectRepository->saveProject($project);
$view = new View($team, Response::HTTP_OK);
$view->getContext()->setGroups(self::GROUPS_ENTITY);
@@ -334,15 +333,14 @@ final class TeamController extends BaseApiController
#[Rest\Post(path: '/{id}/activities/{activityId}', name: 'post_team_activity', requirements: ['id' => '\d+', 'activityId' => '\d+'])]
#[ApiSecurity(name: 'apiUser')]
#[ApiSecurity(name: 'apiToken')]
public function postActivityAction(Team $team, #[MapEntity(mapping: ['activityId' => 'id'])] Activity $activity): Response
public function postActivityAction(Team $team, #[MapEntity(mapping: ['activityId' => 'id'])] Activity $activity, ActivityRepository $activityRepository): Response
{
if ($team->hasActivity($activity)) {
throw new BadRequestHttpException('Team has already access to activity');
}
$team->addActivity($activity);
$this->repository->saveTeam($team);
$activityRepository->saveActivity($activity);
$view = new View($team, Response::HTTP_OK);
$view->getContext()->setGroups(self::GROUPS_ENTITY);
@@ -360,15 +358,14 @@ final class TeamController extends BaseApiController
#[ApiSecurity(name: 'apiUser')]
#[ApiSecurity(name: 'apiToken')]
#[Rest\Delete(path: '/{id}/activities/{activityId}', name: 'delete_team_activity', requirements: ['id' => '\d+', 'activityId' => '\d+'])]
public function deleteActivityAction(Team $team, #[MapEntity(mapping: ['activityId' => 'id'])] Activity $activity): Response
public function deleteActivityAction(Team $team, #[MapEntity(mapping: ['activityId' => 'id'])] Activity $activity, ActivityRepository $activityRepository): Response
{
if (!$team->hasActivity($activity)) {
throw new BadRequestHttpException('Activity is not assigned to the team');
}
$team->removeActivity($activity);
$this->repository->saveTeam($team);
$activityRepository->saveActivity($activity);
$view = new View($team, Response::HTTP_OK);
$view->getContext()->setGroups(self::GROUPS_ENTITY);

View File

@@ -17,11 +17,11 @@ class Constants
/**
* The current release version
*/
public const VERSION = '2.0.10';
public const VERSION = '2.0.11';
/**
* The current release: major * 10000 + minor * 100 + patch
*/
public const VERSION_ID = 20010;
public const VERSION_ID = 20011;
/**
* The software name
*/

View File

@@ -68,7 +68,7 @@ final class CalendarController extends AbstractController
$defaultStart = null;
if ($this->configuration->getTimesheetDefaultBeginTime() !== 'now') {
$defaultStart = $factory->createDateTime($this->configuration->getTimesheetDefaultBeginTime());
$defaultStart = $defaultStart->format('h:i:s');
$defaultStart = $defaultStart->format('H:i:s');
}
$config = $this->calendarService->getConfiguration();

View File

@@ -36,10 +36,17 @@ final class QuickEntryController extends AbstractController
public function quickEntry(Request $request, ?string $begin = null)
{
$factory = $this->getDateTimeFactory();
if ($begin !== null) {
try {
$begin = $factory->createDateTime($begin);
} catch (\Exception $ex) {
$begin = null;
}
}
if ($begin === null) {
$begin = $factory->createDateTime();
} else {
$begin = $factory->createDateTime($begin);
}
$startWeek = $factory->getStartOfWeek($begin);

View File

@@ -77,7 +77,7 @@ final class TagController extends AbstractController
#[Route(path: '/{id}/edit', name: 'tags_edit', methods: ['GET', 'POST'])]
#[IsGranted('manage_tag')]
public function editAction(Tag $tag, TagRepository $repository, Request $request)
public function editAction(Tag $tag, TagRepository $repository, Request $request): Response
{
$editForm = $this->createForm(TagEditForm::class, $tag, [
'action' => $this->generateUrl('tags_edit', ['id' => $tag->getId()]),
@@ -109,7 +109,7 @@ final class TagController extends AbstractController
#[Route(path: '/create', name: 'tags_create', methods: ['GET', 'POST'])]
#[IsGranted('manage_tag')]
public function createAction(TagRepository $repository, Request $request)
public function createAction(TagRepository $repository, Request $request): Response
{
$tag = new Tag();
@@ -143,7 +143,7 @@ final class TagController extends AbstractController
#[Route(path: '/multi-delete', name: 'tags_multi_delete', methods: ['POST'])]
#[IsGranted('delete_tag')]
public function multiDelete(TagRepository $repository, Request $request)
public function multiDelete(TagRepository $repository, Request $request): Response
{
$form = $this->getMultiUpdateForm($repository);
$form->handleRequest($request);

View File

@@ -10,14 +10,15 @@
namespace App\Controller;
use App\Entity\Team;
use App\Form\TeamCustomerForm;
use App\Form\TeamEditForm;
use App\Form\TeamProjectForm;
use App\Form\Toolbar\TeamToolbarForm;
use App\Form\Type\CustomerType;
use App\Form\Type\ProjectType;
use App\Repository\Query\TeamQuery;
use App\Repository\TeamRepository;
use App\Utils\DataTable;
use App\Utils\PageSetup;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -90,7 +91,7 @@ final class TeamController extends AbstractController
#[Route(path: '/{id}/duplicate', name: 'team_duplicate', methods: ['GET', 'POST'])]
#[IsGranted('create_team')]
#[IsGranted('edit', 'team')]
public function duplicateTeam(Team $team, Request $request)
public function duplicateTeam(Team $team, Request $request): Response
{
$newTeam = clone $team;
@@ -105,14 +106,14 @@ final class TeamController extends AbstractController
#[Route(path: '/{id}/edit', name: 'admin_team_edit', methods: ['GET', 'POST'])]
#[IsGranted('edit', 'team')]
public function editAction(Team $team, Request $request)
public function editAction(Team $team, Request $request): Response
{
return $this->renderEditScreen($team, $request);
}
#[Route(path: '/{id}/edit_member', name: 'admin_team_member', methods: ['GET', 'POST'])]
#[IsGranted('edit', 'team')]
public function editMemberAction(Team $team, Request $request)
public function editMemberAction(Team $team, Request $request): Response
{
$editForm = $this->createForm(TeamEditForm::class, $team, [
'action' => $this->generateUrl('admin_team_member', ['id' => $team->getId()]),
@@ -176,37 +177,21 @@ final class TeamController extends AbstractController
}
if (null !== $team->getId()) {
$customerForm = $this->createForm(TeamCustomerForm::class, $team, [
'method' => 'POST',
]);
$customerForm->handleRequest($request);
$customerForm = $this->createFormWithName('team_customer_form', FormType::class, $team)
->add('customers', CustomerType::class, [
'label' => false,
'multiple' => true,
'expanded' => true,
'query_builder_for_user' => false,
]);
if ($customerForm->isSubmitted() && $customerForm->isValid()) {
try {
$this->repository->saveTeam($team);
$this->flashSuccess('action.update.success');
return $this->redirectToRoute('admin_team_edit', ['id' => $team->getId()]);
} catch (\Exception $ex) {
$this->flashUpdateException($ex);
}
}
$projectForm = $this->createForm(TeamProjectForm::class, $team, [
'method' => 'POST',
]);
$projectForm->handleRequest($request);
if ($projectForm->isSubmitted() && $projectForm->isValid()) {
try {
$this->repository->saveTeam($team);
$this->flashSuccess('action.update.success');
return $this->redirectToRoute('admin_team_edit', ['id' => $team->getId()]);
} catch (\Exception $ex) {
$this->flashUpdateException($ex);
}
}
$projectForm = $this->createFormWithName('team_project_form', FormType::class, $team)
->add('projects', ProjectType::class, [
'label' => false,
'multiple' => true,
'expanded' => true,
'query_builder_for_user' => false,
]);
}
$page = new PageSetup('teams');

View File

@@ -280,15 +280,19 @@ abstract class TimesheetAbstractController extends AbstractController
]);
}
protected function multiUpdate(Request $request)
protected function multiUpdate(Request $request): Response
{
$dto = new TimesheetMultiUpdateDTO();
// initial request from the listing posts a different form
$form = $this->getMultiUpdateActionForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$dto->setEntities($form->getData()->getEntities());
$data = $form->getData();
if ($data instanceof MultiUpdateTableDTO) {
$dto->setEntities($data->getEntities());
}
}
// using a new timesheet to make sure we ONLY use meta-fields which are registered via events
@@ -321,14 +325,14 @@ abstract class TimesheetAbstractController extends AbstractController
$dto->setEntities($timesheets);
if (\count($dto->getEntities()) === 0) {
if (\count($timesheets) === 0) {
return $this->redirectToRoute($this->getTimesheetRoute());
}
if ($form->isSubmitted() && $form->isValid()) {
/** @var Timesheet $timesheet */
$execute = false;
foreach ($dto->getEntities() as $timesheet) {
/** @var Timesheet $timesheet */
foreach ($timesheets as $timesheet) {
if ($dto->isReplaceTags()) {
foreach ($timesheet->getTags() as $tag) {
$timesheet->removeTag($tag);
@@ -391,13 +395,17 @@ abstract class TimesheetAbstractController extends AbstractController
if ($execute) {
try {
$this->service->updateMultipleTimesheets($dto->getEntities());
$this->service->updateMultipleTimesheets($timesheets);
$this->flashSuccess('action.update.success');
return $this->redirectToRoute($this->getTimesheetRoute());
} catch (\Exception $ex) {
$this->flashUpdateException($ex);
}
} else {
$this->flashSuccess(sprintf('No changes for %s entries detected.', \count($timesheets)));
return $this->redirectToRoute($this->getTimesheetRoute());
}
}

View File

@@ -14,9 +14,9 @@ use Doctrine\Common\Collections\Collection;
class MultiUpdateTableDTO
{
/**
* @var iterable<object>|Collection<object>
* @var array<object>|Collection<object>
*/
private iterable|Collection $entities = [];
private array|Collection $entities = [];
/**
* @var string[]
*/
@@ -26,16 +26,16 @@ class MultiUpdateTableDTO
/**
* @return object[]
*/
public function getEntities(): iterable|Collection
public function getEntities(): array|Collection
{
return $this->entities;
}
/**
* @param iterable<object>|Collection<object> $entities
* @param array<object>|Collection<object> $entities
* @return MultiUpdateTableDTO
*/
public function setEntities(iterable|Collection $entities): MultiUpdateTableDTO
public function setEntities(array|Collection $entities): MultiUpdateTableDTO
{
$this->entities = $entities;

View File

@@ -195,7 +195,7 @@ final class TimesheetMultiUpdate extends AbstractType
// meta fields only if at least one exists
if ($entry !== null && $entry->getMetaFields()->count() > 0) {
$builder->add('metaFields', MetaFieldsCollectionType::class);
$builder->add('metaFields', MetaFieldsCollectionType::class, ['fields_required' => false]);
$choices = [];
foreach ($entry->getMetaFields() as $field) {

View File

@@ -1,45 +0,0 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Form;
use App\Entity\Team;
use App\Form\Type\CustomerType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class TeamCustomerForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('customers', CustomerType::class, [
'multiple' => true,
'expanded' => true,
'by_reference' => false,
'query_builder_for_user' => false,
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Team::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'admin_team_customer',
'attr' => [
'data-form-event' => 'kimai.teamUpdate'
],
]);
}
}

View File

@@ -1,46 +0,0 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Form;
use App\Entity\Team;
use App\Form\Type\ProjectType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class TeamProjectForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('projects', ProjectType::class, [
'multiple' => true,
'expanded' => false,
'by_reference' => false,
'attr' => ['size' => '20'],
'query_builder_for_user' => false,
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Team::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'admin_team_project',
'attr' => [
'data-form-event' => 'kimai.teamUpdate'
],
]);
}
}

View File

@@ -28,15 +28,15 @@ final class ExportToolbarForm extends AbstractType
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
$this->addCustomerMultiChoice($builder, ['start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true], true);
$this->addProjectMultiChoice($builder, ['ignore_date' => true], true, true);
$this->addActivityMultiChoice($builder, [], true);
$this->addActivitySelect($builder, [], true, true, false);
$this->addTagInputField($builder);
if ($options['include_user']) {
$this->addUsersChoice($builder);
$this->addTeamsChoice($builder);
}
$this->addExportStateChoice($builder);
$this->addTimesheetStateChoice($builder);
$this->addBillableChoice($builder);
$this->addExportStateChoice($builder);
$builder->add('renderer', HiddenType::class, []);
if ($options['include_export']) {
$builder->add('markAsExported', HiddenType::class, [

View File

@@ -25,21 +25,21 @@ final class InvoiceToolbarForm extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$this->addTemplateChoice($builder);
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
$this->addCustomerMultiChoice($builder, ['required' => false, 'start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true, 'placeholder' => ''], true);
$this->addProjectMultiChoice($builder, ['ignore_date' => true], false, true);
$this->addSearchTermInputField($builder);
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
$this->addCustomerMultiChoice($builder, ['start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true], true);
$this->addProjectMultiChoice($builder, ['ignore_date' => true], true, true);
$this->addActivitySelect($builder, [], true, true, false);
$this->addTagInputField($builder);
if ($options['include_user']) {
$this->addUsersChoice($builder);
$this->addTeamsChoice($builder);
}
$this->addActivityMultiChoice($builder, $options, true);
$this->addTagInputField($builder);
$this->addExportStateChoice($builder);
$builder->add('invoiceDate', DatePickerType::class, [
'required' => true,
]);
$this->addTemplateChoice($builder);
}
protected function addTemplateChoice(FormBuilderInterface $builder): void

View File

@@ -45,14 +45,6 @@ use Symfony\Component\Form\FormEvents;
*/
trait ToolbarFormTrait
{
protected function addUserChoice(FormBuilderInterface $builder): void
{
$builder->add('user', UserType::class, [
'label' => 'user',
'required' => false,
]);
}
protected function addUsersChoice(FormBuilderInterface $builder, string $field = 'users', array $options = []): void
{
$builder->add($field, UserType::class, array_merge([
@@ -67,14 +59,6 @@ trait ToolbarFormTrait
], $options));
}
protected function addTeamChoice(FormBuilderInterface $builder): void
{
$builder->add('team', TeamType::class, [
'label' => 'team',
'required' => false,
]);
}
protected function addTeamsChoice(FormBuilderInterface $builder, string $field = 'teams', array $options = []): void
{
$builder->add($field, TeamType::class, array_merge([
@@ -89,11 +73,6 @@ trait ToolbarFormTrait
], $options));
}
protected function addCustomerChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false): void
{
$this->addCustomerSelect($builder, $options, false, $multiProject);
}
protected function addCustomerMultiChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false): void
{
$this->addCustomerSelect($builder, $options, true, $multiProject);
@@ -190,16 +169,6 @@ trait ToolbarFormTrait
$builder->add('daterange', DateRangeType::class, $params);
}
protected function addDateRangeChoice(FormBuilderInterface $builder, $allowEmpty = true, $required = false): void
{
$this->addDateRange($builder, [], $allowEmpty, $required);
}
protected function addProjectChoice(FormBuilderInterface $builder, array $options = [], bool $multiCustomer = false, bool $multiActivity = false): void
{
$this->addProjectSelect($builder, $options, false, $multiCustomer, $multiActivity);
}
protected function addProjectMultiChoice(FormBuilderInterface $builder, array $options = [], bool $multiCustomer = false, bool $multiActivity = false): void
{
$this->addProjectSelect($builder, $options, true, $multiCustomer, $multiActivity);
@@ -271,46 +240,45 @@ trait ToolbarFormTrait
);
}
protected function addActivityChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false): void
{
$this->addActivitySelect($builder, $options, false, $multiProject);
}
protected function addActivityMultiChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false): void
{
$this->addActivitySelect($builder, $options, true, $multiProject);
}
private function addActivitySelect(FormBuilderInterface $builder, array $options = [], bool $multiActivity = false, bool $multiProject = false): void
private function addActivitySelect(FormBuilderInterface $builder, array $options = [], bool $multiActivity = false, bool $multiProject = false, bool $autoFill = true): void
{
$name = 'activity';
if ($multiActivity) {
$name = 'activities';
}
$name = $multiActivity ? 'activities' : 'activity';
// just a fake field for having this field at the right position in the frontend
$builder->add($name, ActivityType::class, [
$activityOptions = [
'required' => false,
'documentation' => [
'type' => 'array',
'items' => ['type' => 'integer', 'description' => 'Activity ID'],
'description' => 'Array of activity IDs',
],
'choices' => [],
'multiple' => $multiActivity,
]);
];
if (!$autoFill) {
$activityOptions['attr'] = [
'data-autoselect' => 'false'
];
}
// just a fake field for having this field at the right position in the frontend
$builder->add($name, ActivityType::class, array_merge($activityOptions, [
'choices' => [],
]));
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($name, $multiActivity, $multiProject) {
function (FormEvent $event) use ($name, $multiProject, $activityOptions) {
/** @var array<string, mixed> $data */
$data = $event->getData();
$event->getForm()->add($name, ActivityType::class, [
'multiple' => $multiActivity,
'required' => false,
'query_builder' => function (ActivityRepository $repo) use ($data, $multiActivity, $multiProject) {
$event->getForm()->add($name, ActivityType::class, array_merge($activityOptions, [
'query_builder' => function (ActivityRepository $repo) use ($name, $data, $multiProject) {
$query = new ActivityFormTypeQuery();
$name = $multiActivity ? 'activities' : 'activity';
if (\array_key_exists($name, $data) && $data[$name] !== null && $data[$name] !== '') {
// we need to pre-fetch the activities to see if they are global, see ActivityFormTypeQuery::isGlobalsOnly()
$activities = \is_array($data[$name]) ? $data[$name] : [$data[$name]];
@@ -323,9 +291,9 @@ trait ToolbarFormTrait
}
}
$name = $multiProject ? 'projects' : 'project';
if (\array_key_exists($name, $data) && $data[$name] !== null && $data[$name] !== '') {
$projects = \is_array($data[$name]) ? $data[$name] : [$data[$name]];
$projectName = $multiProject ? 'projects' : 'project';
if (\array_key_exists($projectName, $data) && $data[$projectName] !== null && $data[$projectName] !== '') {
$projects = \is_array($data[$projectName]) ? $data[$projectName] : [$data[$projectName]];
foreach ($projects as $project) {
$project = \is_string($project) ? (int) $project : $project;
if (!\is_int($project) && !($project instanceof Project)) {
@@ -337,7 +305,7 @@ trait ToolbarFormTrait
return $repo->getQueryBuilderForFormType($query);
},
]);
]));
}
);
}

View File

@@ -62,6 +62,7 @@ final class DateRangeType extends AbstractType
$factory = DateTimeFactory::createByUser($user);
$view->vars['ranges'] = [
'today' => [$factory->createDateTime('00:00:00'), $factory->createDateTime('23:59:59')],
'yesterday' => [$factory->createDateTime('-1 day 00:00:00'), $factory->createDateTime('-1 day 23:59:59')],
'thisWeek' => [$factory->getStartOfWeek(), $factory->getEndOfWeek()],
'lastWeek' => [$factory->getStartOfWeek('-1 week'), $factory->getEndOfWeek('-1 week')],

View File

@@ -27,7 +27,7 @@ final class MetaFieldsCollectionType extends AbstractType
{
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) {
function (FormEvent $event) use ($options) {
/** @var ArrayCollection<MetaTableTypeInterface> $collection */
$collection = $event->getData();
foreach ($collection as $collectionItem) {
@@ -42,6 +42,11 @@ final class MetaFieldsCollectionType extends AbstractType
continue;
}
if ($options['fields_required'] !== null) {
// TODO required select-fields can receive an empty value
$collectionItem->setIsRequired((bool) $options['fields_required']);
}
$collection->set($collectionItem->getName(), $collectionItem);
}
},
@@ -57,8 +62,11 @@ final class MetaFieldsCollectionType extends AbstractType
'entry_options' => ['label' => false],
'allow_add' => false,
'allow_delete' => false,
'fields_required' => null,
'label' => false,
]);
$resolver->setAllowedTypes('fields_required', ['null', 'bool']);
}
public function getParent(): string

View File

@@ -532,10 +532,6 @@ final class ServiceInvoice
$model->addEntries($settings['entries']);
$this->prepareModelQueryDates($model);
if ($model->getCalculator()->getTotal() < 0.0) {
continue;
}
$models[] = $model;
}

View File

@@ -48,11 +48,12 @@ final class DurationFixedBeginMode implements TrackingModeInterface
$timesheet->setBegin(new DateTime('now', $this->getTimezone($timesheet)));
}
$newBegin = clone $timesheet->getBegin();
/** @var DateTime $newBegin */
$newBegin = clone $timesheet->getBegin(); // @phpstan-ignore-line
// this prevents the problem that "now" is being ignored in modify()
$beginTime = (new DateTime($this->configuration->getTimesheetDefaultBeginTime(), $newBegin->getTimezone()))->format('H:i:s');
$newBegin->modify($beginTime);
$beginTime = new DateTime($this->configuration->getTimesheetDefaultBeginTime(), $newBegin->getTimezone());
$newBegin->setTime((int) $beginTime->format('H'), (int) $beginTime->format('i'), 0, 0);
$timesheet->setBegin($newBegin);
}

View File

@@ -18,13 +18,13 @@
<th>
{{ 'user'|trans }}
</th>
<th>
<th class="text-end">
{{ 'hourlyRate'|trans }}
</th>
<th class="d-none d-sm-table-cell">
<th class="d-none d-sm-table-cell text-end">
{{ 'internalRate'|trans }}
</th>
<th>
<th class="text-center">
{{ 'fixedRate'|trans }}
</th>
<th class="actions"></th>
@@ -40,13 +40,17 @@
&ndash;
{% endif %}
</td>
<td>
<td class="text-end">
{{ rate.rate|money(currency) }}
</td>
<td class="d-none d-sm-table-cell">
{{ rate.internalRate|money(currency) }}
<td class="d-none d-sm-table-cell text-end">
{% if rate.internalRate is not null %}
{{ rate.internalRate|money(currency) }}
{% else %}
&ndash;
{% endif %}
</td>
<td>
<td class="text-center">
{{ widgets.label_boolean(rate.fixed) }}
</td>
<td class="actions">

View File

@@ -1,3 +1,35 @@
{# added for the team project form, to show divider for the customer name between all projects #}
{% block choice_widget_expanded -%}
{%- if '-inline' in label_attr.class|default('') -%}
{%- for child in form %}
{{- form_widget(child, {
parent_label_class: label_attr.class|default(''),
translation_domain: choice_translation_domain,
}) -}}
{% endfor -%}
{%- else -%}
<div {{ block('widget_container_attributes') }}>
{% if form.vars.choices|length != form.children|length %}
{% for name, choices in form.vars.choices %}
<fieldset class="form-fieldset">
<legend>{{ name }}</legend>
{% for key, choice in choices %}
{{- form_widget(form[key]) -}}
{{- form_label(form[key]) -}}
{% endfor %}
</fieldset>
{% endfor %}
{% else %}
{%- for child in form %}
{{- form_widget(child, {
parent_label_class: label_attr.class|default(''),
translation_domain: choice_translation_domain,
}) -}}
{% endfor -%}
{% endif %}
</div>
{%- endif -%}
{%- endblock choice_widget_expanded %}
{% block quick_entry_week_row %}
<tr{% with {attr: row_attr|merge({class: (row_attr.class|default('') ~ ' form-group qe-entry-week-row' ~ (not valid ? ' is-invalid'))|trim})} %}{{ block('attributes') }}{% endwith %}>

View File

@@ -1,21 +0,0 @@
<!DOCTYPE html>
{% set fallback = app.request is not null ? app.request.locale : 'en' %}
{% set language = model.template.language|default(fallback) %}
<html lang="{{ language }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<title>{% block title %}{{ model.invoiceNumber }}-{{ model.customer.company|default(model.customer.name)|u.snake }}{% endblock %}</title>
<style type="text/css">
{{ encore_entry_css_source('invoice')|raw }}
</style>
</head>
<body class="invoice_print">
<div class="wrapper">
<section class="invoice">
{% block invoice %}{% endblock %}
</section>
</div>
</body>
</html>

View File

@@ -1,148 +1,166 @@
{% extends 'invoice/layout.html.twig' %}
{% block invoice %}
<!DOCTYPE html>
{% set fallback = app.request is not null ? app.request.locale : 'en' %}
{% set language = model.template.language|default(fallback) %}
{% set isDecimal = true %}
{% set currency = model.currency %}
{% set project = model.query.project %}
<div class="row">
<div class="col-xs-12">
<h2 class="page-header">
<span contenteditable="true">{{ model.template.title }}</span>
<small class="pull-right">{{ 'date'|trans }}: {{ model.invoiceDate|date_short }}</small>
</h2>
</div>
</div>
<html lang="{{ language }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<title>{% block title %}{{ model.invoiceNumber }}-{{ model.customer.company|default(model.customer.name)|u.snake }}{% endblock %}</title>
<style type="text/css">
{{ encore_entry_css_source('invoice')|raw }}
</style>
</head>
<body class="invoice_print">
<div class="wrapper">
<section class="invoice">
<div class="row">
<div class="col-sm-5">
{{ 'invoice.to'|trans }}
<address contenteditable="true">
<strong>{{ model.customer.company|default(model.customer.name) }}</strong><br>
{{ model.customer.address|nl2br }}
{% if model.customer.vatId is not empty %}
<br>
{{ 'vat_id'|trans }}: {{ model.customer.vatId }}
{% endif %}
{% if model.customer.number is not empty %}
<br>
{{ 'number'|trans }}: {{ model.customer.number }}
{% endif %}
{% if project is not null and project.orderNumber is not empty %}
<br>
{{ 'orderNumber'|trans }}: {{ project.orderNumber }}
{% endif %}
</address>
</div>
<div class="col-sm-2"></div>
<div class="col-sm-5">
{{ 'invoice.from'|trans }}
<address contenteditable="true">
<strong>{{ model.template.company }}</strong><br>
{{ model.template.address|trim|nl2br }}
{% if model.template.vatId is not empty %}
<br>
{{ 'vat_id'|trans }}:
{{ model.template.vatId }}
{% endif %}
</address>
</div>
</div>
<div class="row">
<div class="col-sm-5">
<p contenteditable="true">
<strong>{{ 'invoice.number'|trans }}:</strong>
{{ model.invoiceNumber }}
<br>
<strong>{{ 'invoice.due_days'|trans }}:</strong>
{{ model.dueDate|date_short }}
</p>
</div>
<div class="col-sm-7"></div>
</div>
<div class="row invoice-items mt-2 mb-3">
<div class="col-xs-12 table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ 'date'|trans }}</th>
<th>{{ 'description'|trans }}</th>
<th class="text-end">{{ 'unit_price'|trans }}</th>
<th class="text-end">{{ 'amount'|trans }}</th>
<th class="text-end">{{ 'total_rate'|trans }}</th>
</tr>
</thead>
<tbody>
{% for entry in model.calculator.entries %}
{% set duration = entry.duration|duration(isDecimal) %}
{% if entry.fixedRate %}
{% set rate = entry.fixedRate %}
{% set duration = entry.amount|amount %}
{% else %}
{% set rate = entry.hourlyRate %}
{% endif %}
<tr>
<td nowrap class="text-nowrap">{{ entry.begin|date_short }}</td>
<td contenteditable="true">
{% if entry.description is not empty %}
{{ entry.description|nl2br }}
{% else %}
{% if entry.activity is not null %}{{ entry.activity.name }} / {% endif %}{{ entry.project.name }}
{% endif %}
</td>
<td nowrap class="text-nowrap text-end">{{ rate|money(currency) }}</td>
<td nowrap class="text-nowrap text-end">{{ duration }}</td>
<td nowrap class="text-nowrap text-end">{{ entry.rate|money(currency) }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
{% if model.calculator.tax > 0 or not model.isHideZeroTax() %}
<tr>
<td colspan="4" class="text-end">
{{ 'invoice.subtotal'|trans }}
</td>
<td class="text-end">{{ model.calculator.subtotal|money(currency) }}</td>
</tr>
<tr>
<td colspan="4" class="text-end">
{{ 'invoice.tax'|trans }} ({{ model.calculator.vat }}%)
</td>
<td class="text-end">{{ model.calculator.tax|money(currency) }}</td>
</tr>
{% endif %}
<tr>
<td colspan="4" class="text-end text-nowrap">
<strong>{{ 'invoice.total'|trans }}</strong>
</td>
<td class="text-end">
<strong>{{ model.calculator.total|money(currency) }}</strong>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
<div class="row">
<div class="col-xs-12">
{% if model.template.paymentTerms is not empty %}
<div contenteditable="true" class="paymentTerms">
{{ model.template.paymentTerms|md2html }}
<div class="row">
<div class="col-xs-12">
<h2 class="page-header">
<span contenteditable="true">{{ model.template.title }}</span>
<small class="pull-right">{{ 'date'|trans }}: {{ model.invoiceDate|date_short }}</small>
</h2>
</div>
</div>
{% endif %}
</div>
</div>
<footer class="footer">
<p>
<strong>{{ 'address'|trans }}</strong>: {{ model.template.company }} &ndash; {{ model.template.address|nl2str(' ') }}
<br>
<strong>{{ 'invoice_bank_account'|trans }}</strong>: {{ model.template.paymentDetails|nl2str(' ') }}
<br>
<strong>{{ 'contact'|trans }}</strong>: {{ model.template.contact|nl2str(' ') }}
</p>
</footer>
{% endblock %}
<div class="row">
<div class="col-sm-5">
{{ 'invoice.to'|trans }}
<address contenteditable="true">
<strong>{{ model.customer.company|default(model.customer.name) }}</strong><br>
{{ model.customer.address|nl2br }}
{% if model.customer.vatId is not empty %}
<br>
{{ 'vat_id'|trans }}: {{ model.customer.vatId }}
{% endif %}
{% if model.customer.number is not empty %}
<br>
{{ 'number'|trans }}: {{ model.customer.number }}
{% endif %}
{% if project is not null and project.orderNumber is not empty %}
<br>
{{ 'orderNumber'|trans }}: {{ project.orderNumber }}
{% endif %}
</address>
</div>
<div class="col-sm-2"></div>
<div class="col-sm-5">
{{ 'invoice.from'|trans }}
<address contenteditable="true">
<strong>{{ model.template.company }}</strong><br>
{{ model.template.address|trim|nl2br }}
{% if model.template.vatId is not empty %}
<br>
{{ 'vat_id'|trans }}:
{{ model.template.vatId }}
{% endif %}
</address>
</div>
</div>
<div class="row">
<div class="col-sm-5">
<p contenteditable="true">
<strong>{{ 'invoice.number'|trans }}:</strong>
{{ model.invoiceNumber }}
<br>
<strong>{{ 'invoice.due_days'|trans }}:</strong>
{{ model.dueDate|date_short }}
</p>
</div>
<div class="col-sm-7"></div>
</div>
<div class="row invoice-items mt-2 mb-3">
<div class="col-xs-12 table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ 'date'|trans }}</th>
<th>{{ 'description'|trans }}</th>
<th class="text-end">{{ 'unit_price'|trans }}</th>
<th class="text-end">{{ 'amount'|trans }}</th>
<th class="text-end">{{ 'total_rate'|trans }}</th>
</tr>
</thead>
<tbody>
{% for entry in model.calculator.entries %}
{% set duration = entry.duration|duration(isDecimal) %}
{% if entry.fixedRate %}
{% set rate = entry.fixedRate %}
{% set duration = entry.amount|amount %}
{% else %}
{% set rate = entry.hourlyRate %}
{% endif %}
<tr>
<td nowrap class="text-nowrap">{{ entry.begin|date_short }}</td>
<td contenteditable="true">
{% if entry.description is not empty %}
{{ entry.description|nl2br }}
{% else %}
{% if entry.activity is not null %}{{ entry.activity.name }} / {% endif %}{{ entry.project.name }}
{% endif %}
</td>
<td nowrap class="text-nowrap text-end">{{ rate|money(currency) }}</td>
<td nowrap class="text-nowrap text-end">{{ duration }}</td>
<td nowrap class="text-nowrap text-end">{{ entry.rate|money(currency) }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
{% if model.calculator.tax > 0 or not model.isHideZeroTax() %}
<tr>
<td colspan="4" class="text-end">
{{ 'invoice.subtotal'|trans }}
</td>
<td class="text-end">{{ model.calculator.subtotal|money(currency) }}</td>
</tr>
<tr>
<td colspan="4" class="text-end">
{{ 'invoice.tax'|trans }} ({{ model.calculator.vat }}%)
</td>
<td class="text-end">{{ model.calculator.tax|money(currency) }}</td>
</tr>
{% endif %}
<tr>
<td colspan="4" class="text-end text-nowrap">
<strong>{{ 'invoice.total'|trans }}</strong>
</td>
<td class="text-end">
<strong>{{ model.calculator.total|money(currency) }}</strong>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
<div class="row">
<div class="col-xs-12">
{% if model.template.paymentTerms is not empty %}
<div contenteditable="true" class="paymentTerms">
{{ model.template.paymentTerms|md2html }}
</div>
{% endif %}
</div>
</div>
<footer class="footer">
<p>
<strong>{{ 'address'|trans }}</strong>: {{ model.template.company }} &ndash; {{ model.template.address|nl2str(' ') }}
<br>
<strong>{{ 'invoice_bank_account'|trans }}</strong>: {{ model.template.paymentDetails|nl2str(' ') }}
<br>
<strong>{{ 'contact'|trans }}</strong>: {{ model.template.contact|nl2str(' ') }}
</p>
</footer>
</section>
</div>
</body>
</html>

View File

@@ -7,21 +7,93 @@
{% include 'team/member-form.html.twig' %}
</div>
<div class="col-md-6">
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
{% if customerForm is defined and customerForm is not null %}
{{ include(formEditTemplate, {
'title': 'teams.customer_access'|trans({}, 'teams'),
'form': customerForm,
}) }}
{% embed '@theme/embeds/card.html.twig' with {collapsible: true, collapsed: (team.customers|length == 0)} %}
{% from "macros/status.html.twig" import status_count %}
{% block box_title %}
{{ 'teams.customer_access'|trans({}, 'teams') }}
{{ status_count(team.customers|length) }}
{% endblock %}
{% block box_body %}
{{ form(customerForm) }}
{% endblock %}
{% endembed %}
{% endif %}
{% if projectForm is defined and projectForm is not null %}
{{ include(formEditTemplate, {
'title': 'teams.project_access'|trans({}, 'teams'),
'form': projectForm,
}) }}
{% embed '@theme/embeds/card.html.twig' with {collapsible: true, collapsed: (team.projects|length == 0)} %}
{% from "macros/status.html.twig" import status_count %}
{% block box_title %}
{{ 'teams.project_access'|trans({}, 'teams') }}
{{ status_count(team.projects|length) }}
{% endblock %}
{% block box_body %}
{{ form(projectForm) }}
{% endblock %}
{% endembed %}
{% endif %}
</div>
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script type="text/javascript">
document.addEventListener('kimai.initialized', (event) => {
/** @type {KimaiAPI} API */
const API = event.detail.kimai.getPlugin('api');
/** @type {KimaiAlert} ALERT */
const ALERT = event.detail.kimai.getPlugin('alert');
{% if customerForm is defined and customerForm is not null %}
document.getElementById('team_customer_form').addEventListener('change',
(event) => {
if (event.target.matches('input[type=checkbox]')) {
const checkbox = event.target;
const customerId = checkbox.value;
if (checkbox.checked) {
API.post(
'{{ path('post_team_customer', {'id': team.id, 'customerId': '000'}) }}'.replace(/000/, customerId),
{},
(json) => {ALERT.success('action.update.success');},
(error) => {ALERT.error('action.update.error', error);}, // TODO test me
);
} else {
API.delete(
'{{ path('delete_team_customer', {'id': team.id, 'customerId': '000'}) }}'.replace(/000/, customerId),
(json) => {ALERT.success('action.delete.success');},
(error) => {ALERT.error('action.delete.error', error);}, // TODO test me
);
}
}
}
);
{% endif %}
{% if projectForm is defined and customerForm is not null %}
document.getElementById('team_project_form').addEventListener('change',
(event) => {
if (event.target.matches('input[type=checkbox]')) {
const checkbox = event.target;
const projectId = checkbox.value;
if (checkbox.checked) {
API.post(
'{{ path('post_team_project', {'id': team.id, 'projectId': '000'}) }}'.replace(/000/, projectId),
{},
(json) => {ALERT.success('action.update.success');},
(error) => {ALERT.error('action.update.error', error);}, // TODO test me
);
} else {
API.delete(
'{{ path('delete_team_project', {'id': team.id, 'projectId': '000'}) }}'.replace(/000/, projectId),
(json) => {ALERT.success('action.delete.success');},
(error) => {ALERT.error('action.delete.error', error);}, // TODO test me
);
}
}
}
);
{% endif %}
});
</script>
{% endblock %}

View File

@@ -17,8 +17,16 @@
{% endif %}
{% if form.end_time is defined or form.duration is defined %}
<div class="mb-3 row">
<label class="col-form-label col-sm-2" for="{{ form.vars.name }}_duration">{{ 'duration'|trans }} / {{ 'endtime'|trans }}</label>
{% set size = form.duration is defined ? 5 : 10 %}
<label class="col-form-label col-sm-2" for="{{ form.vars.name }}_duration">
{% if form.duration is defined %}
{{ 'duration'|trans }}
{% if form.end_time is defined %} / {% endif %}
{% endif %}
{% if form.end_time is defined %}
{{ 'endtime'|trans }}
{% endif %}
</label>
{% set size = form.duration is defined and form.end_time is defined ? 5 : 10 %}
{% if form.duration is defined %}
<div class="col-sm-{{ size }}">
{{ form_widget(form.duration, {'label': false}) }}

View File

@@ -9,7 +9,7 @@
} %}
{% embed formEditTemplate with formOptions %}
{% block form_body %}
<fieldset>
<fieldset class="form-fieldset">
<div class="row">
<div class="col-md-4">
{{ form_row(form.customer) }}
@@ -22,27 +22,27 @@
</div>
</div>
</fieldset>
<fieldset>
<fieldset class="form-fieldset">
{{ form_row(form.replaceTags) }}
{{ form_row(form.tags) }}
</fieldset>
{% if form.user is defined %}
<fieldset>
<fieldset class="form-fieldset">
{{ form_row(form.user) }}
</fieldset>
{% endif %}
{% if form.exported is defined %}
<fieldset>
<fieldset class="form-fieldset">
{{ form_row(form.exported) }}
</fieldset>
{% endif %}
{% if form.billable is defined %}
<fieldset>
<fieldset class="form-fieldset">
{{ form_row(form.billable) }}
</fieldset>
{% endif %}
{% if form.recalculateRates is defined %}
<fieldset>
<fieldset class="form-fieldset">
{{ form_row(form.recalculateRates) }}
<div class="row">
<div class="col-md-6">
@@ -55,7 +55,7 @@
</fieldset>
{% endif %}
{% if form.metaFields is defined %}
<fieldset>
<fieldset class="form-fieldset">
<div class="row">
<div class="col-md-6">
{{ form_row(form.updateMeta) }}
@@ -97,6 +97,9 @@
if (elAttr.indexOf('{{ form.metaFields.vars.full_name }}') !== -1) {
{# disable all custom fields, each one has to be activated before it can be used #}
el.setAttribute('disabled', 'disabled');
if (el.tomselect !== undefined) {
el.tomselect.disable();
}
} else if (elAttr.indexOf('{{ form.updateMeta.vars.full_name }}') !== -1) {
el.checked = false;
el.onclick = function() {
@@ -117,8 +120,14 @@
}
if (metaCheckbox.checked) {
current.removeAttribute('disabled');
if (current.tomselect !== undefined) {
current.tomselect.enable();
}
} else {
current.setAttribute('disabled', 'disabled');
if (current.tomselect !== undefined) {
current.tomselect.disable();
}
}
}
};

View File

@@ -12,6 +12,7 @@ namespace App\Tests\API;
use App\Entity\Team;
use App\Entity\User;
use App\Tests\DataFixtures\TeamFixtures;
use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpFoundation\Response;
/**
@@ -30,12 +31,15 @@ class TeamControllerTest extends APIControllerBaseTest
return $this->importFixture($fixture);
}
public function testIsSecure()
public function testIsSecure(): void
{
$this->assertUrlIsSecured('/api/teams');
}
public function getRoleTestData()
/**
* @return array<int, array<int, string>>
*/
public function getRoleTestData(): array
{
return [
[User::ROLE_USER],
@@ -46,12 +50,12 @@ class TeamControllerTest extends APIControllerBaseTest
/**
* @dataProvider getRoleTestData
*/
public function testIsSecureForRole(string $role)
public function testIsSecureForRole(string $role): void
{
$this->assertUrlIsSecuredForRole($role, '/api/teams');
}
public function testGetCollection()
public function testGetCollection(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->importTeamFixtures();
@@ -64,7 +68,7 @@ class TeamControllerTest extends APIControllerBaseTest
self::assertApiResponseTypeStructure('TeamCollection', $result[0]);
}
public function testGetEntity()
public function testGetEntity(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$teams = $this->importTeamFixtures();
@@ -77,18 +81,18 @@ class TeamControllerTest extends APIControllerBaseTest
self::assertApiResponseTypeStructure('TeamEntity', $result);
}
public function testNotFound()
public function testNotFound(): void
{
$this->assertEntityNotFound(User::ROLE_USER, '/api/teams/' . PHP_INT_MAX, 'GET', 'App\\Entity\\Team object not found by the @ParamConverter annotation.');
}
public function testDeleteActionWithUnknownTeam()
public function testDeleteActionWithUnknownTeam(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertNotFoundForDelete($client, '/api/teams/' . PHP_INT_MAX);
}
public function testPostAction()
public function testPostAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -106,7 +110,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertNotEmpty($result['id']);
}
public function testPostActionWithInvalidUser()
public function testPostActionWithInvalidUser(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
$data = [
@@ -118,7 +122,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertApiResponseAccessDenied($response, 'Access denied.');
}
public function testPostActionWithValidationErrors()
public function testPostActionWithValidationErrors(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -134,7 +138,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertApiCallValidationError($response, ['name', 'members.0.user']);
}
public function testPatchAction()
public function testPatchAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
@@ -188,7 +192,7 @@ class TeamControllerTest extends APIControllerBaseTest
self::assertEquals('john_user', $result['members'][0]['user']['username']);
}
public function testPatchActionWithValidationErrors()
public function testPatchActionWithValidationErrors(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -214,7 +218,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertApiCallValidationError($response, ['name', 'members.0.user']);
}
public function testDeleteAction()
public function testDeleteAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$teams = $this->importTeamFixtures();
@@ -233,7 +237,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertEmpty($client->getResponse()->getContent());
}
public function testPostMemberAction()
public function testPostMemberAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -256,7 +260,7 @@ class TeamControllerTest extends APIControllerBaseTest
self::assertCount(2, $result['members']);
}
public function testPostMemberActionErrors()
public function testPostMemberActionErrors(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -284,7 +288,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/members/5', 'POST');
}
public function testDeleteMemberAction()
public function testDeleteMemberAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -310,7 +314,7 @@ class TeamControllerTest extends APIControllerBaseTest
self::assertCount(3, $result['members']);
}
public function testDeleteMemberActionErrors()
public function testDeleteMemberActionErrors(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -343,7 +347,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/members/1', 'DELETE');
}
public function testPostCustomerAction()
public function testPostCustomerAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -367,7 +371,7 @@ class TeamControllerTest extends APIControllerBaseTest
self::assertEquals(1, $result['customers'][0]['id']);
}
public function testPostCustomerActionErrors()
public function testPostCustomerActionErrors(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -397,7 +401,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
}
public function testDeleteCustomerAction()
public function testDeleteCustomerAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -427,9 +431,15 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertIsArray($result);
self::assertApiResponseTypeStructure('TeamEntity', $result);
self::assertCount(0, $result['customers']);
/** @var EntityManager $em */
$em = $this->getEntityManager();
$team = $em->getRepository(Team::class)->find($result['id']);
self::assertInstanceOf(Team::class, $team);
self::assertCount(0, $team->getCustomers());
}
public function testDeleteCustomerActionErrors()
public function testDeleteCustomerActionErrors(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -455,7 +465,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/customers/1', 'DELETE');
}
public function testPostProjectAction()
public function testPostProjectAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -478,7 +488,7 @@ class TeamControllerTest extends APIControllerBaseTest
self::assertEquals(1, $result['projects'][0]['id']);
}
public function testPostProjectActionErrors()
public function testPostProjectActionErrors(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -510,7 +520,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
}
public function testDeleteProjectAction()
public function testDeleteProjectAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -540,9 +550,15 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertIsArray($result);
self::assertApiResponseTypeStructure('TeamEntity', $result);
self::assertCount(0, $result['projects']);
/** @var EntityManager $em */
$em = $this->getEntityManager();
$team = $em->getRepository(Team::class)->find($result['id']);
self::assertInstanceOf(Team::class, $team);
self::assertCount(0, $team->getProjects());
}
public function testDeleteProjectActionErrors()
public function testDeleteProjectActionErrors(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -568,7 +584,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/projects/1', 'DELETE');
}
public function testPostActivityAction()
public function testPostActivityAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -591,7 +607,7 @@ class TeamControllerTest extends APIControllerBaseTest
self::assertEquals(1, $result['activities'][0]['id']);
}
public function testPostActivityActionErrors()
public function testPostActivityActionErrors(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -621,7 +637,7 @@ class TeamControllerTest extends APIControllerBaseTest
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
}
public function testDeleteActivityAction()
public function testDeleteActivityAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
@@ -653,7 +669,7 @@ class TeamControllerTest extends APIControllerBaseTest
self::assertCount(0, $result['activities']);
}
public function testDeleteActivityActionErrors()
public function testDeleteActivityActionErrors(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [

View File

@@ -48,7 +48,7 @@ class SecurityControllerTest extends ControllerBaseTest
$this->assertStringContainsString('<form action="/en/login_check" method="post"', $content);
$this->assertStringContainsString('<input type="text" id="username" name="_username"', $content);
$this->assertStringContainsString('<input id="password" name="_password" type="password"', $content);
$this->assertStringContainsString('">Login</button>', $content);
$this->assertStringContainsString('">Log in</button>', $content);
$this->assertStringContainsString('<input type="hidden" name="_csrf_token" value="', $content);
$this->assertStringNotContainsString('<a href="/en/register/"', $content);
$this->assertStringNotContainsString('Register a new account', $content);

View File

@@ -12,8 +12,6 @@ namespace App\Tests\Controller;
use App\Entity\Team;
use App\Entity\User;
use App\Tests\DataFixtures\TeamFixtures;
use Doctrine\ORM\EntityManager;
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
use Symfony\Component\HttpKernel\HttpKernelBrowser;
/**
@@ -21,17 +19,17 @@ use Symfony\Component\HttpKernel\HttpKernelBrowser;
*/
class TeamControllerTest extends ControllerBaseTest
{
public function testIsSecure()
public function testIsSecure(): void
{
$this->assertUrlIsSecured('/admin/teams/');
}
public function testIsSecureForRole()
public function testIsSecureForRole(): void
{
$this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/admin/teams/');
}
public function testIndexAction()
public function testIndexAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$em = $this->getEntityManager();
@@ -47,7 +45,7 @@ class TeamControllerTest extends ControllerBaseTest
$this->assertDataTableRowCount($client, 'datatable_admin_teams', 6);
}
public function testIndexActionWithSearchTermQuery()
public function testIndexActionWithSearchTermQuery(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$fixture = new TeamFixtures();
@@ -69,7 +67,7 @@ class TeamControllerTest extends ControllerBaseTest
$this->assertDataTableRowCount($client, 'datatable_admin_teams', 5);
}
public function testCreateAction()
public function testCreateAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/teams/create');
@@ -90,7 +88,7 @@ class TeamControllerTest extends ControllerBaseTest
$this->assertHasCustomerAndProjectPermissionBoxes($client);
}
protected function assertHasCustomerAndProjectPermissionBoxes(HttpKernelBrowser $client)
protected function assertHasCustomerAndProjectPermissionBoxes(HttpKernelBrowser $client): void
{
$content = $client->getResponse()->getContent();
$this->assertStringContainsString('Grant access to customers', $content);
@@ -99,7 +97,7 @@ class TeamControllerTest extends ControllerBaseTest
$this->assertEquals(1, $client->getCrawler()->filter('form[name=team_project_form]')->count());
}
public function testEditAction()
public function testEditAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
@@ -121,11 +119,10 @@ class TeamControllerTest extends ControllerBaseTest
$this->assertEquals('Test Team 2', $editForm->get('team_edit_form[name]')->getValue());
}
public function testEditMemberAction()
public function testEditMemberAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$em = $this->getEntityManager();
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$this->importFixture($fixture);
@@ -143,65 +140,7 @@ class TeamControllerTest extends ControllerBaseTest
$this->assertEquals('Test Team 2', $editForm->get('team_edit_form[name]')->getValue());
}
public function testEditCustomerAccessAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
$em = $this->getEntityManager();
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$fixture->setAddCustomer(false);
$this->importFixture($fixture);
$team = $em->getRepository(Team::class)->find(1);
self::assertEquals(0, \count($team->getCustomers()));
$this->assertAccessIsGranted($client, '/admin/teams/1/edit');
$form = $client->getCrawler()->filter('form[name=team_customer_form]')->form();
/** @var ChoiceFormField $customer */
$customer = $form->get('team_customer_form[customers][0]');
$customer->tick();
$client->submit($form);
$this->assertIsRedirect($client, $this->createUrl('/admin/teams/1/edit'));
$team = $em->getRepository(Team::class)->find(1);
self::assertEquals(1, \count($team->getCustomers()));
}
public function testEditProjectAccessAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
$em = $this->getEntityManager();
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$fixture->setAddCustomer(false);
$this->importFixture($fixture);
$team = $em->getRepository(Team::class)->find(1);
self::assertEquals(0, \count($team->getProjects()));
$this->assertAccessIsGranted($client, '/admin/teams/1/edit');
$form = $client->getCrawler()->filter('form[name=team_project_form]')->form();
/** @var ChoiceFormField $customer */
$customer = $form->get('team_project_form[projects]');
$customer->select([1]);
$client->submit($form);
$this->assertIsRedirect($client, $this->createUrl('/admin/teams/1/edit'));
$team = $em->getRepository(Team::class)->find(1);
self::assertEquals(1, \count($team->getProjects()));
}
public function testDuplicateAction()
public function testDuplicateAction(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);

View File

@@ -17,6 +17,7 @@ use App\Entity\Project;
use App\Entity\Timesheet;
use App\Invoice\Calculator\DefaultCalculator;
use App\Invoice\InvoiceItemRepositoryInterface;
use App\Invoice\InvoiceModel;
use App\Invoice\NumberGenerator\DateNumberGenerator;
use App\Invoice\Renderer\TwigRenderer;
use App\Invoice\ServiceInvoice;
@@ -52,7 +53,7 @@ class ServiceInvoiceTest extends TestCase
return new ServiceInvoice($repo, new FileHelper(realpath(__DIR__ . '/../../var/data/')), $invoiceRepo, $formattings, (new InvoiceModelFactoryFactory($this))->create());
}
public function testInvalidExceptionOnChangeState()
public function testInvalidExceptionOnChangeState(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unknown invoice status');
@@ -60,7 +61,7 @@ class ServiceInvoiceTest extends TestCase
$sut->changeInvoiceStatus(new Invoice(), 'foo');
}
public function testEmptyObject()
public function testEmptyObject(): void
{
$sut = $this->getSut([]);
@@ -78,7 +79,7 @@ class ServiceInvoiceTest extends TestCase
$this->assertNull($sut->getNumberGeneratorByName('default'));
}
public function testWithDocumentDirectory()
public function testWithDocumentDirectory(): void
{
$sut = $this->getSut(['templates/invoice/renderer/']);
@@ -92,7 +93,7 @@ class ServiceInvoiceTest extends TestCase
$this->assertInstanceOf(InvoiceDocument::class, $actual);
}
public function testAdd()
public function testAdd(): void
{
$sut = $this->getSut([]);
@@ -110,7 +111,7 @@ class ServiceInvoiceTest extends TestCase
$this->assertEquals(1, \count($sut->getRenderer()));
}
public function testCreateModelThrowsOnMissingTemplate()
public function testCreateModelThrowsOnMissingTemplate(): void
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Cannot create invoice model without template');
@@ -122,7 +123,7 @@ class ServiceInvoiceTest extends TestCase
$sut->createModel($query);
}
public function testCreateModelUsesTemplateLanguage()
public function testCreateModelUsesTemplateLanguage(): void
{
$template = new InvoiceTemplate();
$template->setNumberGenerator('date');
@@ -141,7 +142,7 @@ class ServiceInvoiceTest extends TestCase
self::assertEquals('it', $model->getTemplate()->getLanguage());
}
public function testBeginAndEndDateFallback()
public function testBeginAndEndDateFallback(): void
{
$timezone = new \DateTimeZone('Europe/Vienna');
$customer = new Customer('foo');
@@ -207,7 +208,7 @@ class ServiceInvoiceTest extends TestCase
self::assertEquals('2020-11-27T23:59:59+0100', $query->getEnd()->format(DATE_ISO8601));
}
public function testCreateModelsSkipsModelsWithNegativeTotal()
public function testCreateModelsIncludesModelsWithNegativeTotal(): void
{
$timezone = new \DateTimeZone('Europe/Vienna');
@@ -310,12 +311,18 @@ class ServiceInvoiceTest extends TestCase
$sut->addInvoiceItemRepository($repo);
$models = $sut->createModels($query);
self::assertCount(2, $models);
self::assertEquals(1.73, $models[0]->getCalculator()->getTotal());
self::assertEquals(0.0, $models[1]->getCalculator()->getTotal());
self::assertCount(4, $models);
self::assertInstanceOf(InvoiceModel::class, $models[0]);
self::assertEquals(-0.01, $models[0]->getCalculator()->getTotal());
self::assertInstanceOf(InvoiceModel::class, $models[1]);
self::assertEquals(-100, $models[1]->getCalculator()->getTotal());
self::assertInstanceOf(InvoiceModel::class, $models[2]);
self::assertEquals(1.73, $models[2]->getCalculator()->getTotal());
self::assertInstanceOf(InvoiceModel::class, $models[3]);
self::assertEquals(0.0, $models[3]->getCalculator()->getTotal());
}
private function getNumberGeneratorSut()
private function getNumberGeneratorSut(): DateNumberGenerator
{
$repository = $this->createMock(InvoiceRepository::class);
$repository

View File

@@ -1202,151 +1202,6 @@ parameters:
count: 4
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:getRoleTestData\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testDeleteAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testDeleteActionWithUnknownTeam\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testDeleteActivityAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testDeleteActivityActionErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testDeleteCustomerAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testDeleteCustomerActionErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testDeleteMemberAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testDeleteMemberActionErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testDeleteProjectAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testDeleteProjectActionErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testGetCollection\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testGetEntity\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testIsSecure\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testIsSecureForRole\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testNotFound\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPatchAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPatchActionWithValidationErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostActionWithInvalidUser\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostActionWithValidationErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostActivityAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostActivityActionErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostCustomerAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostCustomerActionErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostMemberAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostMemberActionErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostProjectAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\TeamControllerTest\\:\\:testPostProjectActionErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/TeamControllerTest.php
-
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
count: 38
@@ -4222,76 +4077,11 @@ parameters:
count: 2
path: Controller/TagControllerTest.php
-
message: "#^Cannot call method getCustomers\\(\\) on App\\\\Entity\\\\Team\\|null\\.$#"
count: 2
path: Controller/TeamControllerTest.php
-
message: "#^Cannot call method getProjects\\(\\) on App\\\\Entity\\\\Team\\|null\\.$#"
count: 2
path: Controller/TeamControllerTest.php
-
message: "#^Cannot call method getValue\\(\\) on array\\<array\\<Symfony\\\\Component\\\\DomCrawler\\\\Field\\\\FormField\\>\\|Symfony\\\\Component\\\\DomCrawler\\\\Field\\\\FormField\\>\\|Symfony\\\\Component\\\\DomCrawler\\\\Field\\\\FormField\\.$#"
count: 4
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:assertHasCustomerAndProjectPermissionBoxes\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:testCreateAction\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:testDuplicateAction\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:testEditAction\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:testEditCustomerAccessAction\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:testEditMemberAction\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:testEditProjectAccessAction\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:testIndexAction\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:testIndexActionWithSearchTermQuery\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:testIsSecure\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\TeamControllerTest\\:\\:testIsSecureForRole\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/TeamControllerTest.php
-
message: "#^Parameter \\#2 \\$haystack of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringContainsString\\(\\) expects string, string\\|false given\\.$#"
count: 2
@@ -7859,12 +7649,7 @@ parameters:
-
message: "#^Cannot call method getTotal\\(\\) on App\\\\Invoice\\\\CalculatorInterface\\|null\\.$#"
count: 2
path: Invoice/ServiceInvoiceTest.php
-
message: "#^Method App\\\\Tests\\\\Invoice\\\\ServiceInvoiceTest\\:\\:getNumberGeneratorSut\\(\\) has no return type specified\\.$#"
count: 1
count: 4
path: Invoice/ServiceInvoiceTest.php
-
@@ -7872,46 +7657,6 @@ parameters:
count: 1
path: Invoice/ServiceInvoiceTest.php
-
message: "#^Method App\\\\Tests\\\\Invoice\\\\ServiceInvoiceTest\\:\\:testAdd\\(\\) has no return type specified\\.$#"
count: 1
path: Invoice/ServiceInvoiceTest.php
-
message: "#^Method App\\\\Tests\\\\Invoice\\\\ServiceInvoiceTest\\:\\:testBeginAndEndDateFallback\\(\\) has no return type specified\\.$#"
count: 1
path: Invoice/ServiceInvoiceTest.php
-
message: "#^Method App\\\\Tests\\\\Invoice\\\\ServiceInvoiceTest\\:\\:testCreateModelThrowsOnMissingTemplate\\(\\) has no return type specified\\.$#"
count: 1
path: Invoice/ServiceInvoiceTest.php
-
message: "#^Method App\\\\Tests\\\\Invoice\\\\ServiceInvoiceTest\\:\\:testCreateModelUsesTemplateLanguage\\(\\) has no return type specified\\.$#"
count: 1
path: Invoice/ServiceInvoiceTest.php
-
message: "#^Method App\\\\Tests\\\\Invoice\\\\ServiceInvoiceTest\\:\\:testCreateModelsSkipsModelsWithNegativeTotal\\(\\) has no return type specified\\.$#"
count: 1
path: Invoice/ServiceInvoiceTest.php
-
message: "#^Method App\\\\Tests\\\\Invoice\\\\ServiceInvoiceTest\\:\\:testEmptyObject\\(\\) has no return type specified\\.$#"
count: 1
path: Invoice/ServiceInvoiceTest.php
-
message: "#^Method App\\\\Tests\\\\Invoice\\\\ServiceInvoiceTest\\:\\:testInvalidExceptionOnChangeState\\(\\) has no return type specified\\.$#"
count: 1
path: Invoice/ServiceInvoiceTest.php
-
message: "#^Method App\\\\Tests\\\\Invoice\\\\ServiceInvoiceTest\\:\\:testWithDocumentDirectory\\(\\) has no return type specified\\.$#"
count: 1
path: Invoice/ServiceInvoiceTest.php
-
message: "#^Parameter \\#1 \\$dataDir of class App\\\\Utils\\\\FileHelper constructor expects string, string\\|false given\\.$#"
count: 1