diff --git a/.phpstan-no-baseline.neon b/.phpstan-no-baseline.neon new file mode 100644 index 00000000..dd83cf7c --- /dev/null +++ b/.phpstan-no-baseline.neon @@ -0,0 +1,7 @@ +parameters: + level: 8 + paths: + - src + - tests + excludePaths: + - src/Kernel.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 82ec99a0..f459b87e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +* [PR-308](https://github.com/itk-dev/economics/pull/308) + * PHPStan baseline cleanup in src/ (530 → 0); behavior- and schema-affecting items below. + * Migrations Version20260517131038 and Version20260517151632: relaxed 18 columns and + 11 ManyToOne FKs on synced entities (issue, project, version, worklog) to nullable + to match property types; application-layer asserts/form validators still enforce required-ness. + * Fixed report services crashing on worklogs with null project/issue and rendering empty epic + tags (Epic::getName() → getTitle()); fixed SubscriptionHandlerService::getVersion() + lookup by non-existent field. + * LeantimeApiService switched from object to array json_decode; wire shape unchanged. + ## [3.5.0] - 2026-05-26 * [PR-315](https://github.com/itk-dev/economics/pull/315) @@ -15,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [PR-310](https://github.com/itk-dev/economics/pull/310) * Remove dataProvider scoping. * [PR-264](https://github.com/itk-dev/economics/pull/264) - Added cybersecurity report. + * Added cybersecurity report. * [PR-309](https://github.com/itk-dev/economics/pull/309) * Consolidated project and service-agreement fields. * Moved the Leantime project link from service agreement to project and rendered it via a Twig helper. diff --git a/migrations/Version20260517131038.php b/migrations/Version20260517131038.php new file mode 100644 index 00000000..de73a57d --- /dev/null +++ b/migrations/Version20260517131038.php @@ -0,0 +1,41 @@ +addSql('ALTER TABLE issue CHANGE name name VARCHAR(255) DEFAULT NULL, CHANGE project_tracker_id project_tracker_id VARCHAR(255) DEFAULT NULL, CHANGE project_tracker_key project_tracker_key VARCHAR(255) DEFAULT NULL, CHANGE link_to_issue link_to_issue VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE project CHANGE name name VARCHAR(255) DEFAULT NULL, CHANGE project_tracker_project_url project_tracker_project_url VARCHAR(255) DEFAULT NULL, CHANGE project_tracker_key project_tracker_key VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE project_billing CHANGE period_start period_start DATETIME DEFAULT NULL, CHANGE period_end period_end DATETIME DEFAULT NULL'); + $this->addSql('ALTER TABLE service_agreement CHANGE valid_from valid_from DATETIME DEFAULT NULL'); + $this->addSql('ALTER TABLE version CHANGE name name VARCHAR(255) DEFAULT NULL, CHANGE project_tracker_id project_tracker_id VARCHAR(255) DEFAULT NULL, CHANGE is_billable is_billable TINYINT(1) DEFAULT NULL'); + $this->addSql('ALTER TABLE worklog CHANGE worklog_id worklog_id INT DEFAULT NULL, CHANGE worker worker VARCHAR(255) DEFAULT NULL, CHANGE time_spent_seconds time_spent_seconds INT DEFAULT NULL, CHANGE started started DATETIME DEFAULT NULL, CHANGE project_tracker_issue_id project_tracker_issue_id VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE project CHANGE name name VARCHAR(255) NOT NULL, CHANGE project_tracker_project_url project_tracker_project_url VARCHAR(255) NOT NULL, CHANGE project_tracker_key project_tracker_key VARCHAR(255) NOT NULL'); + $this->addSql('ALTER TABLE worklog CHANGE worklog_id worklog_id INT NOT NULL, CHANGE worker worker VARCHAR(255) NOT NULL, CHANGE time_spent_seconds time_spent_seconds INT NOT NULL, CHANGE started started DATETIME NOT NULL, CHANGE project_tracker_issue_id project_tracker_issue_id VARCHAR(255) NOT NULL'); + $this->addSql('ALTER TABLE project_billing CHANGE period_start period_start DATETIME NOT NULL, CHANGE period_end period_end DATETIME NOT NULL'); + $this->addSql('ALTER TABLE version CHANGE name name VARCHAR(255) NOT NULL, CHANGE project_tracker_id project_tracker_id VARCHAR(255) NOT NULL, CHANGE is_billable is_billable TINYINT(1) NOT NULL'); + $this->addSql('ALTER TABLE service_agreement CHANGE valid_from valid_from DATETIME NOT NULL'); + $this->addSql('ALTER TABLE issue CHANGE name name VARCHAR(255) NOT NULL, CHANGE project_tracker_id project_tracker_id VARCHAR(255) NOT NULL, CHANGE project_tracker_key project_tracker_key VARCHAR(255) NOT NULL, CHANGE link_to_issue link_to_issue VARCHAR(255) NOT NULL'); + } +} diff --git a/migrations/Version20260517151632.php b/migrations/Version20260517151632.php new file mode 100644 index 00000000..e13f2d9c --- /dev/null +++ b/migrations/Version20260517151632.php @@ -0,0 +1,45 @@ +addSql('ALTER TABLE cybersecurity_agreement CHANGE service_agreement_id service_agreement_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE invoice_entry CHANGE invoice_id invoice_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE issue_product CHANGE issue_id issue_id INT DEFAULT NULL, CHANGE product_id product_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE product CHANGE project_id project_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE project_billing CHANGE project_id project_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE service_agreement CHANGE project_id project_id INT DEFAULT NULL, CHANGE client_id client_id INT DEFAULT NULL, CHANGE project_lead_id project_lead_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE version CHANGE project_id project_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE worklog CHANGE issue_id issue_id INT DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE invoice_entry CHANGE invoice_id invoice_id INT NOT NULL'); + $this->addSql('ALTER TABLE worklog CHANGE issue_id issue_id INT NOT NULL'); + $this->addSql('ALTER TABLE product CHANGE project_id project_id INT NOT NULL'); + $this->addSql('ALTER TABLE issue_product CHANGE issue_id issue_id INT NOT NULL, CHANGE product_id product_id INT NOT NULL'); + $this->addSql('ALTER TABLE project_billing CHANGE project_id project_id INT NOT NULL'); + $this->addSql('ALTER TABLE version CHANGE project_id project_id INT NOT NULL'); + $this->addSql('ALTER TABLE service_agreement CHANGE project_id project_id INT NOT NULL, CHANGE client_id client_id INT NOT NULL, CHANGE project_lead_id project_lead_id INT NOT NULL'); + $this->addSql('ALTER TABLE cybersecurity_agreement CHANGE service_agreement_id service_agreement_id INT NOT NULL'); + } +} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index fef52c75..f51e71c3 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,3776 +1,2 @@ parameters: - ignoreErrors: - - - message: '#^Strict comparison using \=\=\= between null and OpenSpout\\Common\\Entity\\Row will always evaluate to false\.$#' - identifier: identical.alwaysFalse - count: 1 - path: src/Command/ProductsImportCommand.php - - - - message: '#^Method App\\Command\\SubscriptionHandlerCommand\:\:getLastQuarter\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Command/SubscriptionHandlerCommand.php - - - - message: '#^Method App\\Controller\\InvoiceController\:\:generateDescription\(\) has parameter \$defaultInvoiceDescriptionTemplate with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Controller/InvoiceController.php - - - - message: '#^Method App\\Controller\\IssueController\:\:__construct\(\) has parameter \$options with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Controller/IssueController.php - - - - message: '#^Method App\\Controller\\ManagementReportController\:\:createGroupedInvoices\(\) has parameter \$invoices with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Controller/ManagementReportController.php - - - - message: '#^Method App\\Controller\\ManagementReportController\:\:createGroupedInvoices\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Controller/ManagementReportController.php - - - - message: '#^Method App\\Controller\\ManagementReportController\:\:getInvoicesDataFromDates\(\) has parameter \$dateInterval with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Controller/ManagementReportController.php - - - - message: '#^Method App\\Controller\\ManagementReportController\:\:getInvoicesDataFromDates\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Controller/ManagementReportController.php - - - - message: '#^Method App\\Controller\\OpenIdConnectController\:\:logout\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Controller/OpenIdConnectController.php - - - - message: '#^Method App\\Controller\\PlanningController\:\:createResponse\(\) has parameter \$data with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Controller/PlanningController.php - - - - message: '#^Method App\\Controller\\PlanningController\:\:preparePlanningData\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Controller/PlanningController.php - - - - message: '#^Method App\\Controller\\SubscriptionController\:\:getFrequencies\(\) has parameter \$subscriptions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Controller/SubscriptionController.php - - - - message: '#^Method App\\Controller\\SubscriptionController\:\:subscriptionHandler\(\) has parameter \$content with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Controller/SubscriptionController.php - - - - message: '#^Method App\\Controller\\SubscriptionController\:\:subscriptionHandler\(\) has parameter \$subscriptionType with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Controller/SubscriptionController.php - - - - message: '#^Method App\\Controller\\SubscriptionController\:\:subscriptionHandler\(\) has parameter \$userEmail with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Controller/SubscriptionController.php - - - - message: '#^Cannot clone DateTime\|false\.$#' - identifier: clone.nonObject - count: 1 - path: src/DataFixtures/AppFixtures.php - - - - message: '#^Property App\\Entity\\Account\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Account.php - - - - message: '#^Property App\\Entity\\Account\:\:\$value type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Account.php - - - - message: '#^Property App\\Entity\\Client\:\:\$invoices type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Client.php - - - - message: '#^Property App\\Entity\\Client\:\:\$invoices with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Client.php - - - - message: '#^Property App\\Entity\\Client\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Client.php - - - - message: '#^Property App\\Entity\\Client\:\:\$projects type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Client.php - - - - message: '#^Property App\\Entity\\Client\:\:\$projects with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Client.php - - - - message: '#^Property App\\Entity\\CybersecurityAgreement\:\:\$serviceAgreement type mapping mismatch\: property can contain App\\Entity\\ServiceAgreement\|null but database expects App\\Entity\\ServiceAgreement\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/CybersecurityAgreement.php - - - - message: '#^Property App\\Entity\\DataProvider\:\:\$class type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/DataProvider.php - - - - message: '#^Property App\\Entity\\DataProvider\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/DataProvider.php - - - - message: '#^Property App\\Entity\\Epic\:\:\$issues type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Epic.php - - - - message: '#^Property App\\Entity\\Epic\:\:\$issues with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Epic.php - - - - message: '#^Property App\\Entity\\Epic\:\:\$title type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Epic.php - - - - message: '#^Method App\\Entity\\Invoice\:\:setInvoiceEntryIndexes\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Entity/Invoice.php - - - - message: '#^Property App\\Entity\\Invoice\:\:\$invoiceEntries type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Invoice.php - - - - message: '#^Property App\\Entity\\Invoice\:\:\$invoiceEntries with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Invoice.php - - - - message: '#^Property App\\Entity\\Invoice\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Invoice.php - - - - message: '#^Property App\\Entity\\Invoice\:\:\$recorded type mapping mismatch\: property can contain bool\|null but database expects bool\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Invoice.php - - - - message: '#^Method App\\Entity\\InvoiceEntry\:\:setInvoiceIndex\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Entity/InvoiceEntry.php - - - - message: '#^Property App\\Entity\\InvoiceEntry\:\:\$index type mapping mismatch\: property can contain int\|null but database expects int\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/InvoiceEntry.php - - - - message: '#^Property App\\Entity\\InvoiceEntry\:\:\$invoice type mapping mismatch\: property can contain App\\Entity\\Invoice\|null but database expects App\\Entity\\Invoice\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/InvoiceEntry.php - - - - message: '#^Property App\\Entity\\InvoiceEntry\:\:\$issueProducts type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/InvoiceEntry.php - - - - message: '#^Property App\\Entity\\InvoiceEntry\:\:\$issueProducts with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/InvoiceEntry.php - - - - message: '#^Property App\\Entity\\InvoiceEntry\:\:\$worklogs type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/InvoiceEntry.php - - - - message: '#^Property App\\Entity\\InvoiceEntry\:\:\$worklogs with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/InvoiceEntry.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$epics type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$epics with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$linkToIssue type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$products type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$products with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$projectTrackerId type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$projectTrackerKey type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$versions type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$versions with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$worklogs type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\Issue\:\:\$worklogs with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Issue.php - - - - message: '#^Property App\\Entity\\IssueProduct\:\:\$issue type mapping mismatch\: property can contain App\\Entity\\Issue\|null but database expects App\\Entity\\Issue\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/IssueProduct.php - - - - message: '#^Property App\\Entity\\IssueProduct\:\:\$product type mapping mismatch\: property can contain App\\Entity\\Product\|null but database expects App\\Entity\\Product\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/IssueProduct.php - - - - message: '#^Property App\\Entity\\IssueProduct\:\:\$quantity type mapping mismatch\: property can contain float\|null but database expects float\|int\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/IssueProduct.php - - - - message: '#^Property App\\Entity\\Product\:\:\$issues type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Product.php - - - - message: '#^Property App\\Entity\\Product\:\:\$issues with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Product.php - - - - message: '#^Property App\\Entity\\Product\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Product.php - - - - message: '#^Property App\\Entity\\Product\:\:\$price type mapping mismatch\: property can contain string\|null but database expects float\|int\|string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Product.php - - - - message: '#^Property App\\Entity\\Product\:\:\$project type mapping mismatch\: property can contain App\\Entity\\Project\|null but database expects App\\Entity\\Project\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Product.php - - - - message: '#^Property App\\Entity\\Project\:\:\$clients type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$clients with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$invoices type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$invoices with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$issues type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$issues with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$products type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$products with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$projectBillings type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$projectBillings with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$projectTrackerKey type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$projectTrackerProjectUrl type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$versions type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$versions with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$worklogs type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\Project\:\:\$worklogs with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Project.php - - - - message: '#^Property App\\Entity\\ProjectBilling\:\:\$invoices type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/ProjectBilling.php - - - - message: '#^Property App\\Entity\\ProjectBilling\:\:\$invoices with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/ProjectBilling.php - - - - message: '#^Property App\\Entity\\ProjectBilling\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ProjectBilling.php - - - - message: '#^Property App\\Entity\\ProjectBilling\:\:\$periodEnd type mapping mismatch\: property can contain DateTimeInterface\|null but database expects DateTimeInterface\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ProjectBilling.php - - - - message: '#^Property App\\Entity\\ProjectBilling\:\:\$periodStart type mapping mismatch\: property can contain DateTimeInterface\|null but database expects DateTimeInterface\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ProjectBilling.php - - - - message: '#^Property App\\Entity\\ProjectBilling\:\:\$project type mapping mismatch\: property can contain App\\Entity\\Project\|null but database expects App\\Entity\\Project\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/ProjectBilling.php - - - - message: '#^Property App\\Entity\\ProjectBilling\:\:\$recorded type mapping mismatch\: property can contain bool\|null but database expects bool\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ProjectBilling.php - - - - message: '#^Property App\\Entity\\ProjectVersionBudget\:\:\$budget type mapping mismatch\: property can contain float\|null but database expects float\|int\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ProjectVersionBudget.php - - - - message: '#^Property App\\Entity\\ProjectVersionBudget\:\:\$projectId type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ProjectVersionBudget.php - - - - message: '#^Property App\\Entity\\ProjectVersionBudget\:\:\$versionId type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ProjectVersionBudget.php - - - - message: '#^Property App\\Entity\\ServiceAgreement\:\:\$client type mapping mismatch\: property can contain App\\Entity\\Client\|null but database expects App\\Entity\\Client\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/ServiceAgreement.php - - - - message: '#^Property App\\Entity\\ServiceAgreement\:\:\$hostingProvider type mapping mismatch\: property can contain App\\Enum\\HostingProviderEnum\|null but database expects App\\Enum\\HostingProviderEnum\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ServiceAgreement.php - - - - message: '#^Property App\\Entity\\ServiceAgreement\:\:\$isActive type mapping mismatch\: property can contain bool\|null but database expects bool\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ServiceAgreement.php - - - - message: '#^Property App\\Entity\\ServiceAgreement\:\:\$price type mapping mismatch\: property can contain float\|null but database expects float\|int\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ServiceAgreement.php - - - - message: '#^Property App\\Entity\\ServiceAgreement\:\:\$project type mapping mismatch\: property can contain App\\Entity\\Project\|null but database expects App\\Entity\\Project\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/ServiceAgreement.php - - - - message: '#^Property App\\Entity\\ServiceAgreement\:\:\$projectLead type mapping mismatch\: property can contain App\\Entity\\Worker\|null but database expects App\\Entity\\Worker\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/ServiceAgreement.php - - - - message: '#^Property App\\Entity\\ServiceAgreement\:\:\$systemOwnerNotices type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Entity/ServiceAgreement.php - - - - message: '#^Property App\\Entity\\ServiceAgreement\:\:\$validFrom type mapping mismatch\: property can contain DateTimeInterface\|null but database expects DateTimeInterface\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/ServiceAgreement.php - - - - message: '#^Method App\\Entity\\Subscription\:\:getUrlParams\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Entity/Subscription.php - - - - message: '#^Method App\\Entity\\Subscription\:\:setUrlParams\(\) has parameter \$urlParams with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Entity/Subscription.php - - - - message: '#^Property App\\Entity\\Subscription\:\:\$email type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Subscription.php - - - - message: '#^Property App\\Entity\\Subscription\:\:\$urlParams type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Entity/Subscription.php - - - - message: '#^Method App\\Entity\\User\:\:getUserIdentifier\(\) should return non\-empty\-string but returns string\.$#' - identifier: return.type - count: 1 - path: src/Entity/User.php - - - - message: '#^Method App\\Entity\\User\:\:setRoles\(\) has parameter \$roles with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Entity/User.php - - - - message: '#^Property App\\Entity\\User\:\:\$email type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/User.php - - - - message: '#^Property App\\Entity\\User\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/User.php - - - - message: '#^Property App\\Entity\\User\:\:\$roles type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Entity/User.php - - - - message: '#^Property App\\Entity\\Version\:\:\$isBillable type mapping mismatch\: property can contain bool\|null but database expects bool\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Version.php - - - - message: '#^Property App\\Entity\\Version\:\:\$issues type mapping mismatch\: property can contain Doctrine\\Common\\Collections\\Collection but database expects Doctrine\\Common\\Collections\\Collection&iterable\\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Version.php - - - - message: '#^Property App\\Entity\\Version\:\:\$issues with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Entity/Version.php - - - - message: '#^Property App\\Entity\\Version\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Version.php - - - - message: '#^Property App\\Entity\\Version\:\:\$project type mapping mismatch\: property can contain App\\Entity\\Project\|null but database expects App\\Entity\\Project\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Version.php - - - - message: '#^Property App\\Entity\\Version\:\:\$projectTrackerId type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Version.php - - - - message: '#^Property App\\Entity\\Worker\:\:\$email type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Worker.php - - - - message: '#^Property App\\Entity\\WorkerGroup\:\:\$name type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/WorkerGroup.php - - - - message: '#^Property App\\Entity\\Worklog\:\:\$issue type mapping mismatch\: property can contain App\\Entity\\Issue\|null but database expects App\\Entity\\Issue\.$#' - identifier: doctrine.associationType - count: 1 - path: src/Entity/Worklog.php - - - - message: '#^Property App\\Entity\\Worklog\:\:\$projectTrackerIssueId type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Worklog.php - - - - message: '#^Property App\\Entity\\Worklog\:\:\$started type mapping mismatch\: property can contain DateTimeInterface\|null but database expects DateTimeInterface\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Worklog.php - - - - message: '#^Property App\\Entity\\Worklog\:\:\$timeSpentSeconds type mapping mismatch\: property can contain int\|null but database expects int\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Worklog.php - - - - message: '#^Property App\\Entity\\Worklog\:\:\$worker type mapping mismatch\: property can contain string\|null but database expects string\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Worklog.php - - - - message: '#^Property App\\Entity\\Worklog\:\:\$worklogId type mapping mismatch\: property can contain int\|null but database expects int\.$#' - identifier: doctrine.columnType - count: 1 - path: src/Entity/Worklog.php - - - - message: '#^Class App\\Form\\AccountType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/AccountType.php - - - - message: '#^Class App\\Form\\BillableUnbilledHoursReportType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/BillableUnbilledHoursReportType.php - - - - message: '#^Class App\\Form\\ClientFilterType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ClientFilterType.php - - - - message: '#^Class App\\Form\\ClientType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ClientType.php - - - - message: '#^Method App\\Form\\ClientType\:\:getVersionOptions\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Form/ClientType.php - - - - message: '#^Class App\\Form\\CombinedServiceAgreementType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/CombinedServiceAgreementType.php - - - - message: '#^Class App\\Form\\CybersecurityAgreementType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/CybersecurityAgreementType.php - - - - message: '#^Class App\\Form\\CybersecurityReportType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/CybersecurityReportType.php - - - - message: '#^Class App\\Form\\ForecastReportType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ForecastReportType.php - - - - message: '#^Class App\\Form\\HourReportType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/HourReportType.php - - - - message: '#^Class App\\Form\\InvoiceEntryType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/InvoiceEntryType.php - - - - message: '#^Class App\\Form\\InvoiceEntryWorklogFilterType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/InvoiceEntryWorklogFilterType.php - - - - message: '#^Class App\\Form\\InvoiceEntryWorklogType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/InvoiceEntryWorklogType.php - - - - message: '#^Class App\\Form\\InvoiceFilterType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/InvoiceFilterType.php - - - - message: '#^Class App\\Form\\InvoiceNewType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/InvoiceNewType.php - - - - message: '#^Class App\\Form\\InvoiceRecordType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/InvoiceRecordType.php - - - - message: '#^Class App\\Form\\InvoiceType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/InvoiceType.php - - - - message: '#^Class App\\Form\\InvoicingRateReportType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/InvoicingRateReportType.php - - - - message: '#^Class App\\Form\\IssueFilterType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/IssueFilterType.php - - - - message: '#^Class App\\Form\\IssueProductType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/IssueProductType.php - - - - message: '#^Class App\\Form\\ManagementReportDateIntervalType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ManagementReportDateIntervalType.php - - - - message: '#^Class App\\Form\\NameFilterType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/NameFilterType.php - - - - message: '#^Class App\\Form\\PlanningType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/PlanningType.php - - - - message: '#^Class App\\Form\\ProductFilterType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ProductFilterType.php - - - - message: '#^Class App\\Form\\ProductType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ProductType.php - - - - message: '#^Class App\\Form\\ProjectBillingFilterType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ProjectBillingFilterType.php - - - - message: '#^Class App\\Form\\ProjectBillingRecordType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ProjectBillingRecordType.php - - - - message: '#^Class App\\Form\\ProjectBillingType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ProjectBillingType.php - - - - message: '#^Class App\\Form\\ProjectFilterType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ProjectFilterType.php - - - - message: '#^Class App\\Form\\ProjectType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ProjectType.php - - - - message: '#^Class App\\Form\\ServiceAgreementFilterType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ServiceAgreementFilterType.php - - - - message: '#^Class App\\Form\\ServiceAgreementType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/ServiceAgreementType.php - - - - message: '#^Class App\\Form\\SubscriptionFilterType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/SubscriptionFilterType.php - - - - message: '#^Class App\\Form\\WorkerGroupType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/WorkerGroupType.php - - - - message: '#^Class App\\Form\\WorkerType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/WorkerType.php - - - - message: '#^Class App\\Form\\WorkloadReportType extends generic class Symfony\\Component\\Form\\AbstractType but does not specify its types\: TData$#' - identifier: missingType.generics - count: 1 - path: src/Form/WorkloadReportType.php - - - - message: '#^Method App\\Message\\LeantimeUpdateMessage\:\:__construct\(\) has parameter \$projectTrackerProjectIds with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Message/LeantimeUpdateMessage.php - - - - message: '#^Method App\\Model\\DashboardData\:\:__construct\(\) has parameter \$monthStatuses with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Model/DashboardData.php - - - - message: '#^Method App\\Model\\DashboardData\:\:__construct\(\) has parameter \$weekStatuses with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Model/DashboardData.php - - - - message: '#^Method App\\Model\\DataProvider\\DataProviderIssueData\:\:__construct\(\) has parameter \$epics with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Model/DataProvider/DataProviderIssueData.php - - - - message: '#^Property App\\Model\\Invoices\\InvoiceEntryWorklogsFilterData\:\:\$epics type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Model/Invoices/InvoiceEntryWorklogsFilterData.php - - - - message: '#^Method App\\Model\\Invoices\\PagedResult\:\:__construct\(\) has parameter \$items with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Model/Invoices/PagedResult.php - - - - message: '#^Property App\\Model\\Reports\\BillableUnbilledHoursReportData\:\:\$projectTotals type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Model/Reports/BillableUnbilledHoursReportData.php - - - - message: '#^Constructor of class App\\Model\\Reports\\ForecastReportWorklogData has an unused parameter \$description\.$#' - identifier: constructor.unusedParameter - count: 1 - path: src/Model/Reports/ForecastReportWorklogData.php - - - - message: '#^Constructor of class App\\Model\\Reports\\ForecastReportWorklogData has an unused parameter \$worklogId\.$#' - identifier: constructor.unusedParameter - count: 1 - path: src/Model/Reports/ForecastReportWorklogData.php - - - - message: '#^Method App\\Model\\Reports\\ForecastReportWorklogData\:\:__construct\(\) has parameter \$description with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Model/Reports/ForecastReportWorklogData.php - - - - message: '#^Method App\\Model\\Reports\\ForecastReportWorklogData\:\:__construct\(\) has parameter \$worklogId with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Model/Reports/ForecastReportWorklogData.php - - - - message: '#^Class App\\Model\\Reports\\HourReportData has an uninitialized readonly property \$id\. Assign it in the constructor\.$#' - identifier: property.uninitializedReadonly - count: 1 - path: src/Model/Reports/HourReportData.php - - - - message: '#^Method App\\Model\\Reports\\HourReportProjectTicket\:\:__construct\(\) has parameter \$headline with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Model/Reports/HourReportProjectTicket.php - - - - message: '#^Method App\\Model\\Reports\\HourReportProjectTicket\:\:__construct\(\) has parameter \$id with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Model/Reports/HourReportProjectTicket.php - - - - message: '#^Method App\\Model\\Reports\\HourReportProjectTicket\:\:__construct\(\) has parameter \$linkToIssue with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Model/Reports/HourReportProjectTicket.php - - - - message: '#^Method App\\Model\\Reports\\HourReportProjectTicket\:\:__construct\(\) has parameter \$projectTrackerId with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Model/Reports/HourReportProjectTicket.php - - - - message: '#^Method App\\Model\\Reports\\HourReportProjectTicket\:\:__construct\(\) has parameter \$totalEstimated with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Model/Reports/HourReportProjectTicket.php - - - - message: '#^Method App\\Model\\Reports\\HourReportProjectTicket\:\:__construct\(\) has parameter \$totalSpent with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Model/Reports/HourReportProjectTicket.php - - - - message: '#^Property App\\Model\\Reports\\HourReportProjectTicket\:\:\$projectTickets with generic class Doctrine\\Common\\Collections\\ArrayCollection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Model/Reports/HourReportProjectTicket.php - - - - message: '#^Property App\\Model\\Reports\\HourReportProjectTicket\:\:\$timesheets with generic class Doctrine\\Common\\Collections\\ArrayCollection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Model/Reports/HourReportProjectTicket.php - - - - message: '#^Property App\\Model\\Reports\\HourReportWorklog\:\:\$projectTicket with generic class Doctrine\\Common\\Collections\\ArrayCollection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Model/Reports/HourReportWorklog.php - - - - message: '#^Class App\\Model\\Reports\\InvoicingRateReportData has an uninitialized readonly property \$id\. Assign it in the constructor\.$#' - identifier: property.uninitializedReadonly - count: 1 - path: src/Model/Reports/InvoicingRateReportData.php - - - - message: '#^Property App\\Model\\Reports\\InvoicingRateReportData\:\:\$periodAverages with generic class Doctrine\\Common\\Collections\\ArrayCollection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Model/Reports/InvoicingRateReportData.php - - - - message: '#^Property App\\Model\\Reports\\InvoicingRateReportWorker\:\:\$dataByPeriod type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Model/Reports/InvoicingRateReportWorker.php - - - - message: '#^Property App\\Model\\Reports\\InvoicingRateReportWorker\:\:\$projectData type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Model/Reports/InvoicingRateReportWorker.php - - - - message: '#^Class App\\Model\\Reports\\WorkloadReportData has an uninitialized readonly property \$id\. Assign it in the constructor\.$#' - identifier: property.uninitializedReadonly - count: 1 - path: src/Model/Reports/WorkloadReportData.php - - - - message: '#^Property App\\Model\\Reports\\WorkloadReportData\:\:\$periodAverages with generic class Doctrine\\Common\\Collections\\ArrayCollection does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Model/Reports/WorkloadReportData.php - - - - message: '#^Class App\\Repository\\AccountRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/AccountRepository.php - - - - message: '#^Class App\\Repository\\AccountRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/AccountRepository.php - - - - message: '#^Class App\\Repository\\AccountRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/AccountRepository.php - - - - message: '#^Class App\\Repository\\AccountRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/AccountRepository.php - - - - message: '#^Method App\\Repository\\AccountRepository\:\:getAllChoices\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/AccountRepository.php - - - - message: '#^Method App\\Repository\\AccountRepository\:\:getFilteredPagination\(\) return type has no value type specified in iterable type Knp\\Component\\Pager\\Pagination\\PaginationInterface\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/AccountRepository.php - - - - message: '#^Method App\\Repository\\AccountRepository\:\:getFilteredPagination\(\) return type with generic interface Knp\\Component\\Pager\\Pagination\\PaginationInterface does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Repository/AccountRepository.php - - - - message: '#^Class App\\Repository\\ClientRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ClientRepository.php - - - - message: '#^Class App\\Repository\\ClientRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ClientRepository.php - - - - message: '#^Class App\\Repository\\ClientRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ClientRepository.php - - - - message: '#^Class App\\Repository\\ClientRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ClientRepository.php - - - - message: '#^Method App\\Repository\\ClientRepository\:\:getFilteredPagination\(\) return type has no value type specified in iterable type Knp\\Component\\Pager\\Pagination\\PaginationInterface\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ClientRepository.php - - - - message: '#^Method App\\Repository\\ClientRepository\:\:getFilteredPagination\(\) return type with generic interface Knp\\Component\\Pager\\Pagination\\PaginationInterface does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Repository/ClientRepository.php - - - - message: '#^Class App\\Repository\\DataProviderRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/DataProviderRepository.php - - - - message: '#^Class App\\Repository\\DataProviderRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/DataProviderRepository.php - - - - message: '#^Class App\\Repository\\DataProviderRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/DataProviderRepository.php - - - - message: '#^Class App\\Repository\\DataProviderRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/DataProviderRepository.php - - - - message: '#^Class App\\Repository\\EpicRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/EpicRepository.php - - - - message: '#^Class App\\Repository\\EpicRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/EpicRepository.php - - - - message: '#^Class App\\Repository\\EpicRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/EpicRepository.php - - - - message: '#^Class App\\Repository\\EpicRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/EpicRepository.php - - - - message: '#^Class App\\Repository\\InvoiceEntryRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/InvoiceEntryRepository.php - - - - message: '#^Class App\\Repository\\InvoiceEntryRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/InvoiceEntryRepository.php - - - - message: '#^Class App\\Repository\\InvoiceEntryRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/InvoiceEntryRepository.php - - - - message: '#^Class App\\Repository\\InvoiceEntryRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/InvoiceEntryRepository.php - - - - message: '#^Class App\\Repository\\InvoiceRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/InvoiceRepository.php - - - - message: '#^Class App\\Repository\\InvoiceRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/InvoiceRepository.php - - - - message: '#^Class App\\Repository\\InvoiceRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/InvoiceRepository.php - - - - message: '#^Class App\\Repository\\InvoiceRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/InvoiceRepository.php - - - - message: '#^Method App\\Repository\\InvoiceRepository\:\:getByRecordedDateBetween\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/InvoiceRepository.php - - - - message: '#^Method App\\Repository\\InvoiceRepository\:\:getFilteredPagination\(\) return type has no value type specified in iterable type Knp\\Component\\Pager\\Pagination\\PaginationInterface\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/InvoiceRepository.php - - - - message: '#^Method App\\Repository\\InvoiceRepository\:\:getFilteredPagination\(\) return type with generic interface Knp\\Component\\Pager\\Pagination\\PaginationInterface does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Repository/InvoiceRepository.php - - - - message: '#^Class App\\Repository\\IssueProductRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueProductRepository.php - - - - message: '#^Class App\\Repository\\IssueProductRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueProductRepository.php - - - - message: '#^Class App\\Repository\\IssueProductRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueProductRepository.php - - - - message: '#^Class App\\Repository\\IssueProductRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueProductRepository.php - - - - message: '#^Class App\\Repository\\IssueRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Class App\\Repository\\IssueRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Class App\\Repository\\IssueRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Class App\\Repository\\IssueRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Method App\\Repository\\IssueRepository\:\:findEpicOptionsByProject\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Method App\\Repository\\IssueRepository\:\:findIssuesInDateRange\(\) has parameter \$projects with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Method App\\Repository\\IssueRepository\:\:findIssuesInDateRange\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Method App\\Repository\\IssueRepository\:\:getClosedIssuesFromInterval\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Method App\\Repository\\IssueRepository\:\:getFilteredPagination\(\) return type has no value type specified in iterable type Knp\\Component\\Pager\\Pagination\\PaginationInterface\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Method App\\Repository\\IssueRepository\:\:getFilteredPagination\(\) return type with generic interface Knp\\Component\\Pager\\Pagination\\PaginationInterface does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Method App\\Repository\\IssueRepository\:\:issuesContainingVersion\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Method App\\Repository\\IssueRepository\:\:issuesContainingVersionTitle\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/IssueRepository.php - - - - message: '#^Class App\\Repository\\ProductRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProductRepository.php - - - - message: '#^Class App\\Repository\\ProductRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProductRepository.php - - - - message: '#^Class App\\Repository\\ProductRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProductRepository.php - - - - message: '#^Class App\\Repository\\ProductRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProductRepository.php - - - - message: '#^Method App\\Repository\\ProductRepository\:\:getFilteredPagination\(\) return type has no value type specified in iterable type Knp\\Component\\Pager\\Pagination\\PaginationInterface\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProductRepository.php - - - - message: '#^Method App\\Repository\\ProductRepository\:\:getFilteredPagination\(\) return type with generic interface Knp\\Component\\Pager\\Pagination\\PaginationInterface does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Repository/ProductRepository.php - - - - message: '#^Class App\\Repository\\ProjectBillingRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectBillingRepository.php - - - - message: '#^Class App\\Repository\\ProjectBillingRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectBillingRepository.php - - - - message: '#^Class App\\Repository\\ProjectBillingRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectBillingRepository.php - - - - message: '#^Class App\\Repository\\ProjectBillingRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectBillingRepository.php - - - - message: '#^Method App\\Repository\\ProjectBillingRepository\:\:getFilteredPagination\(\) return type has no value type specified in iterable type Knp\\Component\\Pager\\Pagination\\PaginationInterface\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectBillingRepository.php - - - - message: '#^Method App\\Repository\\ProjectBillingRepository\:\:getFilteredPagination\(\) return type with generic interface Knp\\Component\\Pager\\Pagination\\PaginationInterface does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Repository/ProjectBillingRepository.php - - - - message: '#^Class App\\Repository\\ProjectRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectRepository.php - - - - message: '#^Class App\\Repository\\ProjectRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectRepository.php - - - - message: '#^Class App\\Repository\\ProjectRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectRepository.php - - - - message: '#^Class App\\Repository\\ProjectRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectRepository.php - - - - message: '#^Method App\\Repository\\ProjectRepository\:\:getFilteredPagination\(\) return type has no value type specified in iterable type Knp\\Component\\Pager\\Pagination\\PaginationInterface\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectRepository.php - - - - message: '#^Method App\\Repository\\ProjectRepository\:\:getFilteredPagination\(\) return type with generic interface Knp\\Component\\Pager\\Pagination\\PaginationInterface does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Repository/ProjectRepository.php - - - - message: '#^Method App\\Repository\\ProjectRepository\:\:getProjectIdsWithCybersecurityAgreement\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectRepository.php - - - - message: '#^Method App\\Repository\\ProjectRepository\:\:getProjectTrackerIdsByDataProviders\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Repository/ProjectRepository.php - - - - message: '#^Method App\\Repository\\ProjectRepository\:\:getProjectTrackerIdsByDataProviders\(\) has parameter \$dataProviders with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectRepository.php - - - - message: '#^Class App\\Repository\\ProjectVersionBudgetRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectVersionBudgetRepository.php - - - - message: '#^Class App\\Repository\\ProjectVersionBudgetRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectVersionBudgetRepository.php - - - - message: '#^Class App\\Repository\\ProjectVersionBudgetRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectVersionBudgetRepository.php - - - - message: '#^Class App\\Repository\\ProjectVersionBudgetRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ProjectVersionBudgetRepository.php - - - - - message: '#^Method App\\Repository\\ServiceAgreementRepository\:\:getFilteredPagination\(\) return type has no value type specified in iterable type Knp\\Component\\Pager\\Pagination\\PaginationInterface\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/ServiceAgreementRepository.php - - - - message: '#^Method App\\Repository\\ServiceAgreementRepository\:\:getFilteredPagination\(\) return type with generic interface Knp\\Component\\Pager\\Pagination\\PaginationInterface does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Repository/ServiceAgreementRepository.php - - - - message: '#^Class App\\Repository\\SubscriptionRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Class App\\Repository\\SubscriptionRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Class App\\Repository\\SubscriptionRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Class App\\Repository\\SubscriptionRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Method App\\Repository\\SubscriptionRepository\:\:findByCustom\(\) has parameter \$email with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Method App\\Repository\\SubscriptionRepository\:\:findByCustom\(\) has parameter \$urlParams with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Method App\\Repository\\SubscriptionRepository\:\:findByCustom\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Method App\\Repository\\SubscriptionRepository\:\:findOneByCustom\(\) has parameter \$email with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Method App\\Repository\\SubscriptionRepository\:\:findOneByCustom\(\) has parameter \$subscriptionType with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Method App\\Repository\\SubscriptionRepository\:\:findOneByCustom\(\) has parameter \$urlParams with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Method App\\Repository\\SubscriptionRepository\:\:getFilteredData\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Property App\\Repository\\SubscriptionRepository\:\:\$paginator is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: src/Repository/SubscriptionRepository.php - - - - message: '#^Class App\\Repository\\UserRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/UserRepository.php - - - - message: '#^Class App\\Repository\\UserRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/UserRepository.php - - - - message: '#^Class App\\Repository\\UserRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/UserRepository.php - - - - message: '#^Class App\\Repository\\UserRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/UserRepository.php - - - - message: '#^Class App\\Repository\\VersionRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/VersionRepository.php - - - - message: '#^Class App\\Repository\\VersionRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/VersionRepository.php - - - - message: '#^Class App\\Repository\\VersionRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/VersionRepository.php - - - - message: '#^Class App\\Repository\\VersionRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/VersionRepository.php - - - - message: '#^Method App\\Repository\\WorkerGroupRepository\:\:getFilteredPagination\(\) return type has no value type specified in iterable type Knp\\Component\\Pager\\Pagination\\PaginationInterface\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorkerGroupRepository.php - - - - message: '#^Method App\\Repository\\WorkerGroupRepository\:\:getFilteredPagination\(\) return type with generic interface Knp\\Component\\Pager\\Pagination\\PaginationInterface does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Repository/WorkerGroupRepository.php - - - - message: '#^Class App\\Repository\\WorkerRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorkerRepository.php - - - - message: '#^Class App\\Repository\\WorkerRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorkerRepository.php - - - - message: '#^Class App\\Repository\\WorkerRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorkerRepository.php - - - - message: '#^Class App\\Repository\\WorkerRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorkerRepository.php - - - - message: '#^Method App\\Repository\\WorkerRepository\:\:findAllIncludedInReports\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorkerRepository.php - - - - message: '#^Class App\\Repository\\WorklogRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorklogRepository.php - - - - message: '#^Class App\\Repository\\WorklogRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorklogRepository.php - - - - message: '#^Class App\\Repository\\WorklogRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorklogRepository.php - - - - message: '#^Class App\\Repository\\WorklogRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorklogRepository.php - - - - message: '#^Method App\\Repository\\WorklogRepository\:\:findBillableWorklogsByWorkerAndDateRange\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorklogRepository.php - - - - message: '#^Method App\\Repository\\WorklogRepository\:\:findBilledWorklogsByWorkerAndDateRange\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Repository/WorklogRepository.php - - - - message: '#^Method App\\Repository\\WorklogRepository\:\:findByFilterData\(\) return type has no value type specified in iterable type iterable\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorklogRepository.php - - - - message: '#^Method App\\Repository\\WorklogRepository\:\:findWorklogsByWorkerAndDateRange\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Repository/WorklogRepository.php - - - - message: '#^Method App\\Repository\\WorklogRepository\:\:getTimeSpentByWorkerInWeekRange\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorklogRepository.php - - - - message: '#^Method App\\Repository\\WorklogRepository\:\:getWorklogsAttachedToInvoiceInDateRange\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Repository/WorklogRepository.php - - - - message: '#^Method App\\Service\\BillingService\:\:exportInvoicesToSpreadsheet\(\) has parameter \$invoiceIds with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/BillingService.php - - - - message: '#^Method App\\Service\\BillingService\:\:generateSpreadsheetCsvResponse\(\) has parameter \$ids with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/BillingService.php - - - - message: '#^Method App\\Service\\BillingService\:\:generateSpreadsheetHtml\(\) has parameter \$ids with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/BillingService.php - - - - message: '#^Method App\\Service\\BillingService\:\:getInvoiceRecordableErrors\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/BillingService.php - - - - message: '#^Method App\\Service\\ClientHelper\:\:__construct\(\) has parameter \$options with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/ClientHelper.php - - - - message: '#^Method App\\Service\\ClientHelper\:\:getStandardPrice\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Service/ClientHelper.php - - - - message: '#^Method App\\Service\\DanishHolidayHelper\:\:__construct\(\) has parameter \$nonWorkdays with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Method App\\Service\\DanishHolidayHelper\:\:buildNames\(\) has parameter \$days with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Method App\\Service\\DanishHolidayHelper\:\:buildNames\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Method App\\Service\\DanishHolidayHelper\:\:getBankHolidayNames\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Method App\\Service\\DanishHolidayHelper\:\:getBankHolidays\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Method App\\Service\\DanishHolidayHelper\:\:getHolidayNames\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Property App\\Service\\DanishHolidayHelper\:\:\$bankHolidayNames type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Property App\\Service\\DanishHolidayHelper\:\:\$bankHolidays type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Property App\\Service\\DanishHolidayHelper\:\:\$holidayNames type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Property App\\Service\\DanishHolidayHelper\:\:\$holidays type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Static property App\\Service\\DanishHolidayHelper\:\:\$instance \(App\\Service\\DanishHolidayHelper\) in empty\(\) is not falsy\.$#' - identifier: empty.property - count: 1 - path: src/Service/DanishHolidayHelper.php - - - - message: '#^Method App\\Service\\DashboardService\:\:getMonthsToDate\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DashboardService.php - - - - message: '#^Method App\\Service\\DashboardService\:\:getWeeksToDate\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DashboardService.php - - - - message: '#^Parameter \#2 \$timestamp of function date expects int\|null, int\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Service/DashboardService.php - - - - message: '#^Parameter \#1 \$timeSpentSeconds of method App\\Entity\\Worklog\:\:setTimeSpentSeconds\(\) expects int, float given\.$#' - identifier: argument.type - count: 1 - path: src/Service/DataProviderService.php - - - - message: '#^Cannot call method format\(\) on DateTime\|false\.$#' - identifier: method.nonObject - count: 1 - path: src/Service/DateTimeHelper.php - - - - message: '#^Match expression does not handle remaining values\: int\\|int\<5, max\>$#' - identifier: match.unhandled - count: 1 - path: src/Service/DateTimeHelper.php - - - - message: '#^Method App\\Service\\DateTimeHelper\:\:getFirstAndLastDateOfMonth\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DateTimeHelper.php - - - - message: '#^Method App\\Service\\DateTimeHelper\:\:getFirstAndLastDateOfQuarter\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DateTimeHelper.php - - - - message: '#^Method App\\Service\\DateTimeHelper\:\:getFirstAndLastDateOfWeek\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DateTimeHelper.php - - - - message: '#^Method App\\Service\\DateTimeHelper\:\:getFirstAndLastDateOfYear\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DateTimeHelper.php - - - - message: '#^Method App\\Service\\DateTimeHelper\:\:getWeeksOfYear\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/DateTimeHelper.php - - - - message: '#^Negated boolean expression is always false\.$#' - identifier: booleanNot.alwaysFalse - count: 1 - path: src/Service/ForecastReportService.php - - - - message: '#^Method App\\Service\\HourReportService\:\:processTimesheetsData\(\) has parameter \$worklogs with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/HourReportService.php - - - - message: '#^Method App\\Service\\HourReportService\:\:processTimesheetsData\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/HourReportService.php - - - - message: '#^PHPDoc tag @var for variable \$tagsIterator contains generic class ArrayIterator but does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Service/HourReportService.php - - - - message: '#^Method App\\Service\\InvoiceEntryHelper\:\:__construct\(\) has parameter \$options with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/InvoiceEntryHelper.php - - - - message: '#^Method App\\Service\\InvoiceEntryHelper\:\:getAccounts\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/InvoiceEntryHelper.php - - - - message: '#^Method App\\Service\\InvoiceEntryHelper\:\:resolveOptions\(\) has parameter \$options with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/InvoiceEntryHelper.php - - - - message: '#^Method App\\Service\\InvoiceEntryHelper\:\:resolveOptions\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/InvoiceEntryHelper.php - - - - message: '#^Property App\\Service\\InvoiceEntryHelper\:\:\$options type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/InvoiceEntryHelper.php - - - - message: '#^Method App\\Service\\InvoicingRateReportService\:\:getDatesOfPeriod\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/InvoicingRateReportService.php - - - - message: '#^Method App\\Service\\InvoicingRateReportService\:\:getPeriods\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/InvoicingRateReportService.php - - - - message: '#^Method App\\Service\\InvoicingRateReportService\:\:getWorklogs\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/InvoicingRateReportService.php - - - - message: '#^Access to an undefined property object\:\:\$description\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$dueDate\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$email\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$hours\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$id\.$#' - identifier: property.notFound - count: 5 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$kind\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$milestoneId\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$modified\.$#' - identifier: property.notFound - count: 4 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$name\.$#' - identifier: property.notFound - count: 4 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$plannedHours\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$projectId\.$#' - identifier: property.notFound - count: 2 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$remainingHours\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$resolutionDate\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$results\.$#' - identifier: property.notFound - count: 2 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$resultsCount\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$status\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$tags\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$ticketId\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$username\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$workDate\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Access to an undefined property object\:\:\$worker\.$#' - identifier: property.notFound - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Match expression does not handle remaining value\: string$#' - identifier: match.unhandled - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Method App\\Service\\LeantimeApiService\:\:fetchFromLeantime\(\) has parameter \$params with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Method App\\Service\\LeantimeApiService\:\:getEnabledLeantimeDataProviders\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Method App\\Service\\LeantimeApiService\:\:post\(\) has parameter \$body with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Method App\\Service\\LeantimeApiService\:\:post\(\) has parameter \$path with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Method App\\Service\\LeantimeApiService\:\:updateAsJob\(\) has parameter \$projectTrackerProjectIds with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/LeantimeApiService.php - - - - message: '#^Method App\\Service\\ManagementReportService\:\:calculateYear\(\) has parameter \$quarterValues with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Service/ManagementReportService.php - - - - message: '#^Method App\\Service\\ManagementReportService\:\:generateSpreadsheetCsvResponse\(\) has parameter \$dateInterval with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Service/ManagementReportService.php - - - - message: '#^Method App\\Service\\ManagementReportService\:\:generateSpreadsheetCsvResponse\(\) has parameter \$groupedInvoices with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/ManagementReportService.php - - - - message: '#^Method App\\Service\\PlanningService\:\:getAssigneeData\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/PlanningService.php - - - - message: '#^Method App\\Service\\PlanningService\:\:getOrCreateAssignee\(\) has parameter \$assigneeData with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/PlanningService.php - - - - message: '#^Method App\\Service\\PlanningService\:\:processIssuesForWeek\(\) has parameter \$issues with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/PlanningService.php - - - - message: '#^Method App\\Service\\PlanningService\:\:sortIssuesByWeek\(\) has parameter \$allIssues with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/PlanningService.php - - - - message: '#^Method App\\Service\\PlanningService\:\:sortIssuesByWeek\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/PlanningService.php - - - - message: '#^PHPDoc tag @var for variable \$iterator contains generic class ArrayIterator but does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 2 - path: src/Service/PlanningService.php - - - - message: '#^Method App\\Service\\ProjectBillingService\:\:getIssuesNotIncludedInProjectBilling\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/ProjectBillingService.php - - - - message: '#^Call to method App\\Repository\\VersionRepository\:\:findOneBy\(\) \- entity App\\Entity\\Version does not have a field named \$versionId\.$#' - identifier: doctrine.findOneByArgument - count: 1 - path: src/Service/SubscriptionHandlerService.php - - - - message: '#^Method App\\Service\\SubscriptionHandlerService\:\:prepareMailData\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/SubscriptionHandlerService.php - - - - message: '#^Method App\\Service\\SubscriptionHandlerService\:\:sendNotification\(\) has parameter \$notification with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/SubscriptionHandlerService.php - - - - message: '#^Unreachable statement \- code above always terminates\.$#' - identifier: deadCode.unreachable - count: 1 - path: src/Service/SubscriptionHandlerService.php - - - - message: '#^Method App\\Service\\WorkloadReportService\:\:getDatesOfPeriod\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/WorkloadReportService.php - - - - message: '#^Method App\\Service\\WorkloadReportService\:\:getPeriods\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/WorkloadReportService.php - - - - message: '#^Method App\\Service\\WorkloadReportService\:\:getWorklogs\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Service/WorkloadReportService.php - - - - message: '#^Call to an undefined method object\:\:findOneBy\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/AbstractControllerTestCase.php - - - - message: '#^Method App\\Tests\\Integration\\Controller\\AbstractControllerTestCase\:\:assertDeniedFor\(\) has parameter \$roles with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Integration/Controller/AbstractControllerTestCase.php - - - - message: '#^Method App\\Tests\\Integration\\Controller\\AbstractControllerTestCase\:\:assertGrantedFor\(\) has parameter \$roles with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Integration/Controller/AbstractControllerTestCase.php - - - - message: '#^Method App\\Tests\\Integration\\Controller\\AbstractControllerTestCase\:\:assertSmokeMatrix\(\) has parameter \$allowedRoles with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Integration/Controller/AbstractControllerTestCase.php - - - - message: '#^Method App\\Tests\\Integration\\Controller\\AbstractControllerTestCase\:\:assertSmokeMatrix\(\) has parameter \$deniedRoles with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Integration/Controller/AbstractControllerTestCase.php - - - - message: '#^Method App\\Tests\\Integration\\Controller\\AbstractControllerTestCase\:\:createClientLoggedInAs\(\) has parameter \$roles with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Integration/Controller/AbstractControllerTestCase.php - - - - message: '#^Call to an undefined method object\:\:getIncluded\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/HourReportFilterTest.php - - - - message: '#^Call to an undefined method object\:\:clear\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Integration/Controller/InvoiceEntryFlowTest.php - - - - message: '#^Call to an undefined method object\:\:find\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Integration/Controller/InvoiceEntryFlowTest.php - - - - message: '#^Call to an undefined method object\:\:flush\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Integration/Controller/InvoiceEntryFlowTest.php - - - - message: '#^Call to an undefined method object\:\:getIncluded\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/InvoiceEntryFlowTest.php - - - - message: '#^Call to an undefined method object\:\:persist\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Integration/Controller/InvoiceEntryFlowTest.php - - - - message: '#^Parameter \#1 \$id of method App\\Tests\\Integration\\Controller\\InvoiceEntryFlowTest\:\:reloadEntry\(\) expects int, int\|null given\.$#' - identifier: argument.type - count: 5 - path: tests/Integration/Controller/InvoiceEntryFlowTest.php - - - - message: '#^Parameter \#1 \$invoiceId of method App\\Tests\\Integration\\Controller\\InvoiceEntryFlowTest\:\:markInvoiceRecorded\(\) expects int, int\|null given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Controller/InvoiceEntryFlowTest.php - - - - message: '#^Call to an undefined method object\:\:findOneBy\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Integration/Controller/InvoiceFlowTest.php - - - - message: '#^Call to an undefined method object\:\:getIncluded\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/InvoiceFlowTest.php - - - - message: '#^Cannot call method getId\(\) on App\\Entity\\Project\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFlowTest.php - - - - message: '#^Parameter \#2 \$string of method PHPUnit\\Framework\\Assert\:\:assertMatchesRegularExpression\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: tests/Integration/Controller/InvoiceFlowTest.php - - - - message: '#^Call to an undefined method object\:\:clear\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Call to an undefined method object\:\:find\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Call to an undefined method object\:\:findBy\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Call to an undefined method object\:\:findOneBy\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Call to an undefined method object\:\:getIncluded\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Cannot call method getAmount\(\) on App\\Entity\\InvoiceEntry\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Cannot call method getExportedDate\(\) on App\\Entity\\Invoice\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Cannot call method getLockedContactName\(\) on App\\Entity\\Invoice\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Cannot call method getLockedCustomerKey\(\) on App\\Entity\\Invoice\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Cannot call method getLockedEan\(\) on App\\Entity\\Invoice\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Cannot call method getLockedType\(\) on App\\Entity\\Invoice\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Cannot call method getRecordedDate\(\) on App\\Entity\\Invoice\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Cannot call method getTotalPrice\(\) on App\\Entity\\InvoiceEntry\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Cannot call method isNoCost\(\) on App\\Entity\\Invoice\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Cannot call method isRecorded\(\) on App\\Entity\\Invoice\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Offset 1 might not exist on array\{\}\|array\{non\-falsy\-string, numeric\-string\}\.$#' - identifier: offsetAccess.notFound - count: 2 - path: tests/Integration/Controller/InvoiceFullFlowTest.php - - - - message: '#^Call to an undefined method object\:\:findOneBy\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/ProjectBillingFlowTest.php - - - - message: '#^Call to an undefined method object\:\:getIncluded\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/ProjectBillingFlowTest.php - - - - message: '#^Cannot call method getId\(\) on App\\Entity\\Project\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/ProjectBillingFlowTest.php - - - - message: '#^Parameter \#2 \$string of method PHPUnit\\Framework\\Assert\:\:assertMatchesRegularExpression\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: tests/Integration/Controller/ProjectBillingFlowTest.php - - - - message: '#^Call to an undefined method object\:\:clear\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/ProjectBillingFullFlowTest.php - - - - message: '#^Call to an undefined method object\:\:find\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/ProjectBillingFullFlowTest.php - - - - message: '#^Call to an undefined method object\:\:findOneBy\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Controller/ProjectBillingFullFlowTest.php - - - - message: '#^Cannot call method getDescription\(\) on App\\Entity\\ProjectBilling\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/ProjectBillingFullFlowTest.php - - - - message: '#^Cannot call method getExportedDate\(\) on App\\Entity\\ProjectBilling\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/ProjectBillingFullFlowTest.php - - - - message: '#^Cannot call method getInvoices\(\) on App\\Entity\\ProjectBilling\|null\.$#' - identifier: method.nonObject - count: 4 - path: tests/Integration/Controller/ProjectBillingFullFlowTest.php - - - - message: '#^Cannot call method getName\(\) on App\\Entity\\ProjectBilling\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/ProjectBillingFullFlowTest.php - - - - message: '#^Cannot call method isRecorded\(\) on App\\Entity\\ProjectBilling\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Controller/ProjectBillingFullFlowTest.php - - - - message: '#^Offset 1 might not exist on array\{\}\|array\{non\-falsy\-string, numeric\-string\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: tests/Integration/Controller/ProjectBillingFullFlowTest.php - - - - message: '#^You should use assertCount\(\$expectedCount, \$variable\) instead of assertSame\(\$expectedCount, \$variable\-\>count\(\)\)\.$#' - identifier: phpunit.assertCount - count: 1 - path: tests/Integration/Controller/ProjectBillingFullFlowTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Knp\\\\Component\\\\Pager\\\\Pagination\\\\PaginationInterface'' and Knp\\Component\\Pager\\Pagination\\PaginationInterface will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/AccountRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/AccountRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\AccountRepositoryTest\:\:\$repository \(App\\Repository\\AccountRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/AccountRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Knp\\\\Component\\\\Pager\\\\Pagination\\\\PaginationInterface'' and Knp\\Component\\Pager\\Pagination\\PaginationInterface will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/ClientRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\ClientRepositoryTest\:\:\$repository \(App\\Repository\\ClientRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/ClientRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Entity\\\\CybersecurityAgreement'' and App\\Entity\\CybersecurityAgreement will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/CybersecurityAgreementRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with non\-empty\-array\ will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/CybersecurityAgreementRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\CybersecurityAgreementRepositoryTest\:\:\$repository \(App\\Repository\\CybersecurityAgreementRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/CybersecurityAgreementRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Knp\\\\Component\\\\Pager\\\\Pagination\\\\PaginationInterface'' and Knp\\Component\\Pager\\Pagination\\PaginationInterface will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/InvoiceRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\InvoiceRepositoryTest\:\:\$repository \(App\\Repository\\InvoiceRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/InvoiceRepositoryTest.php - - - - message: '#^Call to an undefined method object\:\:findOneBy\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Integration/Repository/IssueRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Knp\\\\Component\\\\Pager\\\\Pagination\\\\PaginationInterface'' and Knp\\Component\\Pager\\Pagination\\PaginationInterface will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/IssueRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/IssueRepositoryTest.php - - - - message: '#^Cannot call method getId\(\) on App\\Entity\\Project\|null\.$#' - identifier: method.nonObject - count: 2 - path: tests/Integration/Repository/IssueRepositoryTest.php - - - - message: '#^Parameter \#1 \$project of method App\\Repository\\IssueRepository\:\:findEpicOptionsByProject\(\) expects App\\Entity\\Project, App\\Entity\\Project\|null given\.$#' - identifier: argument.type - count: 1 - path: tests/Integration/Repository/IssueRepositoryTest.php - - - - message: '#^Parameter \#1 \$project of method App\\Repository\\IssueRepository\:\:getClosedIssuesFromInterval\(\) expects App\\Entity\\Project, App\\Entity\\Project\|null given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Repository/IssueRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\IssueRepositoryTest\:\:\$entityManager \(Doctrine\\ORM\\EntityManagerInterface\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/IssueRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\IssueRepositoryTest\:\:\$entityManager is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: tests/Integration/Repository/IssueRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\IssueRepositoryTest\:\:\$projectRepository \(App\\Repository\\ProjectRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/IssueRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\IssueRepositoryTest\:\:\$repository \(App\\Repository\\IssueRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/IssueRepositoryTest.php - - - - message: '#^Call to an undefined method object\:\:findOneBy\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Repository/ProductRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Knp\\\\Component\\\\Pager\\\\Pagination\\\\PaginationInterface'' and Knp\\Component\\Pager\\Pagination\\PaginationInterface will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/ProductRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\ProductRepositoryTest\:\:\$repository \(App\\Repository\\ProductRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/ProductRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Knp\\\\Component\\\\Pager\\\\Pagination\\\\PaginationInterface'' and Knp\\Component\\Pager\\Pagination\\PaginationInterface will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/ProjectBillingRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\ProjectBillingRepositoryTest\:\:\$repository \(App\\Repository\\ProjectBillingRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/ProjectBillingRepositoryTest.php - - - - message: '#^Call to an undefined method object\:\:findAll\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Repository/ProjectRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Doctrine\\\\ORM\\\\QueryBuilder'' and Doctrine\\ORM\\QueryBuilder will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/ProjectRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Knp\\\\Component\\\\Pager\\\\Pagination\\\\PaginationInterface'' and Knp\\Component\\Pager\\Pagination\\PaginationInterface will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/ProjectRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/ProjectRepositoryTest.php - - - - message: '#^Cannot call method getId\(\) on App\\Entity\\Project\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Repository/ProjectRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\ProjectRepositoryTest\:\:\$entityManager \(Doctrine\\ORM\\EntityManagerInterface\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/ProjectRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\ProjectRepositoryTest\:\:\$entityManager is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: tests/Integration/Repository/ProjectRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\ProjectRepositoryTest\:\:\$repository \(App\\Repository\\ProjectRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/ProjectRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Knp\\\\Component\\\\Pager\\\\Pagination\\\\PaginationInterface'' and Knp\\Component\\Pager\\Pagination\\PaginationInterface will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/ServiceAgreementRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\ServiceAgreementRepositoryTest\:\:\$repository \(App\\Repository\\ServiceAgreementRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/ServiceAgreementRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\SubscriptionRepositoryTest\:\:\$repository \(App\\Repository\\SubscriptionRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/SubscriptionRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Knp\\\\Component\\\\Pager\\\\Pagination\\\\PaginationInterface'' and Knp\\Component\\Pager\\Pagination\\PaginationInterface will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/WorkerGroupRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\WorkerGroupRepositoryTest\:\:\$repository \(App\\Repository\\WorkerGroupRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/WorkerGroupRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\WorkerRepositoryTest\:\:\$entityManager \(Doctrine\\ORM\\EntityManagerInterface\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/WorkerRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\WorkerRepositoryTest\:\:\$repository \(App\\Repository\\WorkerRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/WorkerRepositoryTest.php - - - - message: '#^Call to an undefined method object\:\:findOneBy\(\)\.$#' - identifier: method.notFound - count: 8 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Entity\\\\Worklog'' and App\\Entity\\Worklog will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Cannot call method format\(\) on DateTimeInterface\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Cannot call method getId\(\) on App\\Entity\\Issue\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Cannot call method getTimestamp\(\) on DateTimeInterface\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Parameter \#1 \$project of method App\\Repository\\WorklogRepository\:\:findByFilterData\(\) expects App\\Entity\\Project, App\\Entity\\Project\|null given\.$#' - identifier: argument.type - count: 5 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\WorklogRepositoryTest\:\:\$entityManager \(Doctrine\\ORM\\EntityManagerInterface\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\WorklogRepositoryTest\:\:\$entityManager is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\WorklogRepositoryTest\:\:\$projectRepository \(App\\Repository\\ProjectRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Property App\\Tests\\Integration\\Repository\\WorklogRepositoryTest\:\:\$repository \(App\\Repository\\WorklogRepository\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Repository/WorklogRepositoryTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\BillableUnbilledHoursReportData'' and App\\Model\\Reports\\BillableUnbilledHoursReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Service/BillableUnbilledHoursReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\CybersecurityReportData'' and App\\Model\\Reports\\CybersecurityReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Service/CybersecurityReportServiceTest.php - - - - message: '#^Property App\\Tests\\Integration\\Service\\CybersecurityReportServiceTest\:\:\$service \(App\\Service\\CybersecurityReportService\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Integration/Service/CybersecurityReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\ForecastReportData'' and App\\Model\\Reports\\ForecastReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Service/ForecastReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\HourReportData'' and App\\Model\\Reports\\HourReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Service/HourReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\HourReportProjectTag'' and App\\Model\\Reports\\HourReportProjectTag will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 2 - path: tests/Integration/Service/HourReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\InvoicingRateReportData'' and App\\Model\\Reports\\InvoicingRateReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Service/InvoicingRateReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\InvoicingRateReportWorker'' and App\\Model\\Reports\\InvoicingRateReportWorker will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Service/InvoicingRateReportServiceTest.php - - - - message: '#^You should use assertCount\(\$expectedCount, \$variable\) instead of assertSame\(\$expectedCount, \$variable\-\>count\(\)\)\.$#' - identifier: phpunit.assertCount - count: 1 - path: tests/Integration/Service/InvoicingRateReportServiceTest.php - - - - message: '#^Call to an undefined method object\:\:clear\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Call to an undefined method object\:\:find\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Call to an undefined method object\:\:findAll\(\)\.$#' - identifier: method.notFound - count: 24 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Call to an undefined method object\:\:findOneBy\(\)\.$#' - identifier: method.notFound - count: 8 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Call to an undefined method object\:\:flush\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Call to an undefined method object\:\:persist\(\)\.$#' - identifier: method.notFound - count: 12 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Method App\\Tests\\Integration\\Service\\LeantimeApiServiceTest\:\:getMilestones\(\) has parameter \$modifiedYear with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Method App\\Tests\\Integration\\Service\\LeantimeApiServiceTest\:\:getProjects\(\) has parameter \$modifiedYear with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Method App\\Tests\\Integration\\Service\\LeantimeApiServiceTest\:\:getTickets\(\) has parameter \$modifiedYear with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Method App\\Tests\\Integration\\Service\\LeantimeApiServiceTest\:\:getTimesheets\(\) has parameter \$modifiedYear with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#1 \$dataProviderId of method App\\Service\\LeantimeApiService\:\:deleteAsJob\(\) expects int, int\|null given\.$#' - identifier: argument.type - count: 1 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#1 \$projectTrackerId of method App\\Entity\\Issue\:\:setProjectTrackerId\(\) expects string, int given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#1 \$projectTrackerId of method App\\Entity\\Project\:\:setProjectTrackerId\(\) expects string\|null, int given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#1 \$projectTrackerId of method App\\Entity\\Version\:\:setProjectTrackerId\(\) expects string, int given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#1 \$projectTrackerIssueId of method App\\Entity\\Worklog\:\:setProjectTrackerIssueId\(\) expects string, int given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#1 \$projectTrackerKey of method App\\Entity\\Issue\:\:setProjectTrackerKey\(\) expects string, int given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#1 \$projectTrackerKey of method App\\Entity\\Project\:\:setProjectTrackerKey\(\) expects string\|null, int given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#1 \$started of method App\\Entity\\Worklog\:\:setStarted\(\) expects DateTimeInterface, DateTime\|false given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#2 \$messageBus of class App\\Service\\LeantimeApiService constructor expects Symfony\\Component\\Messenger\\MessageBusInterface, object given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#3 \$dataProviderRepository of class App\\Service\\LeantimeApiService constructor expects App\\Repository\\DataProviderRepository, object given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#4 \$dataProviderId of method App\\Service\\LeantimeApiService\:\:updateAsJob\(\) expects int, int\|null given\.$#' - identifier: argument.type - count: 8 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#4 \$entityManager of class App\\Service\\LeantimeApiService constructor expects Doctrine\\ORM\\EntityManagerInterface, object given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Parameter \#5 \$projectRepository of class App\\Service\\LeantimeApiService constructor expects App\\Repository\\ProjectRepository, object given\.$#' - identifier: argument.type - count: 2 - path: tests/Integration/Service/LeantimeApiServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\BillingService\:\:createProjectBilling\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\BillingService\:\:getIssuesNotIncludedInProjectBilling\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method object\:\:exportInvoicesToSpreadsheet\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method object\:\:getClosedIssuesFromInterval\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Integration/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\WorkloadReportData'' and App\\Model\\Reports\\WorkloadReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Service/WorkloadReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\WorkloadReportWorker'' and App\\Model\\Reports\\WorkloadReportWorker will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Integration/Service/WorkloadReportServiceTest.php - - - - message: '#^You should use assertCount\(\$expectedCount, \$variable\) instead of assertSame\(\$expectedCount, \$variable\-\>count\(\)\)\.$#' - identifier: phpunit.assertCount - count: 1 - path: tests/Integration/Service/WorkloadReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\SubscriptionRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 7 - path: tests/Unit/Command/SubscriptionHandlerCommandTest.php - - - - message: '#^Call to an undefined method App\\Service\\SubscriptionHandlerService\:\:expects\(\)\.$#' - identifier: method.notFound - count: 5 - path: tests/Unit/Command/SubscriptionHandlerCommandTest.php - - - - message: '#^Call to an undefined method App\\Service\\SubscriptionHandlerService\:\:method\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Command/SubscriptionHandlerCommandTest.php - - - - message: '#^Call to an undefined method Psr\\Log\\LoggerInterface\:\:expects\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Unit/Command/SubscriptionHandlerCommandTest.php - - - - message: '#^Call to an undefined method App\\Service\\LeantimeApiService\:\:expects\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Unit/Command/SyncCommandTest.php - - - - message: '#^Call to an undefined method Symfony\\Contracts\\HttpClient\\HttpClientInterface\:\:expects\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Command/SyncCommandTest.php - - - - message: '#^Call to an undefined method App\\Service\\LeantimeApiService\:\:expects\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Unit/Command/SyncDeletedCommandTest.php - - - - message: '#^Call to an undefined method App\\Service\\LeantimeApiService\:\:expects\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Unit/Command/SyncModifiedCommandTest.php - - - - message: '#^Call to an undefined method App\\Service\\DataProviderService\:\:expects\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/MessageHandler/EntityRemovedFromDataProviderHandlerTest.php - - - - message: '#^Call to an undefined method App\\Service\\DataProviderService\:\:method\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/MessageHandler/EntityRemovedFromDataProviderHandlerTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorkerRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/BillableUnbilledHoursReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorklogRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/Service/BillableUnbilledHoursReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\DateTimeHelper\:\:expects\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/Service/BillableUnbilledHoursReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\DateTimeHelper\:\:method\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Unit/Service/BillableUnbilledHoursReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\BillableUnbilledHoursReportData'' and App\\Model\\Reports\\BillableUnbilledHoursReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 2 - path: tests/Unit/Service/BillableUnbilledHoursReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\InvoiceEntryRepository\:\:expects\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Unit/Service/BillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\InvoiceRepository\:\:expects\(\)\.$#' - identifier: method.notFound - count: 5 - path: tests/Unit/Service/BillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\InvoiceRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 10 - path: tests/Unit/Service/BillingServiceTest.php - - - - message: '#^Parameter \#2 \$haystack of method PHPUnit\\Framework\\Assert\:\:assertStringContainsString\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: tests/Unit/Service/BillingServiceTest.php - - - - message: '#^Parameter \#2 \$haystack of method PHPUnit\\Framework\\Assert\:\:assertStringContainsString\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 2 - path: tests/Unit/Service/BillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\IssueRepository\:\:expects\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/CybersecurityReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\IssueRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Unit/Service/CybersecurityReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\ProjectRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/Service/CybersecurityReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorklogRepository\:\:expects\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/CybersecurityReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorklogRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Unit/Service/CybersecurityReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\CybersecurityReportData'' and App\\Model\\Reports\\CybersecurityReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Unit/Service/CybersecurityReportServiceTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:assertSameDate\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:dataEaster\(\) return type has no value type specified in iterable type iterable\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:dataIsBankHoliday\(\) return type has no value type specified in iterable type iterable\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:dataNextBankDay\(\) return type has no value type specified in iterable type iterable\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:dataNextBankDay30\(\) return type has no value type specified in iterable type iterable\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:dataNextNonHoliday\(\) return type has no value type specified in iterable type iterable\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:testEaster\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:testHmm\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:testHolidayNames\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:testIsBankHoliday\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:testNextBankDay\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:testNextBankDay30\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:testNextNonHoliday\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Parameter \#1 \$expected of method App\\Tests\\Unit\\Service\\DanishHolidayHelperTest\:\:assertSameDate\(\) expects DateTimeInterface, DateTimeInterface\|null given\.$#' - identifier: argument.type - count: 3 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Result of method PHPUnit\\Framework\\Assert\:\:assertEquals\(\) \(void\) is used\.$#' - identifier: method.void - count: 1 - path: tests/Unit/Service/DanishHolidayHelperTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorkerRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/Service/DashboardServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorklogRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Unit/Service/DashboardServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\DateTimeHelper\:\:method\(\)\.$#' - identifier: method.notFound - count: 6 - path: tests/Unit/Service/DashboardServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsFloat\(\) with float will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Unit/Service/DashboardServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\EpicRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/DataProviderServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\IssueRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 7 - path: tests/Unit/Service/DataProviderServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\ProjectRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 12 - path: tests/Unit/Service/DataProviderServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\VersionRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Unit/Service/DataProviderServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorkerRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Unit/Service/DataProviderServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorklogRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 5 - path: tests/Unit/Service/DataProviderServiceTest.php - - - - message: '#^Call to an undefined method Doctrine\\ORM\\EntityManagerInterface\:\:expects\(\)\.$#' - identifier: method.notFound - count: 34 - path: tests/Unit/Service/DataProviderServiceTest.php - - - - message: '#^Call to an undefined method Doctrine\\ORM\\EntityManagerInterface\:\:method\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/DataProviderServiceTest.php - - - - message: '#^Call to an undefined method Psr\\Log\\LoggerInterface\:\:expects\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Unit/Service/DataProviderServiceTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DateTimeHelperTest\:\:monthNameDataProvider\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DateTimeHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DateTimeHelperTest\:\:monthYearProvider\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DateTimeHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DateTimeHelperTest\:\:testGetFirstAndLastDateOfMonth\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DateTimeHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DateTimeHelperTest\:\:testGetFirstAndLastDateOfWeek\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DateTimeHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DateTimeHelperTest\:\:testGetFirstAndLastDateOfYear\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DateTimeHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DateTimeHelperTest\:\:testGetWeeksOfYear\(\) has parameter \$expected with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DateTimeHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DateTimeHelperTest\:\:weekYearProvider\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DateTimeHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DateTimeHelperTest\:\:weeksOfYearProvider\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DateTimeHelperTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\DateTimeHelperTest\:\:yearProvider\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Unit/Service/DateTimeHelperTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorkerRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/Service/ForecastReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorklogRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/Service/ForecastReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\ForecastReportData'' and App\\Model\\Reports\\ForecastReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Unit/Service/ForecastReportServiceTest.php - - - - message: '#^Cannot access property \$invoiced on App\\Model\\Reports\\ForecastReportProjectData\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/Unit/Service/ForecastReportServiceTest.php - - - - message: '#^Cannot access property \$issues on App\\Model\\Reports\\ForecastReportProjectData\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/Unit/Service/ForecastReportServiceTest.php - - - - message: '#^Offset 1 does not exist on array\\.$#' - identifier: offsetAccess.notFound - count: 1 - path: tests/Unit/Service/ForecastReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\IssueRepository\:\:expects\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/Service/HourReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\IssueRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 6 - path: tests/Unit/Service/HourReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorklogRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 6 - path: tests/Unit/Service/HourReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\HourReportData'' and App\\Model\\Reports\\HourReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 2 - path: tests/Unit/Service/HourReportServiceTest.php - - - - message: '#^Cannot access property \$projectTickets on App\\Model\\Reports\\HourReportProjectTag\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/Unit/Service/HourReportServiceTest.php - - - - message: '#^Cannot access property \$tag on App\\Model\\Reports\\HourReportProjectTag\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/Unit/Service/HourReportServiceTest.php - - - - message: '#^Cannot access property \$totalEstimated on App\\Model\\Reports\\HourReportProjectTag\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/Unit/Service/HourReportServiceTest.php - - - - message: '#^Cannot access property \$totalSpent on App\\Model\\Reports\\HourReportProjectTag\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/Unit/Service/HourReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorkerRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 5 - path: tests/Unit/Service/InvoicingRateReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorklogRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Unit/Service/InvoicingRateReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\DateTimeHelper\:\:method\(\)\.$#' - identifier: method.notFound - count: 7 - path: tests/Unit/Service/InvoicingRateReportServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\InvoicingRateReportData'' and App\\Model\\Reports\\InvoicingRateReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 2 - path: tests/Unit/Service/InvoicingRateReportServiceTest.php - - - - message: '#^Cannot access property \$average on App\\Model\\Reports\\InvoicingRateReportWorker\|false\.$#' - identifier: property.nonObject - count: 1 - path: tests/Unit/Service/InvoicingRateReportServiceTest.php - - - - message: '#^Parameter \#2 \$timestamp of function date expects int\|null, int\|false given\.$#' - identifier: argument.type - count: 2 - path: tests/Unit/Service/InvoicingRateReportServiceTest.php - - - - message: '#^Parameter \#2 \$haystack of method PHPUnit\\Framework\\Assert\:\:assertStringContainsString\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 2 - path: tests/Unit/Service/ManagementReportServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\IssueRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 7 - path: tests/Unit/Service/PlanningServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\ProjectRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/PlanningServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\WorkerRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 6 - path: tests/Unit/Service/PlanningServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\DateTimeHelper\:\:method\(\)\.$#' - identifier: method.notFound - count: 7 - path: tests/Unit/Service/PlanningServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Planning\\\\PlanningData'' and App\\Model\\Planning\\PlanningData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Unit/Service/PlanningServiceTest.php - - - - message: '#^Cannot access property \$displayName on App\\Model\\Planning\\Assignee\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/Unit/Service/PlanningServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\ClientRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Unit/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\IssueRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 10 - path: tests/Unit/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\ProjectBillingRepository\:\:expects\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\ProjectBillingRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 9 - path: tests/Unit/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\BillingService\:\:expects\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\BillingService\:\:method\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\ClientHelper\:\:method\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Unit/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method Doctrine\\ORM\\EntityManagerInterface\:\:expects\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method Doctrine\\ORM\\EntityManagerInterface\:\:method\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Unit/Service/ProjectBillingServiceTest.php - - - - message: '#^Strict comparison using \=\=\= between ''Widget'' and bool will always evaluate to false\.$#' - identifier: identical.alwaysFalse - count: 1 - path: tests/Unit/Service/ProjectBillingServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\ProjectRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 6 - path: tests/Unit/Service/SubscriptionHandlerServiceTest.php - - - - message: '#^Call to an undefined method App\\Repository\\VersionRepository\:\:method\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/SubscriptionHandlerServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\HourReportService\:\:expects\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/Service/SubscriptionHandlerServiceTest.php - - - - message: '#^Call to an undefined method App\\Service\\HourReportService\:\:method\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/Service/SubscriptionHandlerServiceTest.php - - - - message: '#^Call to an undefined method Doctrine\\ORM\\EntityManagerInterface\:\:expects\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Unit/Service/SubscriptionHandlerServiceTest.php - - - - message: '#^Call to an undefined method Symfony\\Component\\Mailer\\MailerInterface\:\:expects\(\)\.$#' - identifier: method.notFound - count: 2 - path: tests/Unit/Service/SubscriptionHandlerServiceTest.php - - - - message: '#^Call to an undefined method Symfony\\Contracts\\Translation\\TranslatorInterface\:\:method\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Unit/Service/SubscriptionHandlerServiceTest.php - - - - message: '#^Call to an undefined method Twig\\Environment\:\:method\(\)\.$#' - identifier: method.notFound - count: 5 - path: tests/Unit/Service/SubscriptionHandlerServiceTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''App\\\\Model\\\\Reports\\\\WorkloadReportData'' and App\\Model\\Reports\\WorkloadReportData will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 3 - path: tests/Unit/Service/WorkloadReportServiceTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\WorkloadReportServiceTest\:\:testExceptionIsThrownWhenWorkerIdentifierIsEmpty\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/WorkloadReportServiceTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\WorkloadReportServiceTest\:\:testExceptionIsThrownWhenWorkerWorkloadIsUnset\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/WorkloadReportServiceTest.php - - - - message: '#^Method App\\Tests\\Unit\\Service\\WorkloadReportServiceTest\:\:testGetWorkloadReport\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Unit/Service/WorkloadReportServiceTest.php - - - - message: '#^Parameter \#2 \$timestamp of function date expects int\|null, int\|false given\.$#' - identifier: argument.type - count: 3 - path: tests/Unit/Service/WorkloadReportServiceTest.php + ignoreErrors: [] diff --git a/src/Command/ProductsImportCommand.php b/src/Command/ProductsImportCommand.php index 31c95164..dc673e28 100644 --- a/src/Command/ProductsImportCommand.php +++ b/src/Command/ProductsImportCommand.php @@ -60,9 +60,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int foreach ($reader->getSheetIterator() as $sheet) { $headers = null; foreach ($sheet->getRowIterator() as $row) { - if (null === $row) { - continue; - } if (null === $headers) { $headers = $getRowAsStrings($row); if (!in_array($headerName, $headers)) { diff --git a/src/Command/SubscriptionHandlerCommand.php b/src/Command/SubscriptionHandlerCommand.php index 64d78a4d..6578060a 100644 --- a/src/Command/SubscriptionHandlerCommand.php +++ b/src/Command/SubscriptionHandlerCommand.php @@ -121,7 +121,7 @@ private function handleSubscription(Subscription $subscription, \DateTime $fromD * * @param \DateTime $dateNow the current date * - * @return array an array containing the start and end dates of the last quarter + * @return array{fromDate: \DateTime, toDate: \DateTime} an array containing the start and end dates of the last quarter */ private function getLastQuarter(\DateTime $dateNow): array { diff --git a/src/Controller/InvoiceController.php b/src/Controller/InvoiceController.php index 9ec40cdc..8e7024a0 100644 --- a/src/Controller/InvoiceController.php +++ b/src/Controller/InvoiceController.php @@ -201,7 +201,7 @@ public function edit(Request $request, Invoice $invoice, InvoiceRepository $invo } #[Route('/{id}/generate-description', name: 'app_invoices_generate_description', methods: ['GET'])] - public function generateDescription(Invoice $invoice, $defaultInvoiceDescriptionTemplate): JsonResponse + public function generateDescription(Invoice $invoice, string $defaultInvoiceDescriptionTemplate): JsonResponse { $projectLeadName = $invoice->getProject()?->getProjectLeadName() ?? null; $projectLeadMail = $invoice->getProject()?->getProjectLeadMail() ?? null; @@ -289,7 +289,9 @@ public function record(Request $request, Invoice $invoice): Response #[Route('/{id}/show-export', name: 'app_invoices_show_export', methods: ['GET'])] public function showExport(Request $request, Invoice $invoice): Response { - $html = $this->billingService->generateSpreadsheetHtml([$invoice->getId()]); + $invoiceId = $invoice->getId(); + \assert(null !== $invoiceId); + $html = $this->billingService->generateSpreadsheetHtml([$invoiceId]); return $this->render('invoices/export_show.html.twig', [ 'invoice' => $invoice, @@ -317,7 +319,10 @@ public function export(Invoice $invoice, InvoiceRepository $invoiceRepository): $invoice->setExportedDate(new \DateTime()); $invoiceRepository->save($invoice, true); - return $this->billingService->generateSpreadsheetCsvResponse([$invoice->getId()]); + $invoiceId = $invoice->getId(); + \assert(null !== $invoiceId); + + return $this->billingService->generateSpreadsheetCsvResponse([$invoiceId]); } /** @@ -358,6 +363,6 @@ public function exportSelection(Request $request, InvoiceRepository $invoiceRepo $entityManager->flush(); - return $this->billingService->generateSpreadsheetCsvResponse($ids); + return $this->billingService->generateSpreadsheetCsvResponse(array_map(intval(...), $ids)); } } diff --git a/src/Controller/IssueController.php b/src/Controller/IssueController.php index 372796b9..02afef50 100644 --- a/src/Controller/IssueController.php +++ b/src/Controller/IssueController.php @@ -21,6 +21,9 @@ #[IsGranted('ROLE_PRODUCT_MANAGER')] class IssueController extends AbstractController { + /** + * @param array $options + */ public function __construct( private readonly array $options, ) { diff --git a/src/Controller/ManagementReportController.php b/src/Controller/ManagementReportController.php index f6232365..ad820384 100644 --- a/src/Controller/ManagementReportController.php +++ b/src/Controller/ManagementReportController.php @@ -88,15 +88,23 @@ public function export(Request $request, ManagementReportService $managementRepo return $managementReportService->generateSpreadsheetCsvResponse($this->createGroupedInvoices($invoices), $dateInterval); } + /** + * @param array $invoices + * + * @return array> + */ private function createGroupedInvoices(array $invoices): array { $groupedInvoices = []; foreach ($invoices as $invoice) { $recordedDate = $invoice->getRecordedDate(); - $year = $recordedDate->format('Y'); - $month = $recordedDate->format('n'); - $yearQuarter = ceil($month / 3); - $groupedInvoices[$year][(int) $yearQuarter][] = $invoice; + if (null === $recordedDate) { + continue; + } + $year = (int) $recordedDate->format('Y'); + $month = (int) $recordedDate->format('n'); + $yearQuarter = (int) ceil($month / 3); + $groupedInvoices[$year][$yearQuarter][] = $invoice; } foreach ($groupedInvoices as $year => $quarters) { @@ -123,10 +131,14 @@ private function createGroupedInvoices(array $invoices): array } /** + * @param array{dateFrom: string, dateTo: string} $dateInterval + * + * @return array + * * @throws \Doctrine\ORM\Exception\NotSupported * @throws \Exception */ - private function getInvoicesDataFromDates($dateInterval, InvoiceRepository $invoiceRepository): array + private function getInvoicesDataFromDates(array $dateInterval, InvoiceRepository $invoiceRepository): array { return $invoiceRepository->getByRecordedDateBetween( new \DateTime($dateInterval['dateFrom']), diff --git a/src/Controller/OpenIdConnectController.php b/src/Controller/OpenIdConnectController.php index c66e40d8..0dd356ae 100644 --- a/src/Controller/OpenIdConnectController.php +++ b/src/Controller/OpenIdConnectController.php @@ -18,7 +18,7 @@ public function generic(): RedirectResponse * @throws \Exception */ #[Route('/logout', name: 'app_logout', methods: ['GET'])] - public function logout() + public function logout(): never { // controller can be blank: it will never be called! throw new \Exception('Don\'t forget to activate logout in security.yaml'); diff --git a/src/Controller/PlanningController.php b/src/Controller/PlanningController.php index 29899a98..6dd32752 100644 --- a/src/Controller/PlanningController.php +++ b/src/Controller/PlanningController.php @@ -112,6 +112,9 @@ public function syncAllIssues(Request $request, LeantimeApiService $leantimeApiS return new Response('Sync done.', 200); } + /** + * @param array $data + */ private function createResponse(string $mode, array $data): Response { return $this->render('planning/planning.html.twig', [ @@ -124,6 +127,8 @@ private function createResponse(string $mode, array $data): Response } /** + * @return array + * * @throws \Exception */ private function preparePlanningData(Request $request, bool $holidayPlanning = false): array diff --git a/src/Controller/ProjectBillingController.php b/src/Controller/ProjectBillingController.php index 88e3e06c..7fa30ee3 100644 --- a/src/Controller/ProjectBillingController.php +++ b/src/Controller/ProjectBillingController.php @@ -195,9 +195,10 @@ public function record(Request $request, ProjectBilling $projectBilling, Project #[Route('/{id}/show-export', name: 'app_project_billing_show_export', methods: ['GET'])] public function showExport(Request $request, ProjectBilling $projectBilling, BillingService $billingService): Response { - $ids = array_map(function ($invoice) { - return $invoice->getId(); - }, $projectBilling->getInvoices()->toArray()); + $ids = array_values(array_filter(array_map( + fn (Invoice $invoice) => $invoice->getId(), + $projectBilling->getInvoices()->toArray() + ), fn (?int $id) => null !== $id)); $html = $billingService->generateSpreadsheetHtml($ids); @@ -232,9 +233,10 @@ public function export(Request $request, ProjectBilling $projectBilling, Invoice $projectBilling->setExportedDate(new \DateTime()); $projectBillingRepository->save($projectBilling, true); - $ids = array_map(function ($invoice) { - return $invoice->getId(); - }, $invoices->toArray()); + $ids = array_values(array_filter(array_map( + fn (Invoice $invoice) => $invoice->getId(), + $invoices->toArray() + ), fn (?int $id) => null !== $id)); return $billingService->generateSpreadsheetCsvResponse($ids); } diff --git a/src/Controller/SubscriptionController.php b/src/Controller/SubscriptionController.php index bbdef971..080ca028 100644 --- a/src/Controller/SubscriptionController.php +++ b/src/Controller/SubscriptionController.php @@ -76,6 +76,9 @@ public function check(User $user, Request $request): Response { $content = $request->toArray(); $userEmail = $user->getEmail(); + if (null === $userEmail) { + return new JsonResponse(['error' => 'User email is required.'], Response::HTTP_BAD_REQUEST); + } $reportType = key($content); $report = &$content[$reportType]; switch ($reportType) { @@ -122,11 +125,16 @@ public function check(User $user, Request $request): Response } /** + * @param array $content + * * @throws NonUniqueResultException */ - private function subscriptionHandler($userEmail, $subscriptionType, $content): JsonResponse + private function subscriptionHandler(string $userEmail, string $subscriptionType, array $content): JsonResponse { $reportType = key($content); + if (null === $reportType) { + throw new \InvalidArgumentException('Content is empty.'); + } $subscription = $this->subscriptionRepository->findOneByCustom($userEmail, $subscriptionType, $content); if ($subscription) { @@ -151,11 +159,18 @@ private function subscriptionHandler($userEmail, $subscriptionType, $content): J return new JsonResponse(['action' => 'subscribed', 'frequencies' => $frequencies], 200); } + /** + * @param array $subscriptions + */ private function getFrequencies(array $subscriptions): string { $frequencies = []; foreach ($subscriptions as $subscription) { - $frequencies[] = $subscription->getFrequency()->value; + $frequency = $subscription->getFrequency(); + if (null === $frequency) { + continue; + } + $frequencies[] = $frequency->value; } // Getting the order from Enum diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php index 160b77dd..2f8b8791 100644 --- a/src/DataFixtures/AppFixtures.php +++ b/src/DataFixtures/AppFixtures.php @@ -57,7 +57,9 @@ public function load(ObjectManager $manager): void for ($k = 0; $k < 100; ++$k) { $modMonth = str_pad((string) ($k % 12 + 1), 2, '0', STR_PAD_LEFT); $modDay = str_pad((string) ($k % 28 + 1), 2, '0', STR_PAD_LEFT); - $startedByK[$k] = \DateTime::createFromFormat('U', (string) strtotime("$year-$modMonth-$modDay"), new \DateTimeZone('Europe/Copenhagen')); + $started = \DateTime::createFromFormat('U', (string) strtotime("$year-$modMonth-$modDay"), new \DateTimeZone('Europe/Copenhagen')); + \assert($started instanceof \DateTime); + $startedByK[$k] = $started; } $dataProviders = []; diff --git a/src/Entity/Account.php b/src/Entity/Account.php index 1f895c6d..3e22f3e7 100644 --- a/src/Entity/Account.php +++ b/src/Entity/Account.php @@ -12,10 +12,10 @@ class Account extends AbstractBaseEntity use DataProviderTrait; #[ORM\Column(length: 255)] - private ?string $name = null; + private string $name = ''; #[ORM\Column(length: 255)] - private ?string $value = null; + private string $value = ''; #[ORM\Column(length: 255, nullable: true)] private ?string $projectTrackerId = null; diff --git a/src/Entity/Client.php b/src/Entity/Client.php index c83f29cf..64bd2fe9 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -19,7 +19,7 @@ class Client extends AbstractBaseEntity use SoftDeleteableEntity; #[ORM\Column(length: 255)] - private ?string $name = null; + private string $name = ''; #[ORM\Column(length: 255, nullable: true)] private ?string $contact = null; @@ -36,9 +36,11 @@ class Client extends AbstractBaseEntity #[ORM\Column(length: 255, nullable: true)] private ?string $ean = null; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'client', targetEntity: Invoice::class)] private Collection $invoices; + /** @var Collection */ #[ORM\ManyToMany(targetEntity: Project::class, mappedBy: 'clients')] private Collection $projects; diff --git a/src/Entity/CybersecurityAgreement.php b/src/Entity/CybersecurityAgreement.php index 34a9d473..4ed07f8c 100644 --- a/src/Entity/CybersecurityAgreement.php +++ b/src/Entity/CybersecurityAgreement.php @@ -14,7 +14,7 @@ class CybersecurityAgreement private ?int $id = null; #[ORM\ManyToOne(targetEntity: ServiceAgreement::class)] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?ServiceAgreement $serviceAgreement = null; #[ORM\Column(nullable: true)] diff --git a/src/Entity/DataProvider.php b/src/Entity/DataProvider.php index 8cb130b1..f64a94dd 100644 --- a/src/Entity/DataProvider.php +++ b/src/Entity/DataProvider.php @@ -9,7 +9,7 @@ class DataProvider extends AbstractBaseEntity { #[ORM\Column(length: 255, unique: true)] - private ?string $name = null; + private string $name = ''; #[ORM\Column(length: 255, nullable: true)] private ?string $url = null; @@ -18,7 +18,7 @@ class DataProvider extends AbstractBaseEntity private ?string $secret = null; #[ORM\Column(length: 255)] - private ?string $class = null; + private string $class = ''; #[ORM\Column(nullable: true)] private ?bool $enableClientSync = null; diff --git a/src/Entity/Epic.php b/src/Entity/Epic.php index d711986d..e7e2b43c 100644 --- a/src/Entity/Epic.php +++ b/src/Entity/Epic.php @@ -20,8 +20,9 @@ public function __construct() private ?int $id = null; #[ORM\Column(length: 255)] - private ?string $title = null; + private string $title = ''; + /** @var Collection */ #[ORM\ManyToMany(targetEntity: Issue::class, mappedBy: 'epics')] private Collection $issues; @@ -43,7 +44,7 @@ public function setTitle(string $title): static } /** - * @return Collection + * @return Collection */ public function getIssues(): Collection { diff --git a/src/Entity/Invoice.php b/src/Entity/Invoice.php index e0eb235c..df5efcd0 100644 --- a/src/Entity/Invoice.php +++ b/src/Entity/Invoice.php @@ -14,13 +14,13 @@ class Invoice extends AbstractBaseEntity { #[ORM\Column(length: 255)] - private ?string $name = null; + private string $name = ''; #[ORM\Column(length: 255, nullable: true)] private ?string $description = null; #[ORM\Column] - private ?bool $recorded = null; + private bool $recorded = false; #[ORM\Column(nullable: true)] private ?int $customerAccountId = null; @@ -61,6 +61,7 @@ class Invoice extends AbstractBaseEntity #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] private ?\DateTimeInterface $periodTo = null; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'invoice', targetEntity: InvoiceEntry::class, cascade: ['remove'])] #[ORM\OrderBy(['index' => Criteria::ASC])] private Collection $invoiceEntries; @@ -259,7 +260,7 @@ public function removeInvoiceEntry(InvoiceEntry $invoiceEntry): self return $this; } - public function setInvoiceEntryIndexes() + public function setInvoiceEntryIndexes(): void { $index = 0; foreach ($this->getInvoiceEntries() as $entry) { diff --git a/src/Entity/InvoiceEntry.php b/src/Entity/InvoiceEntry.php index 643ae128..e1cf71fa 100644 --- a/src/Entity/InvoiceEntry.php +++ b/src/Entity/InvoiceEntry.php @@ -15,11 +15,11 @@ class InvoiceEntry extends AbstractBaseEntity { #[ORM\ManyToOne(inversedBy: 'invoiceEntries')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?Invoice $invoice = null; #[ORM\Column(type: Types::INTEGER, name: 'entry_index')] - private ?int $index = null; + private int $index = 0; // TODO: Remove since it is unused. #[ORM\Column(length: 255, nullable: true)] @@ -48,9 +48,11 @@ class InvoiceEntry extends AbstractBaseEntity #[ORM\Column(length: 255, nullable: true)] private ?MaterialNumberEnum $materialNumber = null; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'invoiceEntry', targetEntity: Worklog::class)] private Collection $worklogs; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'invoiceEntry', targetEntity: IssueProduct::class)] private Collection $issueProducts; @@ -242,7 +244,7 @@ public function setTotalPrice(?float $totalPrice): self #[ORM\PrePersist] #[ORM\PreUpdate] - public function setInvoiceIndex() + public function setInvoiceIndex(): void { $this->getInvoice()?->setInvoiceEntryIndexes(); } diff --git a/src/Entity/Issue.php b/src/Entity/Issue.php index 99acc2b8..e77e653e 100644 --- a/src/Entity/Issue.php +++ b/src/Entity/Issue.php @@ -21,7 +21,7 @@ class Issue extends AbstractBaseEntity use DataProviderTrait; use SynchronizedEntityTrait; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $name = null; #[ORM\Column(type: 'string', nullable: true, enumType: IssueStatusEnum::class)] @@ -33,10 +33,10 @@ class Issue extends AbstractBaseEntity #[ORM\Column(length: 255, nullable: true)] private ?string $accountId = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $projectTrackerId = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $projectTrackerKey = null; // TODO: Deprecated. Remove in 4.0.0. @@ -47,12 +47,15 @@ class Issue extends AbstractBaseEntity #[ORM\Column(length: 255, nullable: true)] private ?string $epicName = null; + /** @var Collection */ #[ORM\ManyToMany(targetEntity: Epic::class, inversedBy: 'issues')] private Collection $epics; + /** @var Collection */ #[ORM\ManyToMany(targetEntity: Version::class, inversedBy: 'issues')] private Collection $versions; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'issue', targetEntity: Worklog::class)] private Collection $worklogs; @@ -62,6 +65,7 @@ class Issue extends AbstractBaseEntity #[ORM\ManyToOne(inversedBy: 'issues')] private ?Project $project = null; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'issue', targetEntity: IssueProduct::class, orphanRemoval: true)] #[ORM\OrderBy(['createdAt' => Criteria::ASC])] private Collection $products; @@ -78,7 +82,7 @@ class Issue extends AbstractBaseEntity #[ORM\Column(length: 255, nullable: true)] private ?string $worker = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $linkToIssue = null; public function __construct() diff --git a/src/Entity/IssueProduct.php b/src/Entity/IssueProduct.php index 39e7f51f..cd51a289 100644 --- a/src/Entity/IssueProduct.php +++ b/src/Entity/IssueProduct.php @@ -10,15 +10,15 @@ class IssueProduct extends AbstractBaseEntity { #[ORM\ManyToOne(inversedBy: 'products')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?Issue $issue = null; #[ORM\ManyToOne(inversedBy: 'issues')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?Product $product = null; #[ORM\Column] - private ?float $quantity; + private float $quantity = 0.0; #[ORM\Column(type: Types::TEXT, nullable: true)] private ?string $description = null; diff --git a/src/Entity/Product.php b/src/Entity/Product.php index 98606f83..20271d30 100644 --- a/src/Entity/Product.php +++ b/src/Entity/Product.php @@ -14,18 +14,19 @@ class Product extends AbstractBaseEntity { #[ORM\Column(length: 255)] #[Assert\NotBlank] - private ?string $name = null; + private string $name = ''; #[ORM\ManyToOne(inversedBy: 'products')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] #[Assert\NotNull] private ?Project $project = null; #[ORM\Column(type: Types::DECIMAL, precision: 10, scale: 2)] #[Assert\GreaterThanOrEqual(0)] #[Assert\LessThan(1_000_000)] - private ?string $price = null; + private string $price = '0'; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'product', targetEntity: IssueProduct::class, orphanRemoval: true)] private Collection $issues; diff --git a/src/Entity/Project.php b/src/Entity/Project.php index 5854c027..9085252e 100644 --- a/src/Entity/Project.php +++ b/src/Entity/Project.php @@ -19,36 +19,42 @@ class Project extends AbstractBaseEntity use DataProviderTrait; use SynchronizedEntityTrait; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $name = null; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'project', targetEntity: Invoice::class)] private Collection $invoices; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $projectTrackerProjectUrl; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $projectTrackerKey; #[ORM\Column(length: 255, nullable: true)] private ?string $projectTrackerId; + /** @var Collection */ #[ORM\ManyToMany(targetEntity: Client::class, inversedBy: 'projects')] private Collection $clients; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'project', targetEntity: Version::class)] private Collection $versions; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'project', targetEntity: Worklog::class)] private Collection $worklogs; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectBilling::class)] private Collection $projectBillings; #[ORM\Column(nullable: true)] private ?bool $include = null; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'project', targetEntity: Issue::class)] private Collection $issues; @@ -58,6 +64,7 @@ class Project extends AbstractBaseEntity #[ORM\Column(length: 255, nullable: true)] private ?string $projectLeadMail = null; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'project', targetEntity: Product::class, orphanRemoval: true)] private Collection $products; diff --git a/src/Entity/ProjectBilling.php b/src/Entity/ProjectBilling.php index 9df0da84..366d3ec9 100644 --- a/src/Entity/ProjectBilling.php +++ b/src/Entity/ProjectBilling.php @@ -12,23 +12,24 @@ class ProjectBilling extends AbstractBaseEntity { #[ORM\Column(length: 255)] - private ?string $name = null; + private string $name = ''; - #[ORM\Column(type: Types::DATETIME_MUTABLE)] + #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] private ?\DateTimeInterface $periodStart = null; - #[ORM\Column(type: Types::DATETIME_MUTABLE)] + #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] private ?\DateTimeInterface $periodEnd = null; + /** @var Collection */ #[ORM\OneToMany(mappedBy: 'projectBilling', targetEntity: Invoice::class)] private Collection $invoices; #[ORM\ManyToOne(inversedBy: 'projectBillings')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?Project $project = null; #[ORM\Column] - private ?bool $recorded = null; + private bool $recorded = false; #[ORM\Column(type: Types::TEXT, nullable: true)] private ?string $description = null; diff --git a/src/Entity/ProjectVersionBudget.php b/src/Entity/ProjectVersionBudget.php index 744b91fb..5c059bc0 100644 --- a/src/Entity/ProjectVersionBudget.php +++ b/src/Entity/ProjectVersionBudget.php @@ -9,13 +9,13 @@ class ProjectVersionBudget extends AbstractBaseEntity { #[ORM\Column(length: 255)] - private ?string $projectId = null; + private string $projectId = ''; #[ORM\Column(length: 255)] - private ?string $versionId = null; + private string $versionId = ''; #[ORM\Column] - private ?float $budget = null; + private float $budget = 0.0; public function getProjectId(): ?string { diff --git a/src/Entity/ServiceAgreement.php b/src/Entity/ServiceAgreement.php index 0dc45d76..934dc231 100644 --- a/src/Entity/ServiceAgreement.php +++ b/src/Entity/ServiceAgreement.php @@ -15,11 +15,11 @@ class ServiceAgreement extends AbstractBaseEntity { #[ORM\ManyToOne(targetEntity: Project::class, inversedBy: 'serviceAgreements')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?Project $project = null; #[ORM\ManyToOne(targetEntity: Client::class)] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?Client $client = null; #[ORM\ManyToOne(targetEntity: CybersecurityAgreement::class)] @@ -27,27 +27,28 @@ class ServiceAgreement extends AbstractBaseEntity private ?CybersecurityAgreement $cybersecurityAgreement = null; #[ORM\Column(enumType: HostingProviderEnum::class)] - private ?HostingProviderEnum $hostingProvider = null; + private HostingProviderEnum $hostingProvider = HostingProviderEnum::ADM; #[ORM\Column(length: 255, nullable: true)] private ?string $documentUrl = null; #[ORM\Column] - private ?float $price = null; + private float $price = 0.0; #[ORM\ManyToOne(targetEntity: Worker::class)] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?Worker $projectLead = null; - #[ORM\Column(type: Types::DATETIME_MUTABLE)] + #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] private ?\DateTimeInterface $validFrom = null; #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] private ?\DateTimeInterface $validTo = null; #[ORM\Column] - private ?bool $isActive = null; + private bool $isActive = false; + /** @var array */ #[ORM\Column(type: Types::JSON)] private array $systemOwnerNotices = []; diff --git a/src/Entity/Subscription.php b/src/Entity/Subscription.php index 1c40d2c0..219efcb3 100644 --- a/src/Entity/Subscription.php +++ b/src/Entity/Subscription.php @@ -12,11 +12,12 @@ class Subscription extends AbstractBaseEntity { #[ORM\Column(length: 180, unique: false)] - private ?string $email = null; + private string $email = ''; #[ORM\Column(type: 'string', nullable: true, enumType: SubscriptionSubjectEnum::class)] private ?SubscriptionSubjectEnum $subject = null; + /** @var array|null */ #[ORM\Column(type: 'json', nullable: true)] private ?array $urlParams = null; @@ -55,11 +56,17 @@ public function setSubject(SubscriptionSubjectEnum $subject): self return $this; } + /** + * @return array|null + */ public function getUrlParams(): ?array { return $this->urlParams; } + /** + * @param array|null $urlParams + */ public function setUrlParams(?array $urlParams): self { $this->urlParams = $urlParams; diff --git a/src/Entity/User.php b/src/Entity/User.php index 89df9b7a..afea81d5 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -15,13 +15,14 @@ class User implements UserInterface private ?int $id = null; #[ORM\Column(length: 180, unique: true)] - private ?string $email = null; + private string $email = ''; + /** @var array */ #[ORM\Column] private array $roles = []; #[ORM\Column(length: 255)] - private ?string $name = null; + private string $name = ''; public function __construct() { @@ -51,7 +52,11 @@ public function setEmail(string $email): self */ public function getUserIdentifier(): string { - return (string) $this->email; + if ('' === $this->email) { + throw new \LogicException('User identifier requires a non-empty email.'); + } + + return $this->email; } /** @@ -64,6 +69,9 @@ public function getRoles(): array return array_unique($roles); } + /** + * @param array $roles + */ public function setRoles(array $roles): self { $this->roles = $roles; diff --git a/src/Entity/Version.php b/src/Entity/Version.php index a89f78f3..e04c41eb 100644 --- a/src/Entity/Version.php +++ b/src/Entity/Version.php @@ -18,20 +18,21 @@ class Version extends AbstractBaseEntity use DataProviderTrait; use SynchronizedEntityTrait; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $name = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $projectTrackerId = null; #[ORM\ManyToOne(inversedBy: 'versions')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?Project $project = null; + /** @var Collection */ #[ORM\ManyToMany(targetEntity: Issue::class, mappedBy: 'versions')] private Collection $issues; - #[ORM\Column] + #[ORM\Column(nullable: true)] private ?bool $isBillable = true; public function __construct() diff --git a/src/Entity/Worker.php b/src/Entity/Worker.php index 7f95c248..8d7fbb32 100644 --- a/src/Entity/Worker.php +++ b/src/Entity/Worker.php @@ -17,7 +17,7 @@ class Worker private ?int $id = null; #[ORM\Column(length: 180, unique: true)] - private ?string $email = null; + private string $email = ''; #[ORM\Column(length: 180, nullable: true)] private ?float $workload = null; @@ -41,7 +41,7 @@ public function __construct() public function __toString(): string { - return (string) ($this->name ?? $this->email ?? $this->id); + return (string) ($this->name ?: $this->email ?: $this->id); } public function getId(): ?int diff --git a/src/Entity/WorkerGroup.php b/src/Entity/WorkerGroup.php index 4d04540a..4a3bdec0 100644 --- a/src/Entity/WorkerGroup.php +++ b/src/Entity/WorkerGroup.php @@ -17,7 +17,7 @@ class WorkerGroup private ?int $id = null; #[ORM\Column(length: 255, unique: true)] - private ?string $name = null; + private string $name = ''; /** * @var Collection @@ -49,7 +49,7 @@ public function setName(string $name): static public function __toString(): string { - return (string) ($this->name ?? $this->id); + return (string) ($this->name ?: $this->id); } /** diff --git a/src/Entity/Worklog.php b/src/Entity/Worklog.php index 95d65c0e..45476219 100644 --- a/src/Entity/Worklog.php +++ b/src/Entity/Worklog.php @@ -21,7 +21,7 @@ class Worklog extends AbstractBaseEntity use SynchronizedEntityTrait; // TODO: Rename to projectTrackerId. - #[ORM\Column] + #[ORM\Column(nullable: true)] private ?int $worklogId = null; #[ORM\ManyToOne(inversedBy: 'worklogs')] @@ -34,13 +34,13 @@ class Worklog extends AbstractBaseEntity #[ORM\Column(type: 'text', nullable: true)] private ?string $description = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $worker = null; - #[ORM\Column] + #[ORM\Column(nullable: true)] private ?int $timeSpentSeconds = null; - #[ORM\Column(type: Types::DATETIME_MUTABLE)] + #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] private ?\DateTimeInterface $started = null; #[ORM\ManyToOne(inversedBy: 'worklogs')] @@ -51,10 +51,10 @@ class Worklog extends AbstractBaseEntity private ?int $billedSeconds = null; #[ORM\ManyToOne(inversedBy: 'worklogs')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?Issue $issue = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $projectTrackerIssueId = null; #[ORM\Column(type: 'string', nullable: true, enumType: BillableKindsEnum::class)] diff --git a/src/Form/AccountType.php b/src/Form/AccountType.php index f2c84fe2..d7114b89 100644 --- a/src/Form/AccountType.php +++ b/src/Form/AccountType.php @@ -8,6 +8,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class AccountType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/BillableUnbilledHoursReportType.php b/src/Form/BillableUnbilledHoursReportType.php index 8fa8a005..b755cc50 100644 --- a/src/Form/BillableUnbilledHoursReportType.php +++ b/src/Form/BillableUnbilledHoursReportType.php @@ -9,6 +9,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class BillableUnbilledHoursReportType extends AbstractType { public function __construct( diff --git a/src/Form/ClientFilterType.php b/src/Form/ClientFilterType.php index 6d69e913..cb91f846 100644 --- a/src/Form/ClientFilterType.php +++ b/src/Form/ClientFilterType.php @@ -8,6 +8,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class ClientFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ClientType.php b/src/Form/ClientType.php index 444055fa..7deaae7c 100644 --- a/src/Form/ClientType.php +++ b/src/Form/ClientType.php @@ -16,6 +16,9 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Translation\TranslatableMessage; +/** + * @extends AbstractType + */ class ClientType extends AbstractType { public function __construct(private readonly VersionRepository $versionRepository, private readonly ClientRepository $clientRepository) @@ -106,6 +109,9 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ]); } + /** + * @return array + */ private function getVersionOptions(?Client $client): array { $versions = $this->versionRepository->findAll(); diff --git a/src/Form/CombinedServiceAgreementType.php b/src/Form/CombinedServiceAgreementType.php index 8081d759..8abb02fd 100644 --- a/src/Form/CombinedServiceAgreementType.php +++ b/src/Form/CombinedServiceAgreementType.php @@ -7,6 +7,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class CombinedServiceAgreementType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/CybersecurityAgreementType.php b/src/Form/CybersecurityAgreementType.php index 5a63059b..95e045c0 100644 --- a/src/Form/CybersecurityAgreementType.php +++ b/src/Form/CybersecurityAgreementType.php @@ -9,6 +9,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class CybersecurityAgreementType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/CybersecurityReportType.php b/src/Form/CybersecurityReportType.php index d0523f9d..72a02511 100644 --- a/src/Form/CybersecurityReportType.php +++ b/src/Form/CybersecurityReportType.php @@ -11,6 +11,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class CybersecurityReportType extends AbstractType { private const string DEFAULT_CYBERSECURITY_MILESTONE = 'Cybersikkerhedsaftale'; diff --git a/src/Form/ForecastReportType.php b/src/Form/ForecastReportType.php index b624cfa0..8a32d105 100644 --- a/src/Form/ForecastReportType.php +++ b/src/Form/ForecastReportType.php @@ -10,6 +10,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class ForecastReportType extends AbstractType { public function __construct( diff --git a/src/Form/HourReportType.php b/src/Form/HourReportType.php index d7dbd4e9..9d501718 100644 --- a/src/Form/HourReportType.php +++ b/src/Form/HourReportType.php @@ -14,6 +14,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class HourReportType extends AbstractType { public function __construct( diff --git a/src/Form/InvoiceEntryType.php b/src/Form/InvoiceEntryType.php index 9ba9f40c..10726d4c 100644 --- a/src/Form/InvoiceEntryType.php +++ b/src/Form/InvoiceEntryType.php @@ -9,6 +9,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class InvoiceEntryType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/InvoiceEntryWorklogFilterType.php b/src/Form/InvoiceEntryWorklogFilterType.php index a85a116f..c0928227 100644 --- a/src/Form/InvoiceEntryWorklogFilterType.php +++ b/src/Form/InvoiceEntryWorklogFilterType.php @@ -9,6 +9,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class InvoiceEntryWorklogFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/InvoiceEntryWorklogType.php b/src/Form/InvoiceEntryWorklogType.php index 4c51cf45..f72f7c6a 100644 --- a/src/Form/InvoiceEntryWorklogType.php +++ b/src/Form/InvoiceEntryWorklogType.php @@ -9,6 +9,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class InvoiceEntryWorklogType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/InvoiceFilterType.php b/src/Form/InvoiceFilterType.php index 749e2993..0ddff4fc 100644 --- a/src/Form/InvoiceFilterType.php +++ b/src/Form/InvoiceFilterType.php @@ -9,6 +9,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class InvoiceFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/InvoiceNewType.php b/src/Form/InvoiceNewType.php index 6fc7a393..5f4ac894 100644 --- a/src/Form/InvoiceNewType.php +++ b/src/Form/InvoiceNewType.php @@ -9,6 +9,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class InvoiceNewType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/InvoiceRecordType.php b/src/Form/InvoiceRecordType.php index da2a08cd..c5e58e1a 100644 --- a/src/Form/InvoiceRecordType.php +++ b/src/Form/InvoiceRecordType.php @@ -8,6 +8,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class InvoiceRecordType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/InvoiceType.php b/src/Form/InvoiceType.php index 8a847e3e..3e6d90e3 100644 --- a/src/Form/InvoiceType.php +++ b/src/Form/InvoiceType.php @@ -12,6 +12,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class InvoiceType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/InvoicingRateReportType.php b/src/Form/InvoicingRateReportType.php index 2ebf0a64..770442eb 100644 --- a/src/Form/InvoicingRateReportType.php +++ b/src/Form/InvoicingRateReportType.php @@ -11,6 +11,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class InvoicingRateReportType extends AbstractType { public function __construct( diff --git a/src/Form/IssueFilterType.php b/src/Form/IssueFilterType.php index b6d7b969..2d72fecf 100644 --- a/src/Form/IssueFilterType.php +++ b/src/Form/IssueFilterType.php @@ -8,6 +8,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class IssueFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/IssueProductType.php b/src/Form/IssueProductType.php index 289b0914..43df0492 100644 --- a/src/Form/IssueProductType.php +++ b/src/Form/IssueProductType.php @@ -17,6 +17,9 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Translation\TranslatableMessage; +/** + * @extends AbstractType + */ class IssueProductType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ManagementReportDateIntervalType.php b/src/Form/ManagementReportDateIntervalType.php index c9878e2c..1b572e2f 100644 --- a/src/Form/ManagementReportDateIntervalType.php +++ b/src/Form/ManagementReportDateIntervalType.php @@ -6,6 +6,9 @@ use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\FormBuilderInterface; +/** + * @extends AbstractType + */ class ManagementReportDateIntervalType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/NameFilterType.php b/src/Form/NameFilterType.php index 10be09f2..83ab0f50 100644 --- a/src/Form/NameFilterType.php +++ b/src/Form/NameFilterType.php @@ -8,6 +8,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class NameFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/PlanningType.php b/src/Form/PlanningType.php index dcdfd92a..490daabe 100644 --- a/src/Form/PlanningType.php +++ b/src/Form/PlanningType.php @@ -10,6 +10,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class PlanningType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ProductFilterType.php b/src/Form/ProductFilterType.php index 98cd37e1..eb302193 100644 --- a/src/Form/ProductFilterType.php +++ b/src/Form/ProductFilterType.php @@ -10,6 +10,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class ProductFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ProductType.php b/src/Form/ProductType.php index 54ab2db8..2b1224aa 100644 --- a/src/Form/ProductType.php +++ b/src/Form/ProductType.php @@ -11,6 +11,9 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Translation\TranslatableMessage; +/** + * @extends AbstractType + */ class ProductType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ProjectBillingFilterType.php b/src/Form/ProjectBillingFilterType.php index d0205db3..83128ad1 100644 --- a/src/Form/ProjectBillingFilterType.php +++ b/src/Form/ProjectBillingFilterType.php @@ -9,6 +9,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class ProjectBillingFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ProjectBillingRecordType.php b/src/Form/ProjectBillingRecordType.php index 77c8330e..18761aba 100644 --- a/src/Form/ProjectBillingRecordType.php +++ b/src/Form/ProjectBillingRecordType.php @@ -8,6 +8,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class ProjectBillingRecordType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ProjectBillingType.php b/src/Form/ProjectBillingType.php index 753b2218..56e46ada 100644 --- a/src/Form/ProjectBillingType.php +++ b/src/Form/ProjectBillingType.php @@ -10,6 +10,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class ProjectBillingType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ProjectFilterType.php b/src/Form/ProjectFilterType.php index 98131a32..0983e914 100644 --- a/src/Form/ProjectFilterType.php +++ b/src/Form/ProjectFilterType.php @@ -9,6 +9,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class ProjectFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ProjectType.php b/src/Form/ProjectType.php index 2b8f8364..6b2a6fc3 100644 --- a/src/Form/ProjectType.php +++ b/src/Form/ProjectType.php @@ -11,6 +11,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class ProjectType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ServiceAgreementFilterType.php b/src/Form/ServiceAgreementFilterType.php index a31ad838..fe4ca442 100644 --- a/src/Form/ServiceAgreementFilterType.php +++ b/src/Form/ServiceAgreementFilterType.php @@ -13,6 +13,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class ServiceAgreementFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/ServiceAgreementType.php b/src/Form/ServiceAgreementType.php index b9ea1ad6..850e1916 100644 --- a/src/Form/ServiceAgreementType.php +++ b/src/Form/ServiceAgreementType.php @@ -22,6 +22,9 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints\Url; +/** + * @extends AbstractType + */ class ServiceAgreementType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/SubscriptionFilterType.php b/src/Form/SubscriptionFilterType.php index 6b7a2dba..26787852 100644 --- a/src/Form/SubscriptionFilterType.php +++ b/src/Form/SubscriptionFilterType.php @@ -8,6 +8,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class SubscriptionFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/WorkerGroupType.php b/src/Form/WorkerGroupType.php index 887b1520..41f05b01 100644 --- a/src/Form/WorkerGroupType.php +++ b/src/Form/WorkerGroupType.php @@ -10,6 +10,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class WorkerGroupType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/WorkerType.php b/src/Form/WorkerType.php index 110a9cd5..fb2df93c 100644 --- a/src/Form/WorkerType.php +++ b/src/Form/WorkerType.php @@ -11,6 +11,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class WorkerType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/WorkloadReportType.php b/src/Form/WorkloadReportType.php index 70961dbe..737a08a8 100644 --- a/src/Form/WorkloadReportType.php +++ b/src/Form/WorkloadReportType.php @@ -12,6 +12,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractType + */ class WorkloadReportType extends AbstractType { public function __construct( diff --git a/src/Message/LeantimeUpdateMessage.php b/src/Message/LeantimeUpdateMessage.php index ffffd97e..1d651816 100644 --- a/src/Message/LeantimeUpdateMessage.php +++ b/src/Message/LeantimeUpdateMessage.php @@ -4,6 +4,9 @@ readonly class LeantimeUpdateMessage { + /** + * @param array|null $projectTrackerProjectIds + */ public function __construct( public string $className, public int $start, diff --git a/src/Model/DashboardData.php b/src/Model/DashboardData.php index d32ddf17..9ff9b1fd 100644 --- a/src/Model/DashboardData.php +++ b/src/Model/DashboardData.php @@ -4,6 +4,10 @@ class DashboardData { + /** + * @param array $monthStatuses + * @param array $weekStatuses + */ public function __construct( public readonly float $workHours, public readonly int $year, diff --git a/src/Model/DataProvider/DataProviderIssueData.php b/src/Model/DataProvider/DataProviderIssueData.php index 51bde7ab..c3dff8f8 100644 --- a/src/Model/DataProvider/DataProviderIssueData.php +++ b/src/Model/DataProvider/DataProviderIssueData.php @@ -6,6 +6,9 @@ class DataProviderIssueData { + /** + * @param array $epics + */ public function __construct( public string $projectTrackerId, public int $dataProviderId, diff --git a/src/Model/Invoices/InvoiceEntryWorklogsFilterData.php b/src/Model/Invoices/InvoiceEntryWorklogsFilterData.php index 4848bd2f..5579c6f1 100644 --- a/src/Model/Invoices/InvoiceEntryWorklogsFilterData.php +++ b/src/Model/Invoices/InvoiceEntryWorklogsFilterData.php @@ -12,5 +12,6 @@ class InvoiceEntryWorklogsFilterData public ?string $worker = null; public ?Version $version = null; public ?bool $onlyAvailable = true; + /** @var array|null */ public ?array $epics = []; } diff --git a/src/Model/Invoices/PagedResult.php b/src/Model/Invoices/PagedResult.php index c9db977c..b67b5459 100644 --- a/src/Model/Invoices/PagedResult.php +++ b/src/Model/Invoices/PagedResult.php @@ -4,6 +4,9 @@ class PagedResult { + /** + * @param array $items + */ public function __construct( public readonly array $items, public readonly int $startAt, diff --git a/src/Model/Reports/BillableUnbilledHoursReportData.php b/src/Model/Reports/BillableUnbilledHoursReportData.php index 541abbc4..01fcbfbe 100644 --- a/src/Model/Reports/BillableUnbilledHoursReportData.php +++ b/src/Model/Reports/BillableUnbilledHoursReportData.php @@ -10,6 +10,7 @@ class BillableUnbilledHoursReportData /** @var ArrayCollection */ public ArrayCollection $projectData; + /** @var array */ public array $projectTotals; public int|float $totalHoursForAllProjects; diff --git a/src/Model/Reports/ForecastReportIssueVersionData.php b/src/Model/Reports/ForecastReportIssueVersionData.php index 60bf7910..2b0eca02 100644 --- a/src/Model/Reports/ForecastReportIssueVersionData.php +++ b/src/Model/Reports/ForecastReportIssueVersionData.php @@ -8,7 +8,7 @@ class ForecastReportIssueVersionData public string $issueVersionIdentifier; public float $invoiced = 0.0; public float $invoicedAndRecorded = 0.0; - /** @var array */ + /** @var array */ public array $worklogs = []; public function __construct(string $issueVersion) diff --git a/src/Model/Reports/ForecastReportWorklogData.php b/src/Model/Reports/ForecastReportWorklogData.php index 5b7181c3..34dbab28 100644 --- a/src/Model/Reports/ForecastReportWorklogData.php +++ b/src/Model/Reports/ForecastReportWorklogData.php @@ -9,7 +9,7 @@ class ForecastReportWorklogData public float $invoiced = 0.0; public float $invoicedAndRecorded = 0.0; - public function __construct($worklogId, $description) + public function __construct() { } } diff --git a/src/Model/Reports/HourReportData.php b/src/Model/Reports/HourReportData.php index be68959b..54089c05 100644 --- a/src/Model/Reports/HourReportData.php +++ b/src/Model/Reports/HourReportData.php @@ -6,7 +6,6 @@ class HourReportData { - public readonly string $id; public float $projectTotalSpent; public float $projectTotalEstimated; /** @var ArrayCollection */ diff --git a/src/Model/Reports/HourReportProjectTicket.php b/src/Model/Reports/HourReportProjectTicket.php index 62444511..a6e3efcc 100644 --- a/src/Model/Reports/HourReportProjectTicket.php +++ b/src/Model/Reports/HourReportProjectTicket.php @@ -12,10 +12,12 @@ class HourReportProjectTicket public float $totalEstimated; public float $totalSpent; public readonly string $linkToIssue; + /** @var ArrayCollection */ public ArrayCollection $timesheets; + /** @var ArrayCollection */ public ArrayCollection $projectTickets; - public function __construct($id, $projectTrackerId, $headline, $totalEstimated, $totalSpent, $linkToIssue) + public function __construct(string $id, string $projectTrackerId, string $headline, float $totalEstimated, float $totalSpent, string $linkToIssue) { $this->id = $id; $this->projectTrackerId = $projectTrackerId; diff --git a/src/Model/Reports/HourReportWorklog.php b/src/Model/Reports/HourReportWorklog.php index fa64d134..b5878701 100644 --- a/src/Model/Reports/HourReportWorklog.php +++ b/src/Model/Reports/HourReportWorklog.php @@ -8,6 +8,7 @@ class HourReportWorklog { public readonly ?int $id; public readonly float $hours; + /** @var ArrayCollection */ public ArrayCollection $projectTicket; public function __construct(?int $id, float $hours) diff --git a/src/Model/Reports/InvoicingRateReportData.php b/src/Model/Reports/InvoicingRateReportData.php index 44ed0237..4cc2bdeb 100644 --- a/src/Model/Reports/InvoicingRateReportData.php +++ b/src/Model/Reports/InvoicingRateReportData.php @@ -6,13 +6,13 @@ class InvoicingRateReportData { - public readonly string $id; public readonly string $viewmode; /** @var ArrayCollection */ public ArrayCollection $period; /** @var ArrayCollection */ public ArrayCollection $workers; public int $currentPeriodNumeric; + /** @var ArrayCollection */ public ArrayCollection $periodAverages; public float $totalAverage; public bool $includeIssues; diff --git a/src/Model/Reports/InvoicingRateReportWorker.php b/src/Model/Reports/InvoicingRateReportWorker.php index 2d72ec2f..8099507e 100644 --- a/src/Model/Reports/InvoicingRateReportWorker.php +++ b/src/Model/Reports/InvoicingRateReportWorker.php @@ -11,10 +11,10 @@ class InvoicingRateReportWorker public float $average; - /** @var ArrayCollection */ + /** @var ArrayCollection> */ public ArrayCollection $dataByPeriod; - /** @var ArrayCollection */ + /** @var ArrayCollection> */ public ArrayCollection $projectData; public function __construct(Worker $worker) diff --git a/src/Model/Reports/WorkloadReportData.php b/src/Model/Reports/WorkloadReportData.php index b75469f4..e03bd280 100644 --- a/src/Model/Reports/WorkloadReportData.php +++ b/src/Model/Reports/WorkloadReportData.php @@ -6,13 +6,13 @@ class WorkloadReportData { - public readonly string $id; public readonly string $viewmode; /** @var ArrayCollection */ public ArrayCollection $period; /** @var ArrayCollection */ public ArrayCollection $workers; public int $currentPeriodNumeric; + /** @var ArrayCollection */ public ArrayCollection $periodAverages; public float $totalAverage; diff --git a/src/Repository/AccountRepository.php b/src/Repository/AccountRepository.php index 0629fff8..a7c624e4 100644 --- a/src/Repository/AccountRepository.php +++ b/src/Repository/AccountRepository.php @@ -11,11 +11,6 @@ /** * @extends ServiceEntityRepository - * - * @method Account|null find($id, $lockMode = null, $lockVersion = null) - * @method Account|null findOneBy(array $criteria, array $orderBy = null) - * @method Account[] findAll() - * @method Account[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class AccountRepository extends ServiceEntityRepository { @@ -42,6 +37,9 @@ public function remove(Account $entity, bool $flush = false): void } } + /** + * @return array + */ public function getAllChoices(): array { $accounts = $this->findAll(); @@ -58,6 +56,9 @@ public function getAllChoices(): array return $accountChoices; } + /** + * @return PaginationInterface + */ public function getFilteredPagination(NameFilterData $accountFilterData, int $page = 1): PaginationInterface { $qb = $this->createQueryBuilder('account'); diff --git a/src/Repository/ClientRepository.php b/src/Repository/ClientRepository.php index eeb59ecf..4f19f767 100644 --- a/src/Repository/ClientRepository.php +++ b/src/Repository/ClientRepository.php @@ -11,11 +11,6 @@ /** * @extends ServiceEntityRepository - * - * @method Client|null find($id, $lockMode = null, $lockVersion = null) - * @method Client|null findOneBy(array $criteria, array $orderBy = null) - * @method Client[] findAll() - * @method Client[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class ClientRepository extends ServiceEntityRepository { @@ -42,6 +37,9 @@ public function remove(Client $entity, bool $flush = false): void } } + /** + * @return PaginationInterface + */ public function getFilteredPagination(ClientFilterData $clientFilterData, int $page = 1): PaginationInterface { $qb = $this->createQueryBuilder('client'); diff --git a/src/Repository/DataProviderRepository.php b/src/Repository/DataProviderRepository.php index 9fdca1ea..05b933aa 100644 --- a/src/Repository/DataProviderRepository.php +++ b/src/Repository/DataProviderRepository.php @@ -8,11 +8,6 @@ /** * @extends ServiceEntityRepository - * - * @method DataProvider|null find($id, $lockMode = null, $lockVersion = null) - * @method DataProvider|null findOneBy(array $criteria, array $orderBy = null) - * @method DataProvider[] findAll() - * @method DataProvider[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class DataProviderRepository extends ServiceEntityRepository { diff --git a/src/Repository/EpicRepository.php b/src/Repository/EpicRepository.php index e3e3dc5c..9d1b7f9d 100644 --- a/src/Repository/EpicRepository.php +++ b/src/Repository/EpicRepository.php @@ -8,11 +8,6 @@ /** * @extends ServiceEntityRepository - * - * @method Epic|null find($id, $lockMode = null, $lockVersion = null) - * @method Epic|null findOneBy(array $criteria, array $orderBy = null) - * @method Epic[] findAll() - * @method Epic[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class EpicRepository extends ServiceEntityRepository { diff --git a/src/Repository/InvoiceEntryRepository.php b/src/Repository/InvoiceEntryRepository.php index 9d2cd40c..00828f60 100644 --- a/src/Repository/InvoiceEntryRepository.php +++ b/src/Repository/InvoiceEntryRepository.php @@ -8,11 +8,6 @@ /** * @extends ServiceEntityRepository - * - * @method InvoiceEntry|null find($id, $lockMode = null, $lockVersion = null) - * @method InvoiceEntry|null findOneBy(array $criteria, array $orderBy = null) - * @method findAll() - * @method findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class InvoiceEntryRepository extends ServiceEntityRepository { diff --git a/src/Repository/InvoiceRepository.php b/src/Repository/InvoiceRepository.php index 367bc4f4..26b4d3d3 100644 --- a/src/Repository/InvoiceRepository.php +++ b/src/Repository/InvoiceRepository.php @@ -11,11 +11,6 @@ /** * @extends ServiceEntityRepository - * - * @method Invoice|null find($id, $lockMode = null, $lockVersion = null) - * @method Invoice|null findOneBy(array $criteria, array $orderBy = null) - * @method findAll() - * @method findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class InvoiceRepository extends ServiceEntityRepository { @@ -42,6 +37,9 @@ public function remove(Invoice $entity, bool $flush = false): void } } + /** + * @return array + */ public function getByRecordedDateBetween(\DateTime $from, \DateTime $to): array { $parameters = [ @@ -59,6 +57,9 @@ public function getByRecordedDateBetween(\DateTime $from, \DateTime $to): array return $qb->getQuery()->getResult(); } + /** + * @return PaginationInterface + */ public function getFilteredPagination(InvoiceFilterData $invoiceFilterData, int $page = 1): PaginationInterface { $qb = $this->createQueryBuilder('invoice'); diff --git a/src/Repository/IssueProductRepository.php b/src/Repository/IssueProductRepository.php index d3790a08..5deef411 100644 --- a/src/Repository/IssueProductRepository.php +++ b/src/Repository/IssueProductRepository.php @@ -8,11 +8,6 @@ /** * @extends ServiceEntityRepository - * - * @method IssueProduct|null find($id, $lockMode = null, $lockVersion = null) - * @method IssueProduct|null findOneBy(array $criteria, array $orderBy = null) - * @method IssueProduct[] findAll() - * @method IssueProduct[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class IssueProductRepository extends ServiceEntityRepository { diff --git a/src/Repository/IssueRepository.php b/src/Repository/IssueRepository.php index 42adf99d..3320c43f 100644 --- a/src/Repository/IssueRepository.php +++ b/src/Repository/IssueRepository.php @@ -16,11 +16,6 @@ /** * @extends ServiceEntityRepository - * - * @method Issue|null find($id, $lockMode = null, $lockVersion = null) - * @method Issue|null findOneBy(array $criteria, array $orderBy = null) - * @method Issue[] findAll() - * @method Issue[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class IssueRepository extends ServiceEntityRepository { @@ -40,6 +35,9 @@ public function save(Issue $entity, bool $flush = false): void } } + /** + * @return PaginationInterface + */ public function getFilteredPagination(IssueFilterData $issueFilterData, int $page = 1): PaginationInterface { $qb = $this->createQueryBuilder('issue'); @@ -71,6 +69,9 @@ public function remove(Issue $entity, bool $flush = false): void } } + /** + * @return array + */ public function findEpicOptionsByProject(Project $project): array { $qb = $this->createQueryBuilder('issue'); @@ -87,7 +88,10 @@ public function findEpicOptionsByProject(Project $project): array return array_combine($titles, $ids); } - public function getClosedIssuesFromInterval(Project $project, \DateTimeInterface $periodStart, \DateTimeInterface $periodEnd) + /** + * @return array + */ + public function getClosedIssuesFromInterval(Project $project, \DateTimeInterface $periodStart, \DateTimeInterface $periodEnd): array { $from = new \DateTime($periodStart->format('Y-m-d').' 00:00:00'); $to = new \DateTime($periodEnd->format('Y-m-d').' 23:59:59'); @@ -105,6 +109,9 @@ public function getClosedIssuesFromInterval(Project $project, \DateTimeInterface return $qb->getQuery()->execute(); } + /** + * @return array + */ public function issuesContainingVersion(Version $version): array { $qb = $this->createQueryBuilder('issue') @@ -114,6 +121,11 @@ public function issuesContainingVersion(Version $version): array return $qb->getQuery()->getResult(); } + /** + * @param array|null $projects + * + * @return array + */ public function findIssuesInDateRange(string $startDate, string $endDate, ?WorkerGroup $group = null, ?array $projects = null): array { $qb = $this->createQueryBuilder('i') @@ -135,14 +147,20 @@ public function findIssuesInDateRange(string $startDate, string $endDate, ?Worke return $query->getResult(); } + /** + * @return Issue[] + */ public function issuesContainingVersionTitle(string $versionTitle): array { - return $this->createQueryBuilder('issue') + /** @var Issue[] $result */ + $result = $this->createQueryBuilder('issue') ->select('DISTINCT issue') ->innerJoin('issue.versions', 'version') ->andWhere('version.name = :versionTitle') ->setParameter('versionTitle', $versionTitle) ->getQuery() ->getResult(); + + return $result; } } diff --git a/src/Repository/ProductRepository.php b/src/Repository/ProductRepository.php index 31bfae5f..2238b3f4 100644 --- a/src/Repository/ProductRepository.php +++ b/src/Repository/ProductRepository.php @@ -11,11 +11,6 @@ /** * @extends ServiceEntityRepository - * - * @method Product|null find($id, $lockMode = null, $lockVersion = null) - * @method Product|null findOneBy(array $criteria, array $orderBy = null) - * @method Product[] findAll() - * @method Product[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class ProductRepository extends ServiceEntityRepository { @@ -26,6 +21,9 @@ public function __construct( parent::__construct($registry, Product::class); } + /** + * @return PaginationInterface + */ public function getFilteredPagination(ProductFilterData $productFilterData, int $page = 1): PaginationInterface { $qb = $this->createQueryBuilder('product'); diff --git a/src/Repository/ProjectBillingRepository.php b/src/Repository/ProjectBillingRepository.php index 0ff43535..93a5671c 100644 --- a/src/Repository/ProjectBillingRepository.php +++ b/src/Repository/ProjectBillingRepository.php @@ -11,11 +11,6 @@ /** * @extends ServiceEntityRepository - * - * @method ProjectBilling|null find($id, $lockMode = null, $lockVersion = null) - * @method ProjectBilling|null findOneBy(array $criteria, array $orderBy = null) - * @method ProjectBilling[] findAll() - * @method ProjectBilling[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class ProjectBillingRepository extends ServiceEntityRepository { @@ -42,6 +37,9 @@ public function remove(ProjectBilling $entity, bool $flush = false): void } } + /** + * @return PaginationInterface + */ public function getFilteredPagination(ProjectBillingFilterData $projectBillingFilterData, int $page = 1): PaginationInterface { $qb = $this->createQueryBuilder('pb'); diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php index 2984f8df..23372fca 100644 --- a/src/Repository/ProjectRepository.php +++ b/src/Repository/ProjectRepository.php @@ -14,11 +14,6 @@ /** * @extends ServiceEntityRepository - * - * @method Project|null find($id, $lockMode = null, $lockVersion = null) - * @method Project|null findOneBy(array $criteria, array $orderBy = null) - * @method findAll() - * @method findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class ProjectRepository extends ServiceEntityRepository { @@ -56,6 +51,9 @@ public function getIncluded(): QueryBuilder return $qb; } + /** + * @return PaginationInterface + */ public function getFilteredPagination(ProjectFilterData $projectFilterData, int $page = 1): PaginationInterface { $qb = $this->createQueryBuilder('project'); @@ -94,7 +92,12 @@ public function getFilteredPagination(ProjectFilterData $projectFilterData, int ); } - public function getProjectTrackerIdsByDataProviders(array $dataProviders) + /** + * @param array $dataProviders + * + * @return array + */ + public function getProjectTrackerIdsByDataProviders(array $dataProviders): array { $qb = $this->createQueryBuilder('project'); @@ -108,6 +111,9 @@ public function getProjectTrackerIdsByDataProviders(array $dataProviders) return $qb->getQuery()->getSingleColumnResult(); } + /** + * @return int[] + */ public function getProjectIdsWithCybersecurityAgreement(): array { $result = $this->_em->createQueryBuilder() @@ -118,7 +124,7 @@ public function getProjectIdsWithCybersecurityAgreement(): array ->getQuery() ->getScalarResult(); - return array_column($result, 'id'); + return array_map('intval', array_column($result, 'id')); } /** diff --git a/src/Repository/ProjectVersionBudgetRepository.php b/src/Repository/ProjectVersionBudgetRepository.php index 0ada3de8..ac83bf1d 100644 --- a/src/Repository/ProjectVersionBudgetRepository.php +++ b/src/Repository/ProjectVersionBudgetRepository.php @@ -8,11 +8,6 @@ /** * @extends ServiceEntityRepository - * - * @method ProjectVersionBudget|null find($id, $lockMode = null, $lockVersion = null) - * @method ProjectVersionBudget|null findOneBy(array $criteria, array $orderBy = null) - * @method findAll() - * @method findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class ProjectVersionBudgetRepository extends ServiceEntityRepository { diff --git a/src/Repository/ServiceAgreementRepository.php b/src/Repository/ServiceAgreementRepository.php index bb57ad5e..fa1689c8 100644 --- a/src/Repository/ServiceAgreementRepository.php +++ b/src/Repository/ServiceAgreementRepository.php @@ -19,6 +19,9 @@ public function __construct(ManagerRegistry $registry, private readonly Paginato parent::__construct($registry, ServiceAgreement::class); } + /** + * @return PaginationInterface + */ public function getFilteredPagination(ServiceAgreementFilterData $serviceAgreementFilterData, int $page = 1): PaginationInterface { $qb = $this->createQueryBuilder('service_agreement'); diff --git a/src/Repository/SubscriptionRepository.php b/src/Repository/SubscriptionRepository.php index 4fb9ae68..cf75cd64 100644 --- a/src/Repository/SubscriptionRepository.php +++ b/src/Repository/SubscriptionRepository.php @@ -6,19 +6,13 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\NonUniqueResultException; use Doctrine\Persistence\ManagerRegistry; -use Knp\Component\Pager\PaginatorInterface; /** * @extends ServiceEntityRepository - * - * @method Subscription|null find($id, $lockMode = null, $lockVersion = null) - * @method Subscription|null findOneBy(array $criteria, array $orderBy = null) - * @method Subscription[] findAll() - * @method Subscription[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) - * */ + */ class SubscriptionRepository extends ServiceEntityRepository { - public function __construct(ManagerRegistry $registry, private readonly PaginatorInterface $paginator) + public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Subscription::class); } @@ -41,7 +35,12 @@ public function remove(Subscription $entity, bool $flush = false): void } } - public function findByCustom($email, $urlParams): array + /** + * @param array $urlParams + * + * @return array + */ + public function findByCustom(string $email, array $urlParams): array { $qb = $this->createQueryBuilder('s'); @@ -59,9 +58,11 @@ public function findByCustom($email, $urlParams): array } /** + * @param array $urlParams + * * @throws NonUniqueResultException */ - public function findOneByCustom($email, $subscriptionType, $urlParams): ?Subscription + public function findOneByCustom(string $email, string $subscriptionType, array $urlParams): ?Subscription { $qb = $this->createQueryBuilder('s'); @@ -85,6 +86,9 @@ public function findOneByCustom($email, $subscriptionType, $urlParams): ?Subscri * Due to the way searching is implemented in the controller, * the repository does not return a paginated element. */ + /** + * @return array + */ public function getFilteredData(string $email): array { $qb = $this->createQueryBuilder('subscription') diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index 4e249716..583d25d4 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -8,11 +8,6 @@ /** * @extends ServiceEntityRepository - * - * @method User|null find($id, $lockMode = null, $lockVersion = null) - * @method User|null findOneBy(array $criteria, array $orderBy = null) - * @method User[] findAll() - * @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class UserRepository extends ServiceEntityRepository { diff --git a/src/Repository/VersionRepository.php b/src/Repository/VersionRepository.php index 19195d95..1814e451 100644 --- a/src/Repository/VersionRepository.php +++ b/src/Repository/VersionRepository.php @@ -8,11 +8,6 @@ /** * @extends ServiceEntityRepository - * - * @method Version|null find($id, $lockMode = null, $lockVersion = null) - * @method Version|null findOneBy(array $criteria, array $orderBy = null) - * @method Version[] findAll() - * @method Version[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class VersionRepository extends ServiceEntityRepository { diff --git a/src/Repository/WorkerGroupRepository.php b/src/Repository/WorkerGroupRepository.php index b11b79fb..71f06660 100644 --- a/src/Repository/WorkerGroupRepository.php +++ b/src/Repository/WorkerGroupRepository.php @@ -19,6 +19,9 @@ public function __construct(ManagerRegistry $registry, private readonly Paginato parent::__construct($registry, WorkerGroup::class); } + /** + * @return PaginationInterface + */ public function getFilteredPagination(NameFilterData $nameFilterData, int $page = 1): PaginationInterface { $qb = $this->createQueryBuilder('g'); diff --git a/src/Repository/WorkerRepository.php b/src/Repository/WorkerRepository.php index ca5c47f5..20e8d581 100644 --- a/src/Repository/WorkerRepository.php +++ b/src/Repository/WorkerRepository.php @@ -8,11 +8,6 @@ /** * @extends ServiceEntityRepository - * - * @method Worker|null find($id, $lockMode = null, $lockVersion = null) - * @method Worker|null findOneBy(array $criteria, array $orderBy = null) - * @method Worker[] findAll() - * @method Worker[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class WorkerRepository extends ServiceEntityRepository { @@ -39,6 +34,9 @@ public function remove(Worker $entity, bool $flush = false): void } } + /** + * @return array + */ public function findAllIncludedInReports(): array { return $this->createQueryBuilder('w') diff --git a/src/Repository/WorklogRepository.php b/src/Repository/WorklogRepository.php index 7e933f4f..443b6079 100644 --- a/src/Repository/WorklogRepository.php +++ b/src/Repository/WorklogRepository.php @@ -15,11 +15,6 @@ /** * @extends ServiceEntityRepository - * - * @method Worklog|null find($id, $lockMode = null, $lockVersion = null) - * @method Worklog|null findOneBy(array $criteria, array $orderBy = null) - * @method findAll() - * @method findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class WorklogRepository extends ServiceEntityRepository { @@ -46,6 +41,9 @@ public function remove(Worklog $entity, bool $flush = false): void } } + /** + * @return iterable + */ public function findByFilterData(Project $project, InvoiceEntry $invoiceEntry, InvoiceEntryWorklogsFilterData $filterData): iterable { $qb = $this->createQueryBuilder('worklog'); @@ -112,7 +110,10 @@ public function updateProjectByIssue(Issue $issue, Project $project): int ->execute(); } - public function findWorklogsByWorkerAndDateRange(string $workerIdentifier, \DateTime $dateFrom, \DateTime $dateTo) + /** + * @return array + */ + public function findWorklogsByWorkerAndDateRange(string $workerIdentifier, \DateTime $dateFrom, \DateTime $dateTo): array { $qb = $this->createQueryBuilder('worklog'); @@ -135,7 +136,7 @@ public function findWorklogsByWorkerAndDateRange(string $workerIdentifier, \Date * @param \DateTimeInterface $to The ending date time * @param string $groupBy The function to group by, accepts 'week', 'month' and 'year' * - * @return array An array of results containing total time spent, week number, and worker, indexed by week/month/year number + * @return array> An array of results containing total time spent, week number, and worker, indexed by week/month/year number */ public function getTimeSpentByWorkerInWeekRange( string $workerEmail, @@ -188,6 +189,9 @@ public function getTimeSpentByWorkerInWeekRange( * * @return array an array of worklogs matching the specified criteria */ + /** + * @return array + */ public function findBillableWorklogsByWorkerAndDateRange(\DateTime $dateFrom, \DateTime $dateTo, ?string $workerIdentifier = null, mixed $isBilled = null): array { $nonBillableEpics = NonBillableEpicsEnum::getAsArray(); @@ -239,7 +243,10 @@ public function findBillableWorklogsByWorkerAndDateRange(\DateTime $dateFrom, \D return $qb->getQuery()->getResult(); } - public function findBilledWorklogsByWorkerAndDateRange(string $workerIdentifier, \DateTime $dateFrom, \DateTime $dateTo) + /** + * @return array + */ + public function findBilledWorklogsByWorkerAndDateRange(string $workerIdentifier, \DateTime $dateFrom, \DateTime $dateTo): array { $nonBillableEpics = NonBillableEpicsEnum::getAsArray(); $nonBillableVersions = NonBillableVersionsEnum::getAsArray(); @@ -277,6 +284,8 @@ public function findBilledWorklogsByWorkerAndDateRange(string $workerIdentifier, } /** + * @return array{total_count: int, pages_count: float, current_page: int, page_size: int, paginator: Paginator} + * * @throws \Exception */ public function getWorklogsAttachedToInvoiceInDateRange(\DateTimeInterface $periodStart, \DateTimeInterface $periodEnd, int $page = 1, int $pageSize = 50): array diff --git a/src/Service/BillableUnbilledHoursReportService.php b/src/Service/BillableUnbilledHoursReportService.php index ae370457..89693b37 100644 --- a/src/Service/BillableUnbilledHoursReportService.php +++ b/src/Service/BillableUnbilledHoursReportService.php @@ -37,16 +37,23 @@ public function getBillableUnbilledHoursReport( $totalHoursForAllProjects = 0; foreach ($billableWorklogs as $billableWorklog) { - $projectName = $billableWorklog->getProject()->getName(); - $issueName = $billableWorklog->getIssue()->getName(); + $project = $billableWorklog->getProject(); + $issue = $billableWorklog->getIssue(); + + if (null === $project || null === $issue) { + continue; + } + + $projectName = $project->getName(); + $issueName = $issue->getName(); // Initialize issue data if not already set if (!isset($projectData[$projectName][$issueName])) { $projectData[$projectName][$issueName] = [ 'worklogs' => [], 'totalHours' => 0, - 'id' => $billableWorklog->getIssue()->getProjectTrackerId(), - 'linkToIssue' => $billableWorklog->getIssue()->getLinkToIssue(), + 'id' => $issue->getProjectTrackerId(), + 'linkToIssue' => $issue->getLinkToIssue(), ]; } diff --git a/src/Service/BillingService.php b/src/Service/BillingService.php index 9bfdb8c1..1086a62e 100644 --- a/src/Service/BillingService.php +++ b/src/Service/BillingService.php @@ -125,6 +125,9 @@ public function recordInvoice(Invoice $invoice, string $confirmation = ConfirmDa $this->invoiceRepository->save($invoice, $flush); } + /** + * @return array + */ // TODO: Replace with exceptions. public function getInvoiceRecordableErrors(Invoice $invoice): array { @@ -156,7 +159,7 @@ public function getInvoiceRecordableErrors(Invoice $invoice): array /** * Create a spreadsheet response from an array of invoice ids. * - * @param array $ids array of invoice ids + * @param array $ids array of invoice ids * * @throws EconomicsException */ @@ -193,6 +196,8 @@ public function generateSpreadsheetCsvResponse(array $ids): Response /** * Create spreadsheet html from an array of invoice ids. * + * @param array $ids + * * @throws EconomicsException */ public function generateSpreadsheetHtml(array $ids): bool|string @@ -239,7 +244,7 @@ public function generateSpreadsheetHtml(array $ids): bool|string /** * Export the selected invoices (by id) to csv. * - * @param array $invoiceIds array of invoice ids that should be exported + * @param array $invoiceIds array of invoice ids that should be exported * * @throws EconomicsException */ diff --git a/src/Service/ClientHelper.php b/src/Service/ClientHelper.php index 624e3d0c..60398c16 100644 --- a/src/Service/ClientHelper.php +++ b/src/Service/ClientHelper.php @@ -6,6 +6,9 @@ class ClientHelper { + /** + * @param array $options + */ public function __construct( private readonly array $options, ) { @@ -14,7 +17,7 @@ public function __construct( /** * Get standard price from client with fallback to global value. */ - public function getStandardPrice(?Client $client = null) + public function getStandardPrice(?Client $client = null): float { $standardPrice = (float) $this->options['standard_price']; diff --git a/src/Service/CybersecurityReportService.php b/src/Service/CybersecurityReportService.php index 240bedac..5be558fc 100644 --- a/src/Service/CybersecurityReportService.php +++ b/src/Service/CybersecurityReportService.php @@ -38,9 +38,23 @@ public function getCybersecurityReport( $issues = $this->issueRepository->issuesContainingVersionTitle($versionTitle); foreach ($issues as $issue) { + $issueId = $issue->getId(); + $issueProjectTrackerId = $issue->getProjectTrackerId(); + $issueName = $issue->getName(); + $issueLink = $issue->getLinkToIssue(); + $projectEntity = $issue->getProject(); + if (null === $issueId + || null === $issueProjectTrackerId + || null === $issueName + || null === $issueLink + || null === $projectEntity + ) { + continue; + } + // Fetch worklogs for this issue restricted to the period $worklogs = $this->worklogRepository->getWorklogsByIssueAndPeriod( - $issue->getId(), + $issueId, $fromDate, $toDate ); @@ -57,9 +71,11 @@ public function getCybersecurityReport( continue; } - $projectEntity = $issue->getProject(); $projectId = $projectEntity->getId(); $projectName = $projectEntity->getName(); + if (null === $projectName) { + continue; + } // Create project entry once if (!isset($report->projects[$projectName])) { @@ -83,11 +99,11 @@ public function getCybersecurityReport( // Create ticket DTO $ticket = new CybersecurityTicketData( - $issue->getId(), - $issue->getProjectTrackerId(), - $issue->getName(), + $issueId, + $issueProjectTrackerId, + $issueName, $totalTicketSpent, - $issue->getLinkToIssue(), + $issueLink, $worklogData ); diff --git a/src/Service/DanishHolidayHelper.php b/src/Service/DanishHolidayHelper.php index ebd5081a..21f9c631 100644 --- a/src/Service/DanishHolidayHelper.php +++ b/src/Service/DanishHolidayHelper.php @@ -13,27 +13,34 @@ final class DanishHolidayHelper public const SUNDAY = 7; /** - * @var DanishHolidayHelper + * @var ?DanishHolidayHelper */ private static $instance; public static function getInstance(): self { - if (empty(self::$instance)) { + if (!isset(self::$instance)) { self::$instance = new self(); } return self::$instance; } + /** + * @param array $nonWorkdays + */ private function __construct( private readonly array $nonWorkdays = [self::SATURDAY, self::SUNDAY]) { } + /** @var array> */ private array $holidays = []; + /** @var array> */ private array $holidayNames = []; + /** @var array> */ private array $bankHolidays = []; + /** @var array> */ private array $bankHolidayNames = []; /** @@ -67,6 +74,8 @@ public function getHolidays(int $year): array /** * Get holiday names indexed by formatted date. + * + * @return array */ public function getHolidayNames(int $year): array { @@ -81,6 +90,8 @@ public function getHolidayNames(int $year): array * Get bank holidays. * * @see https://www.nationalbanken.dk/da/vores-arbejde/stabile-priser-pengepolitik-og-dansk-oekonomi/banklukkedage + * + * @return array */ public function getBankHolidays(int $year): array { @@ -98,6 +109,9 @@ public function getBankHolidays(int $year): array return $this->bankHolidays[$year]; } + /** + * @return array + */ public function getBankHolidayNames(int $year): array { if (!isset($this->bankHolidayNames[$year])) { @@ -177,6 +191,11 @@ private function getYear(\DateTimeInterface $date): int return (int) $date->format('Y'); } + /** + * @param array $days + * + * @return array + */ private function buildNames(array $days): array { $names = []; diff --git a/src/Service/DashboardService.php b/src/Service/DashboardService.php index 465e84b6..524c2d66 100644 --- a/src/Service/DashboardService.php +++ b/src/Service/DashboardService.php @@ -47,7 +47,9 @@ public function getUserDashboard(User $user, ?int $year = null): ?DashboardData $today->setTime(23, 59, 59); for ($month = 1; $month <= 12; ++$month) { - $daysInMonth = date('t', mktime(0, 0, 0, $month, 1, $year)); + $monthStart = mktime(0, 0, 0, $month, 1, $year); + \assert(false !== $monthStart); + $daysInMonth = date('t', $monthStart); for ($day = 1; $day <= $daysInMonth; ++$day) { $dayDate = new \DateTime(); @@ -97,6 +99,9 @@ public function getUserDashboard(User $user, ?int $year = null): ?DashboardData return new DashboardData($yearStatus, $year, $weekNorm, $monthStatuses, $weekStatuses); } + /** + * @return array + */ private function getWeeksToDate(): array { $currentWeek = (int) date('W'); @@ -108,6 +113,9 @@ private function getWeeksToDate(): array return $weeksToDate; } + /** + * @return array + */ private function getMonthsToDate(): array { $currentMonth = (int) date('m'); diff --git a/src/Service/DataProviderService.php b/src/Service/DataProviderService.php index 899f55c5..cc9d747b 100644 --- a/src/Service/DataProviderService.php +++ b/src/Service/DataProviderService.php @@ -250,7 +250,7 @@ public function upsertWorklog(DataProviderWorklogData $upsertWorklogData): void $worklog->setWorker($upsertWorklogData->username); $worklog->setStarted($upsertWorklogData->startedDate); $worklog->setProjectTrackerIssueId($upsertWorklogData->projectTrackerIssueId); - $worklog->setTimeSpentSeconds($upsertWorklogData->hours * $this::SECONDS_IN_HOUR); + $worklog->setTimeSpentSeconds((int) ($upsertWorklogData->hours * $this::SECONDS_IN_HOUR)); $worklog->setKind(BillableKindsEnum::tryFrom($upsertWorklogData->kind)); $worklog->setProject($issue->getProject()); $worklog->setIssue($issue); diff --git a/src/Service/DateTimeHelper.php b/src/Service/DateTimeHelper.php index e34e5bb8..e089ed20 100644 --- a/src/Service/DateTimeHelper.php +++ b/src/Service/DateTimeHelper.php @@ -14,7 +14,7 @@ public function __construct( * @param int $weekNumber the week number for which to retrieve the dates * @param int $year the year for which to retrieve the dates * - * @return array an array containing the first and last date of the week + * @return array{dateFrom: \DateTime, dateTo: \DateTime} an array containing the first and last date of the week */ public function getFirstAndLastDateOfWeek(int $weekNumber, int $year): array { @@ -33,7 +33,7 @@ public function getFirstAndLastDateOfWeek(int $weekNumber, int $year): array * @param int $monthNumber the month number (1-12) * @param int $year the year * - * @return array an array containing the first and last date of the specified month and year + * @return array{dateFrom: \DateTime, dateTo: \DateTime} an array containing the first and last date of the specified month and year */ public function getFirstAndLastDateOfMonth(int $monthNumber, int $year): array { @@ -70,7 +70,7 @@ public function getWeekdaysBetween(\DateTime $dateFrom, \DateTime $dateTo): int * * @param int $year the year for which to retrieve the week numbers * - * @return array an array of week numbers + * @return array an array of week numbers */ public function getWeeksOfYear(int $year): array { @@ -104,7 +104,10 @@ public function getWeeksOfYear(int $year): array */ public function getMonthName(int $monthNumber): string { - return \DateTime::createFromFormat('!m', (string) $monthNumber)->format('F'); + $date = \DateTime::createFromFormat('!m', (string) $monthNumber); + \assert($date instanceof \DateTime); + + return $date->format('F'); } /** @@ -112,7 +115,7 @@ public function getMonthName(int $monthNumber): string * * @param int $year the year * - * @return array an array containing the first and last date of the specified year + * @return array{dateFrom: \DateTime, dateTo: \DateTime} an array containing the first and last date of the specified year */ public function getFirstAndLastDateOfYear(int $year): array { @@ -131,7 +134,7 @@ public function getFirstAndLastDateOfYear(int $year): array * @param int $year the year * @param int $quarter the quarter (1-4) * - * @return array an array containing the first and last date of the specified quarter + * @return array{dateFrom: \DateTime, dateTo: \DateTime} an array containing the first and last date of the specified quarter */ public function getFirstAndLastDateOfQuarter(int $year, int $quarter): array { @@ -141,6 +144,7 @@ public function getFirstAndLastDateOfQuarter(int $year, int $quarter): array 2 => 4, 3 => 7, 4 => 10, + default => throw new \InvalidArgumentException(sprintf('Quarter must be 1-4, got %d', $quarter)), }; $lastMonth = $firstMonth + 2; diff --git a/src/Service/ForecastReportService.php b/src/Service/ForecastReportService.php index e9cb4bb2..1b92966a 100644 --- a/src/Service/ForecastReportService.php +++ b/src/Service/ForecastReportService.php @@ -48,24 +48,27 @@ public function getForecastReport(\DateTimeInterface $fromDate, \DateTimeInterfa foreach ($invoiceAttachedWorklogs['paginator'] as $worklog) { // Loop through each worklog - $projectId = $worklog->getProject()->getId(); + $project = $worklog->getProject(); + $issue = $worklog->getIssue(); + + if (null === $project || null === $issue) { + continue; + } + + $projectId = $project->getId(); if (!$projectId) { throw new \Exception('Project id is null'); } // If the project isn't already in the forecast, add it if (!isset($forecastReportData->projects[$projectId])) { - $newForecastReportProjectData = new ForecastReportProjectData($projectId); - $newForecastReportProjectData->projectName = $worklog->getProject()?->getName() ?? '[no project name]'; + $newForecastReportProjectData = new ForecastReportProjectData((string) $projectId); + $newForecastReportProjectData->projectName = $project->getName() ?? '[no project name]'; $forecastReportData->projects[$projectId] = $newForecastReportProjectData; } // Get current project from forecast $currentProject = $forecastReportData->projects[$projectId]; - if (!$currentProject) { - throw new \Exception('Project instance was not found'); - } - // Calculate worklog time in hours $worklogTime = ($worklog->getTimeSpentSeconds() / 3600); @@ -79,11 +82,11 @@ public function getForecastReport(\DateTimeInterface $fromDate, \DateTimeInterfa } // Get issue details from the worklog - $issueId = $worklog->getIssue()->getProjectTrackerKey(); - $issueLink = $worklog->getIssue()->getLinkToIssue(); + $issueId = $issue->getProjectTrackerKey() ?? '[no issue id]'; + $issueLink = $issue->getLinkToIssue() ?? '[no issue link]'; - if ($worklog->getIssue()->getEpics()->count() > 0) { - $issueTag = implode(',', $worklog->getIssue()->getEpics()->map(fn ($epic) => $epic->getTitle())->toArray()); + if ($issue->getEpics()->count() > 0) { + $issueTag = implode(',', array_map(fn ($epic) => $epic->getTitle(), $issue->getEpics()->toArray())); } else { $issueTag = '[no tag]'; } @@ -105,7 +108,7 @@ public function getForecastReport(\DateTimeInterface $fromDate, \DateTimeInterfa } // Get version details from the issue - $issueVersions = $worklog->getIssue()->getVersions(); + $issueVersions = $issue->getVersions(); $issueVersion = count($issueVersions) > 0 ? implode(', ', array_map(function ($version) { return $version->getName(); }, $issueVersions->toArray())) : '[no version]'; $issueVersionIdentifier = $issueTag.$issueVersion; @@ -131,11 +134,11 @@ public function getForecastReport(\DateTimeInterface $fromDate, \DateTimeInterfa $worklogId = $worklog->getId(); $workerEmail = $worklog->getWorker(); $workerName = $workerNameMapping[$workerEmail] ?? '[no worker]'; - $description = $worklog->getDescription(); + $description = $worklog->getDescription() ?? ''; // Add the worklog entry in the version if it does not exist if (!isset($currentVersion->worklogs[$worklogId])) { - $currentVersion->worklogs[$worklogId] = new ForecastReportWorklogData($worklogId, $description); + $currentVersion->worklogs[$worklogId] = new ForecastReportWorklogData(); $currentVersion->worklogs[$worklogId]->worker = $workerName; $currentVersion->worklogs[$worklogId]->description = $description; } diff --git a/src/Service/HourReportService.php b/src/Service/HourReportService.php index 33f83caf..99179527 100644 --- a/src/Service/HourReportService.php +++ b/src/Service/HourReportService.php @@ -50,12 +50,12 @@ public function getHourReport(Project $project, ?\DateTimeInterface $fromDate, ? } $projectTicket = new HourReportProjectTicket( - $issue->getId(), - $issue->getProjectTrackerId(), - $issue->getName(), + (string) $issue->getId(), + $issue->getProjectTrackerId() ?? '', + $issue->getName() ?? '', $totalTicketEstimated, $totalTicketSpent, - $issue->getLinkToIssue() + $issue->getLinkToIssue() ?? '' ); $projectTicket->timesheets->add($timesheets); @@ -86,7 +86,7 @@ public function getHourReport(Project $project, ?\DateTimeInterface $fromDate, ? $hourReportData->projectTotalSpent += $totalTicketSpent; } - /** @var \ArrayIterator $tagsIterator */ + /** @var \ArrayIterator $tagsIterator */ $tagsIterator = $hourReportData->projectTags->getIterator(); // Sort tags by display name (uasort: keys are epic names, sort by the value's `tag` so 'noTag' lands in alphabetical position). $tagsIterator->uasort(fn ($a, $b) => mb_strtolower($a->tag) <=> mb_strtolower($b->tag)); @@ -95,6 +95,11 @@ public function getHourReport(Project $project, ?\DateTimeInterface $fromDate, ? return $hourReportData; } + /** + * @param array $worklogs + * + * @return array{0: array, 1: float|int} + */ private function processTimesheetsData(array $worklogs, ?\DateTimeInterface $fromDate = null, ?\DateTimeInterface $toDate = null): array { $timesheets = []; diff --git a/src/Service/InvoiceEntryHelper.php b/src/Service/InvoiceEntryHelper.php index e72c398b..0555a783 100644 --- a/src/Service/InvoiceEntryHelper.php +++ b/src/Service/InvoiceEntryHelper.php @@ -9,8 +9,12 @@ class InvoiceEntryHelper { + /** @var array */ private readonly array $options; + /** + * @param array $options + */ public function __construct( array $options, ) { @@ -99,6 +103,9 @@ public function isEditable(InvoiceEntry $entry): bool || count($this->getAccountOptions(null)) > 1; } + /** + * @return array> + */ private function getAccounts(?string $account): array { $accounts = $this->options['accounts'] ?? []; @@ -113,6 +120,11 @@ private function getAccounts(?string $account): array return $accounts; } + /** + * @param array $options + * + * @return array + */ private function resolveOptions(array $options): array { return (new OptionsResolver()) diff --git a/src/Service/InvoicingRateReportService.php b/src/Service/InvoicingRateReportService.php index 3e3ef4c6..9190118d 100644 --- a/src/Service/InvoicingRateReportService.php +++ b/src/Service/InvoicingRateReportService.php @@ -101,12 +101,17 @@ public function getInvoicingRateReport( // Tally up billable logged hours in gathered worklogs for current period $loggedBillableHours = 0; foreach ($billableWorklogs as $billableWorklog) { - $projectName = $billableWorklog->getProject()->getName(); - $issueName = $billableWorklog->getIssue()->getName(); + $project = $billableWorklog->getProject(); + $issue = $billableWorklog->getIssue(); + if (null === $project || null === $issue) { + continue; + } + $projectName = $project->getName(); + $issueName = $issue->getName(); $workerProjects[$projectName][$period]['loggedBillableHours'] = ($workerProjects[$projectName][$period]['loggedBillableHours'] ?? 0) + ($billableWorklog->getTimeSpentSeconds() * self::SECONDS_TO_HOURS); if ($includeIssues) { $workerProjects[$projectName][$issueName][$period]['loggedBillableHours'] = ($workerProjects[$projectName][$issueName][$period]['loggedBillableHours'] ?? 0) + ($billableWorklog->getTimeSpentSeconds() * self::SECONDS_TO_HOURS); - $workerProjects[$projectName][$issueName]['linkToissue'][$billableWorklog->getIssue()->getProjectTrackerId()] = $billableWorklog->getIssue()->getLinkToIssue(); + $workerProjects[$projectName][$issueName]['linkToissue'][$issue->getProjectTrackerId()] = $issue->getLinkToIssue(); } $loggedBillableHours += ($billableWorklog->getTimeSpentSeconds() * self::SECONDS_TO_HOURS); } @@ -114,12 +119,17 @@ public function getInvoicingRateReport( // Tally up billed logged hours in gathered worklogs for current period $loggedBilledHours = 0; foreach ($billedWorklogs as $billedWorklog) { - $projectName = $billedWorklog->getProject()->getName(); - $issueName = $billedWorklog->getIssue()->getName(); + $project = $billedWorklog->getProject(); + $issue = $billedWorklog->getIssue(); + if (null === $project || null === $issue) { + continue; + } + $projectName = $project->getName(); + $issueName = $issue->getName(); $workerProjects[$projectName][$period]['loggedBilledHours'] = ($workerProjects[$projectName][$period]['loggedBilledHours'] ?? 0) + ($billedWorklog->getTimeSpentSeconds() * self::SECONDS_TO_HOURS); if ($includeIssues) { $workerProjects[$projectName][$issueName][$period]['loggedBilledHours'] = ($workerProjects[$projectName][$issueName][$period]['loggedBilledHours'] ?? 0) + ($billedWorklog->getTimeSpentSeconds() * self::SECONDS_TO_HOURS); - $workerProjects[$projectName][$issueName]['linkToissue'][$billedWorklog->getIssue()->getProjectTrackerId()] = $billedWorklog->getIssue()->getLinkToIssue(); + $workerProjects[$projectName][$issueName]['linkToissue'][$issue->getProjectTrackerId()] = $issue->getLinkToIssue(); } $loggedBilledHours += ($billedWorklog->getTimeSpentSeconds() * self::SECONDS_TO_HOURS); } @@ -146,16 +156,14 @@ public function getInvoicingRateReport( // Calculate and set the average for this period $average = round($periodSums[$period] / $periodCounts[$period], 4); - $invoicingRateReportData->periodAverages->set($period, $average); + $invoicingRateReportData->periodAverages->set((string) $period, $average); } $invoicingRateReportWorker->average = $loggedHoursSum > 0 ? round($loggedBilledHoursSum / $loggedHoursSum * 100, 4) : 0; $invoicingRateReportData->workers->add($invoicingRateReportWorker); - $invoicingRateReportWorker->projectData->set('projects', [ - $workerProjects, - ]); + $invoicingRateReportWorker->projectData->set('projects', $workerProjects); } // Calculate and set the total average @@ -197,7 +205,7 @@ private function getCurrentPeriodNumeric(PeriodTypeEnum $viewMode): int * @param int $year the year for the period * @param PeriodTypeEnum $viewMode the view mode to determine the dates of the period * - * @return array an array of dates for the given period + * @return array{dateFrom: \DateTime, dateTo: \DateTime} an array of dates for the given period */ private function getDatesOfPeriod(int $period, int $year, PeriodTypeEnum $viewMode): array { @@ -230,7 +238,7 @@ private function getReadablePeriod(int $period, PeriodTypeEnum $viewMode): strin * @param PeriodTypeEnum $viewMode the view mode to determine the periods * @param int $year the year containing the periods * - * @return array an array of periods + * @return array an array of periods */ private function getPeriods(PeriodTypeEnum $viewMode, int $year): array { @@ -247,7 +255,7 @@ private function getPeriods(PeriodTypeEnum $viewMode, int $year): array * @param InvoicingRateReportViewModeEnum $viewMode defines the view mode * @param string $workerIdentifier the worker's identifier * - * @return array the list of workloads matching the criteria defined by the parameters + * @return array> the list of workloads matching the criteria defined by the parameters */ private function getWorklogs(InvoicingRateReportViewModeEnum $viewMode, string $workerIdentifier, \DateTime $dateFrom, \DateTime $dateTo): array { diff --git a/src/Service/LeantimeApiService.php b/src/Service/LeantimeApiService.php index 1aff412b..14a98e69 100644 --- a/src/Service/LeantimeApiService.php +++ b/src/Service/LeantimeApiService.php @@ -75,13 +75,18 @@ public function update(string $className, bool $asyncJobQueue = false, ?\DateTim $dataProviders = $this->getEnabledLeantimeDataProviders(); foreach ($dataProviders as $dataProvider) { + $dataProviderId = $dataProvider->getId(); + if (null === $dataProviderId) { + continue; + } + $projectTrackerProjectIds = match ($className) { Project::class, Worker::class => null, default => $this->projectRepository->getProjectTrackerIdsByDataProviders([$dataProvider]), }; $this->messageBus->dispatch( - new LeantimeUpdateMessage($className, 0, $this::LIMIT, $dataProvider->getId(), $asyncJobQueue, $modifiedAfter, $projectTrackerProjectIds, $disableModifiedAtCheck), + new LeantimeUpdateMessage($className, 0, $this::LIMIT, $dataProviderId, $asyncJobQueue, $modifiedAfter, $projectTrackerProjectIds, $disableModifiedAtCheck), [new TransportNamesStamp($asyncJobQueue ? $this::QUEUE_ASYNC : $this::QUEUE_SYNC)], ); } @@ -92,8 +97,13 @@ public function delete(bool $asyncJobQueue = false, ?\DateTimeInterface $deleted $dataProviders = $this->getEnabledLeantimeDataProviders(); foreach ($dataProviders as $dataProvider) { + $dataProviderId = $dataProvider->getId(); + if (null === $dataProviderId) { + continue; + } + $this->messageBus->dispatch( - new LeantimeDeleteMessage($dataProvider->getId(), $asyncJobQueue, $deletedAfter), + new LeantimeDeleteMessage($dataProviderId, $asyncJobQueue, $deletedAfter), [new TransportNamesStamp($asyncJobQueue ? $this::QUEUE_ASYNC : $this::QUEUE_SYNC)], ); } @@ -107,6 +117,7 @@ public function deleteAsJob(int $dataProviderId, bool $asyncJobQueue = false, ?\ throw new NotFoundException("DataProvider with id: $dataProviderId not found"); } + /** @var list $types */ $types = [ self::TIMESHEETS, self::TICKETS, @@ -121,11 +132,11 @@ public function deleteAsJob(int $dataProviderId, bool $asyncJobQueue = false, ?\ // Get data from Leantime. $data = $this->fetchFromLeantime($dataProvider, 'deleted', $params); - $results = $data->results; + $results = $data['results']; // Queue delete. foreach ($types as $type) { - if (!isset($results->{$type})) { + if (!isset($results[$type])) { continue; } @@ -136,9 +147,9 @@ public function deleteAsJob(int $dataProviderId, bool $asyncJobQueue = false, ?\ self::TIMESHEETS => Worklog::class, }; - foreach ($results->{$type} as $result) { - $projectTrackerId = $result->id; - $deletedDate = $this->getLeanDateTime($result->deletedDate); + foreach ($results[$type] as $result) { + $projectTrackerId = $result['id']; + $deletedDate = $this->getLeanDateTime($result['deletedDate']); $this->messageBus->dispatch( new EntityRemovedFromDataProviderMessage($classname, $dataProviderId, $projectTrackerId, $deletedDate), @@ -148,6 +159,9 @@ public function deleteAsJob(int $dataProviderId, bool $asyncJobQueue = false, ?\ } } + /** + * @param array|null $projectTrackerProjectIds + */ public function updateAsJob(string $className, int $startId, int $limit, int $dataProviderId, ?array $projectTrackerProjectIds = null, bool $asyncJobQueue = false, ?\DateTimeInterface $modifiedAfter = null, bool $disableModifiedAtCheck = false): void { $dataProvider = $this->dataProviderRepository->find($dataProviderId); @@ -177,6 +191,7 @@ public function updateAsJob(string $className, int $startId, int $limit, int $da Issue::class => self::TICKETS, Worklog::class => self::TIMESHEETS, Worker::class => self::WORKERS, + default => throw new \InvalidArgumentException(sprintf('Unsupported class: %s', $className)), }; // Get data from Leantime. @@ -185,9 +200,9 @@ public function updateAsJob(string $className, int $startId, int $limit, int $da $fetchDate = new \DateTime(); // Queue upsert. - foreach ($data->results as $result) { + foreach ($data['results'] as $result) { $this->dispatchUpsertMessage($className, $result, $dataProviderId, $fetchDate, $asyncJobQueue, $dataProviderUrl, $disableModifiedAtCheck); - $startId = $result->id; + $startId = $result['id']; } $startId = $startId + 1; @@ -198,7 +213,7 @@ public function updateAsJob(string $className, int $startId, int $limit, int $da } // Queue next page. - if ($data->resultsCount === $limit) { + if ($data['resultsCount'] === $limit) { $this->messageBus->dispatch( new LeantimeUpdateMessage($className, $startId, $limit, $dataProviderId, $asyncJobQueue, $modifiedAfter, $projectTrackerProjectIds, $disableModifiedAtCheck), [new TransportNamesStamp($asyncJobQueue ? $this::QUEUE_ASYNC : $this::QUEUE_SYNC)], @@ -206,7 +221,10 @@ public function updateAsJob(string $className, int $startId, int $limit, int $da } } - private function dispatchUpsertMessage(string $className, object $data, int $dataProviderId, \DateTimeInterface $fetchDate, bool $asyncJobQueue = false, ?string $dataProviderUrl = null, bool $disableModifiedAtCheck = false): void + /** + * @param array $data + */ + private function dispatchUpsertMessage(string $className, array $data, int $dataProviderId, \DateTimeInterface $fetchDate, bool $asyncJobQueue = false, ?string $dataProviderUrl = null, bool $disableModifiedAtCheck = false): void { try { $message = match ($className) { @@ -231,98 +249,121 @@ private function dispatchUpsertMessage(string $className, object $data, int $dat } } - private function getProjectUpsertFromResult(object $result, int $dataProviderId, \DateTimeInterface $fetchDate, ?string $dataProviderUrl = null, bool $disableModifiedAtCheck = false): DataProviderProjectData + /** + * @param array $result + */ + private function getProjectUpsertFromResult(array $result, int $dataProviderId, \DateTimeInterface $fetchDate, ?string $dataProviderUrl = null, bool $disableModifiedAtCheck = false): DataProviderProjectData { - $projectTrackerId = (string) $result->id; + $projectTrackerId = (string) $result['id']; return new DataProviderProjectData( $dataProviderId, - $result->name, + $result['name'], $projectTrackerId, $this->linkToProject($projectTrackerId, $dataProviderUrl), $fetchDate, - $this->getLeanDateTime($result->modified), + $this->getLeanDateTime($result['modified']), $disableModifiedAtCheck, ); } - private function getVersionUpsertFromResult(object $result, int $dataProviderId, \DateTimeInterface $fetchDate, bool $disableModifiedAtCheck = false): DataProviderVersionData + /** + * @param array $result + */ + private function getVersionUpsertFromResult(array $result, int $dataProviderId, \DateTimeInterface $fetchDate, bool $disableModifiedAtCheck = false): DataProviderVersionData { return new DataProviderVersionData( $dataProviderId, - $result->name, - (string) $result->id, - (string) $result->projectId, + $result['name'], + (string) $result['id'], + (string) $result['projectId'], $fetchDate, - $this->getLeanDateTime($result->modified), + $this->getLeanDateTime($result['modified']), $disableModifiedAtCheck, ); } - private function getIssueUpsertFromResult(object $result, int $dataProviderId, \DateTimeInterface $fetchDate, ?string $dataProviderUrl = null, bool $disableModifiedAtCheck = false): DataProviderIssueData + /** + * @param array $result + */ + private function getIssueUpsertFromResult(array $result, int $dataProviderId, \DateTimeInterface $fetchDate, ?string $dataProviderUrl = null, bool $disableModifiedAtCheck = false): DataProviderIssueData { - $projectTrackerId = (string) $result->id; + $projectTrackerId = (string) $result['id']; return new DataProviderIssueData( $projectTrackerId, $dataProviderId, - (string) $result->projectId, - $result->name, - $result->tags, - $result->plannedHours, - $result->remainingHours, - $result->worker, - $this->convertStatusToEnum($result->status), - $this->getLeanDateTime($result->dueDate), - $this->getLeanDateTime($result->resolutionDate), + (string) $result['projectId'], + $result['name'], + $result['tags'], + $result['plannedHours'], + $result['remainingHours'], + $result['worker'], + $this->convertStatusToEnum($result['status']), + $this->getLeanDateTime($result['dueDate']), + $this->getLeanDateTime($result['resolutionDate']), $fetchDate, $this->linkToTicket($projectTrackerId, $dataProviderUrl), - $this->getLeanDateTime($result->modified), - $result->milestoneId, + $this->getLeanDateTime($result['modified']), + $result['milestoneId'], $disableModifiedAtCheck, ); } - private function getWorklogUpsertFromResult(object $result, int $dataProviderId, \DateTimeInterface $fetchDate, bool $disableModifiedAtCheck = false): DataProviderWorklogData + /** + * @param array $result + */ + private function getWorklogUpsertFromResult(array $result, int $dataProviderId, \DateTimeInterface $fetchDate, bool $disableModifiedAtCheck = false): DataProviderWorklogData { - $startedDate = $this->getLeanDateTime($result->workDate); + $startedDate = $this->getLeanDateTime($result['workDate']); if (null === $startedDate) { throw new NotAcceptableException('Worklog upsert not acceptable: startedDate is null'); } return new DataProviderWorklogData( - $result->id, + $result['id'], $dataProviderId, - (string) $result->ticketId, - $result->description, + (string) $result['ticketId'], + $result['description'], $startedDate, - $result->username, - $result->hours, - $result->kind, + $result['username'], + $result['hours'], + $result['kind'], $fetchDate, - $this->getLeanDateTime($result->modified), + $this->getLeanDateTime($result['modified']), $disableModifiedAtCheck, ); } - private function getWorkerUpsertFromResult(object $result, int $dataProviderId, \DateTimeInterface $fetchDate): DataProviderWorkerData + /** + * @param array $result + */ + private function getWorkerUpsertFromResult(array $result, int $dataProviderId, \DateTimeInterface $fetchDate): DataProviderWorkerData { return new DataProviderWorkerData( - $result->id, - $result->name, - $result->email, + $result['id'], + $result['name'], + $result['email'], ); } - private function fetchFromLeantime(DataProvider $dataProvider, string $type, array $params): object + /** + * @param array $params + * + * @return array + */ + private function fetchFromLeantime(DataProvider $dataProvider, string $type, array $params): array { $response = $this->post($dataProvider, $type, $params); - return json_decode($response->getContent(), null, 512, JSON_THROW_ON_ERROR); + return json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); } - private function post(DataProvider $dataProvider, $path, array $body): ResponseInterface + /** + * @param array $body + */ + private function post(DataProvider $dataProvider, string $path, array $body): ResponseInterface { return $this->httpClient->request('POST', $dataProvider->getUrl().$this::API_PATH_DATA.$path, [ 'headers' => [ @@ -354,6 +395,9 @@ private function convertStatusToEnum(string $statusString): IssueStatusEnum }; } + /** + * @return array + */ private function getEnabledLeantimeDataProviders(): array { return $this->dataProviderRepository->findBy(['class' => LeantimeApiService::class, 'enabled' => true]); diff --git a/src/Service/ManagementReportService.php b/src/Service/ManagementReportService.php index ab9c892e..f4da8cf4 100644 --- a/src/Service/ManagementReportService.php +++ b/src/Service/ManagementReportService.php @@ -2,6 +2,7 @@ namespace App\Service; +use App\Entity\Invoice; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use Symfony\Component\HttpFoundation\Response; @@ -15,7 +16,11 @@ public function __construct( ) { } - public function generateSpreadsheetCsvResponse(array $groupedInvoices, $dateInterval): Response + /** + * @param array> $groupedInvoices + * @param array{dateFrom: string, dateTo: string} $dateInterval + */ + public function generateSpreadsheetCsvResponse(array $groupedInvoices, array $dateInterval): Response { $values = []; // Add header. @@ -61,7 +66,10 @@ public function generateSpreadsheetCsvResponse(array $groupedInvoices, $dateInte return $response; } - private function calculateYear($quarterValues): string + /** + * @param array $quarterValues + */ + private function calculateYear(array $quarterValues): string { $sum = 0; foreach ($quarterValues as $invoice) { diff --git a/src/Service/PlanningService.php b/src/Service/PlanningService.php index 407972dc..8040f0ae 100644 --- a/src/Service/PlanningService.php +++ b/src/Service/PlanningService.php @@ -127,9 +127,9 @@ private function buildPlanningWeeks(PlanningData $planning, int $year, bool $pla /** * Sorts issues by week. * - * @param array $allIssues the array of all issues to sort + * @param array $allIssues the array of all issues to sort * - * @return array the sorted array of issues + * @return array> the sorted array of issues */ private function sortIssuesByWeek(array $allIssues): array { @@ -155,7 +155,10 @@ private function sortIssuesByWeek(array $allIssues): array return $weekIssues; } - private function processIssuesForWeek(PlanningData $planning, int $week, array $issues, ?bool $holidayPlanning = false): void + /** + * @param array $issues + */ + private function processIssuesForWeek(PlanningData $planning, int|string $week, array $issues, ?bool $holidayPlanning = false): void { foreach ($issues as $issueData) { if (!$holidayPlanning && IssueStatusEnum::DONE === $issueData->getStatus()) { @@ -168,7 +171,7 @@ private function processIssuesForWeek(PlanningData $planning, int $week, array $ } $projectKey = (string) $issueProject->getProjectTrackerId(); $projectDisplayName = $issueProject->getName() ?? self::UNNAMED_STR; - $hoursRemaining = $issueData->getHoursRemaining($issueData); + $hoursRemaining = $issueData->getHoursRemaining(); $assigneeData = $this->getAssigneeData($issueData); $assignee = $this->getOrCreateAssignee($planning->assignees, $assigneeData); @@ -210,6 +213,8 @@ private function processIssuesForWeek(PlanningData $planning, int $week, array $ /** * Get the assignee key and display name. + * + * @return array{key: string, displayName: string, weekNorm: int|float} */ private function getAssigneeData(IssueEntity $issue): array { @@ -239,7 +244,8 @@ private function getAssigneeData(IssueEntity $issue): array /** * Gets or creates an Assignee object in an ArrayCollection. * - * @param ArrayCollection $assignees the ArrayCollection containing the Assignee objects + * @param ArrayCollection $assignees the ArrayCollection containing the Assignee objects + * @param array{key: string, displayName: string, weekNorm: int|float} $assigneeData * * @return Assignee the retrieved or created Assignee object */ @@ -317,7 +323,7 @@ private function getOrCreateAssigneeProject(ArrayCollection $projects, string $p */ private function sortAssigneeCollectionByDisplayName(ArrayCollection $collection): ArrayCollection { - /** @var \ArrayIterator $iterator */ + /** @var \ArrayIterator $iterator */ $iterator = $collection->getIterator(); $iterator->uasort(function ($a, $b) { return mb_strtolower($a->displayName) <=> mb_strtolower($b->displayName); @@ -335,7 +341,7 @@ private function sortAssigneeCollectionByDisplayName(ArrayCollection $collection */ private function sortProjectCollectionByDisplayName(ArrayCollection $collection): ArrayCollection { - /** @var \ArrayIterator $iterator */ + /** @var \ArrayIterator $iterator */ $iterator = $collection->getIterator(); $iterator->uasort(function ($a, $b) { return mb_strtolower($a->displayName) <=> mb_strtolower($b->displayName); diff --git a/src/Service/ProjectBillingService.php b/src/Service/ProjectBillingService.php index cef1b1c3..4ecfb724 100644 --- a/src/Service/ProjectBillingService.php +++ b/src/Service/ProjectBillingService.php @@ -40,6 +40,9 @@ public function __construct( * @throws EconomicsException * @throws \Exception */ + /** + * @return array + */ public function getIssuesNotIncludedInProjectBilling(ProjectBilling $projectBilling): array { $project = $projectBilling->getProject(); diff --git a/src/Service/SubscriptionHandlerService.php b/src/Service/SubscriptionHandlerService.php index 4fc5f70e..5b474a02 100644 --- a/src/Service/SubscriptionHandlerService.php +++ b/src/Service/SubscriptionHandlerService.php @@ -42,7 +42,7 @@ public function __construct( /** * Send a notification email. * - * @param array $notification the notification data + * @param array $notification the notification data * * @throws TransportExceptionInterface */ @@ -105,7 +105,6 @@ public function handleSubscription(Subscription $subscription, \DateTime $fromDa break; default: throw new \Exception('Report type is not yet supported'); - break; } } @@ -136,7 +135,7 @@ private function getProject(?int $projectId): ?Project */ private function getVersion(?int $versionId): ?Version { - return $versionId ? $this->versionRepository->findOneBy(['versionId' => $versionId]) : null; + return $versionId ? $this->versionRepository->find($versionId) : null; } /** @@ -157,6 +156,9 @@ private function getVersion(?int $versionId): ?Version * @throws SyntaxError * @throws \Exception */ + /** + * @return array + */ private function prepareMailData(Subscription $subscription, \DateTime $fromDate, \DateTime $toDate, Project $project, HourReportData $reportData, string $email): array { $renderedReport = $this->environment->render('subscription/subscription_hour_report.html.twig', [ diff --git a/src/Service/WorkloadReportService.php b/src/Service/WorkloadReportService.php index 799e6932..f7cfe0e0 100644 --- a/src/Service/WorkloadReportService.php +++ b/src/Service/WorkloadReportService.php @@ -110,7 +110,7 @@ public function getWorkloadReport(int $year, PeriodTypeEnum $viewPeriodType = Pe // Calculate and set the average for this period $average = round($periodSums[$period] / $periodCounts[$period], 1); - $workloadReportData->periodAverages->set($period, $average); + $workloadReportData->periodAverages->set((string) $period, $average); } $workloadReportWorker->average = $expectedWorkloadSum > 0 ? round($loggedHoursSum / $expectedWorkloadSum * 100, 1) : 0; @@ -179,7 +179,7 @@ private function getCurrentPeriodNumeric(PeriodTypeEnum $viewMode, int $year): i * @param int $year the year for the period * @param PeriodTypeEnum $viewMode the view mode to determine the dates of the period * - * @return array an array of dates for the given period + * @return array{dateFrom: \DateTime, dateTo: \DateTime} an array of dates for the given period */ private function getDatesOfPeriod(int $period, int $year, PeriodTypeEnum $viewMode): array { @@ -212,7 +212,7 @@ private function getReadablePeriod(int $period, PeriodTypeEnum $viewMode): strin * @param PeriodTypeEnum $viewMode the view mode to determine the periods * @param int $year the year containing the periods * - * @return array an array of periods + * @return array an array of periods */ private function getPeriods(PeriodTypeEnum $viewMode, int $year): array { @@ -229,7 +229,7 @@ private function getPeriods(PeriodTypeEnum $viewMode, int $year): array * @param ViewModeEnum $viewMode defines the view mode * @param string $workerIdentifier the worker's identifier * - * @return array the list of workloads matching the criteria defined by the parameters + * @return array the list of workloads matching the criteria defined by the parameters */ private function getWorklogs(ViewModeEnum $viewMode, string $workerIdentifier, \DateTime $dateFrom, \DateTime $dateTo): array { diff --git a/tests/Integration/Controller/AbstractControllerTestCase.php b/tests/Integration/Controller/AbstractControllerTestCase.php index af006b1b..16d70706 100644 --- a/tests/Integration/Controller/AbstractControllerTestCase.php +++ b/tests/Integration/Controller/AbstractControllerTestCase.php @@ -24,14 +24,17 @@ protected function getUserWithRole(string $role): User $email = self::ROLE_USER_FIXTURES[$role] ?? throw new \InvalidArgumentException(sprintf('No fixture user for role %s.', $role)); - $user = static::getContainer()->get(UserRepository::class)->findOneBy(['email' => $email]); - if (null === $user) { - throw new \RuntimeException(sprintf('Fixture user %s not found; run `task fixtures`.', $email)); - } + $userRepository = static::getContainer()->get(UserRepository::class); + \assert($userRepository instanceof UserRepository); + $user = $userRepository->findOneBy(['email' => $email]); + $this->assertInstanceOf(User::class, $user, sprintf('Fixture user %s not found; run `task fixtures`.', $email)); return $user; } + /** + * @param string[] $roles + */ protected function createClientLoggedInAs(array $roles): KernelBrowser { self::ensureKernelShutdown(); @@ -50,6 +53,9 @@ protected function assertAnonymousRedirectsToLogin(string $url): void $this->assertResponseRedirects(); } + /** + * @param string[] $roles + */ protected function assertGrantedFor(string $url, array $roles): void { $client = $this->createClientLoggedInAs($roles); @@ -59,6 +65,9 @@ protected function assertGrantedFor(string $url, array $roles): void $this->assertResponseIsSuccessful(sprintf('Expected 2xx at %s for roles [%s]', $url, implode(',', $roles))); } + /** + * @param string[] $roles + */ protected function assertDeniedFor(string $url, array $roles): void { $client = $this->createClientLoggedInAs($roles); @@ -69,6 +78,9 @@ protected function assertDeniedFor(string $url, array $roles): void /** * Smoke matrix: anonymous redirects, an allowed role gets 200, a denied role gets 403. + * + * @param string[] $allowedRoles + * @param string[] $deniedRoles */ protected function assertSmokeMatrix(string $url, array $allowedRoles, array $deniedRoles): void { diff --git a/tests/Integration/Controller/HourReportFilterTest.php b/tests/Integration/Controller/HourReportFilterTest.php index 96ad9dfa..ea52bf8f 100644 --- a/tests/Integration/Controller/HourReportFilterTest.php +++ b/tests/Integration/Controller/HourReportFilterTest.php @@ -8,7 +8,9 @@ class HourReportFilterTest extends AbstractControllerTestCase { public function testFilterSubmissionRendersReportForSelectedProject(): void { - $project = static::getContainer()->get(ProjectRepository::class)->getIncluded() + $projectRepository = static::getContainer()->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); + $project = $projectRepository->getIncluded() ->setMaxResults(1)->getQuery()->getOneOrNullResult(); $this->assertNotNull($project, 'Expected an included project from fixtures.'); diff --git a/tests/Integration/Controller/InvoiceEntryFlowTest.php b/tests/Integration/Controller/InvoiceEntryFlowTest.php index 5c2a6196..989b03d5 100644 --- a/tests/Integration/Controller/InvoiceEntryFlowTest.php +++ b/tests/Integration/Controller/InvoiceEntryFlowTest.php @@ -152,8 +152,11 @@ private function setupInvoiceWithManualEntry(bool $recorded = false): array { $container = static::getContainer(); $em = $container->get(EntityManagerInterface::class); + \assert($em instanceof EntityManagerInterface); - $project = $container->get(ProjectRepository::class)->getIncluded() + $projectRepository = $container->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); + $project = $projectRepository->getIncluded() ->setMaxResults(1)->getQuery()->getOneOrNullResult(); $this->assertNotNull($project, 'Expected an included project from fixtures.'); @@ -181,18 +184,26 @@ private function setupInvoiceWithManualEntry(bool $recorded = false): array $entryId = $entry->getId(); $em->clear(); - $invoice = $container->get(InvoiceRepository::class)->find($invoiceId); - $entry = $container->get(InvoiceEntryRepository::class)->find($entryId); + $invoiceRepository = $container->get(InvoiceRepository::class); + \assert($invoiceRepository instanceof InvoiceRepository); + $invoice = $invoiceRepository->find($invoiceId); $this->assertInstanceOf(Invoice::class, $invoice); + $invoiceEntryRepository = $container->get(InvoiceEntryRepository::class); + \assert($invoiceEntryRepository instanceof InvoiceEntryRepository); + $entry = $invoiceEntryRepository->find($entryId); $this->assertInstanceOf(InvoiceEntry::class, $entry); return [$invoice, $entry]; } - private function markInvoiceRecorded(int $invoiceId): void + private function markInvoiceRecorded(?int $invoiceId): void { + $this->assertNotNull($invoiceId); $em = static::getContainer()->get(EntityManagerInterface::class); - $invoice = static::getContainer()->get(InvoiceRepository::class)->find($invoiceId); + \assert($em instanceof EntityManagerInterface); + $invoiceRepository = static::getContainer()->get(InvoiceRepository::class); + \assert($invoiceRepository instanceof InvoiceRepository); + $invoice = $invoiceRepository->find($invoiceId); $this->assertInstanceOf(Invoice::class, $invoice); $invoice->setRecorded(true); $invoice->setRecordedDate(new \DateTime()); @@ -200,11 +211,16 @@ private function markInvoiceRecorded(int $invoiceId): void $em->clear(); } - private function reloadEntry(int $id): ?InvoiceEntry + private function reloadEntry(?int $id): ?InvoiceEntry { + $this->assertNotNull($id); $em = static::getContainer()->get(EntityManagerInterface::class); + \assert($em instanceof EntityManagerInterface); $em->clear(); - return static::getContainer()->get(InvoiceEntryRepository::class)->find($id); + $invoiceEntryRepository = static::getContainer()->get(InvoiceEntryRepository::class); + \assert($invoiceEntryRepository instanceof InvoiceEntryRepository); + + return $invoiceEntryRepository->find($id); } } diff --git a/tests/Integration/Controller/InvoiceFlowTest.php b/tests/Integration/Controller/InvoiceFlowTest.php index 144b239e..60ed4380 100644 --- a/tests/Integration/Controller/InvoiceFlowTest.php +++ b/tests/Integration/Controller/InvoiceFlowTest.php @@ -14,7 +14,9 @@ public function testCreateInvoicePersistsAndRedirectsToEdit(): void $crawler = $client->request('GET', '/admin/invoices/new'); $this->assertResponseIsSuccessful(); - $project = static::getContainer()->get(ProjectRepository::class)->getIncluded() + $projectRepository = static::getContainer()->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); + $project = $projectRepository->getIncluded() ->setMaxResults(1)->getQuery()->getOneOrNullResult(); $this->assertNotNull($project, 'Expected an included project from fixtures.'); @@ -25,17 +27,25 @@ public function testCreateInvoicePersistsAndRedirectsToEdit(): void $client->submit($form); $this->assertResponseRedirects(); - $this->assertMatchesRegularExpression('#/admin/invoices/\d+/edit$#', $client->getResponse()->headers->get('Location')); + $location = $client->getResponse()->headers->get('Location'); + $this->assertNotNull($location); + $this->assertMatchesRegularExpression('#/admin/invoices/\d+/edit$#', $location); - $created = static::getContainer()->get(InvoiceRepository::class)->findOneBy(['name' => $name]); + $invoiceRepository = static::getContainer()->get(InvoiceRepository::class); + \assert($invoiceRepository instanceof InvoiceRepository); + $created = $invoiceRepository->findOneBy(['name' => $name]); $this->assertInstanceOf(Invoice::class, $created); - $this->assertSame($project->getId(), $created->getProject()->getId()); + $createdProject = $created->getProject(); + $this->assertNotNull($createdProject); + $this->assertSame($project->getId(), $createdProject->getId()); $this->assertFalse($created->isRecorded()); } public function testEditExistingInvoiceRendersForm(): void { - $invoice = static::getContainer()->get(InvoiceRepository::class)->findOneBy([]); + $invoiceRepository = static::getContainer()->get(InvoiceRepository::class); + \assert($invoiceRepository instanceof InvoiceRepository); + $invoice = $invoiceRepository->findOneBy([]); $this->assertNotNull($invoice, 'Expected at least one invoice from fixtures.'); $client = $this->createClientLoggedInAs(['ROLE_INVOICE']); diff --git a/tests/Integration/Controller/InvoiceFullFlowTest.php b/tests/Integration/Controller/InvoiceFullFlowTest.php index 80e744da..ae8a9748 100644 --- a/tests/Integration/Controller/InvoiceFullFlowTest.php +++ b/tests/Integration/Controller/InvoiceFullFlowTest.php @@ -24,12 +24,15 @@ public function testFullInvoiceLifecycle(): void $client = $this->createClientLoggedInAs(['ROLE_INVOICE']); $container = static::getContainer(); - $project = $container->get(ProjectRepository::class)->getIncluded() + $projectRepository = $container->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); + $project = $projectRepository->getIncluded() ->setMaxResults(1)->getQuery()->getOneOrNullResult(); $this->assertNotNull($project, 'Expected an included project from fixtures.'); - $internalClient = $container->get(ClientRepository::class) - ->findOneBy(['type' => ClientTypeEnum::INTERNAL]); + $clientRepository = $container->get(ClientRepository::class); + \assert($clientRepository instanceof ClientRepository); + $internalClient = $clientRepository->findOneBy(['type' => ClientTypeEnum::INTERNAL]); $this->assertInstanceOf(Client::class, $internalClient, 'Expected an internal client fixture.'); // 1. Create invoice. @@ -47,6 +50,7 @@ public function testFullInvoiceLifecycle(): void $this->assertNotNull($location); $this->assertMatchesRegularExpression('#/admin/invoices/(\d+)/edit$#', $location, 'Expected redirect to invoice edit.'); preg_match('#/admin/invoices/(\d+)/edit$#', $location, $matches); + \assert(isset($matches[1])); $invoiceId = (int) $matches[1]; // 2. Edit invoice and set all fields. @@ -72,7 +76,6 @@ public function testFullInvoiceLifecycle(): void $this->assertResponseIsSuccessful(); $invoice = $this->reloadInvoice($invoiceId); - $this->assertInstanceOf(Invoice::class, $invoice); $this->assertSame($finalName, $invoice->getName()); $this->assertSame($description, $invoice->getDescription()); $this->assertNotNull($invoice->getClient()); @@ -99,7 +102,6 @@ public function testFullInvoiceLifecycle(): void ); $manualEntry = $this->reloadInvoiceEntry($manualEntryId); - $this->assertInstanceOf(InvoiceEntry::class, $manualEntry); $this->assertSame(InvoiceEntryTypeEnum::MANUAL, $manualEntry->getEntryType()); $this->assertSame($manualProduct, $manualEntry->getProduct()); $this->assertEqualsWithDelta(750.0, $manualEntry->getPrice(), 0.001); @@ -122,7 +124,6 @@ public function testFullInvoiceLifecycle(): void ); $productEntry = $this->reloadInvoiceEntry($productEntryId); - $this->assertInstanceOf(InvoiceEntry::class, $productEntry); $this->assertSame(InvoiceEntryTypeEnum::PRODUCT, $productEntry->getEntryType()); $this->assertEqualsWithDelta(1200.0, $productEntry->getTotalPrice(), 0.001); @@ -141,12 +142,12 @@ public function testFullInvoiceLifecycle(): void ); $worklogEntry = $this->reloadInvoiceEntry($worklogEntryId); - $this->assertInstanceOf(InvoiceEntry::class, $worklogEntry); $this->assertSame(InvoiceEntryTypeEnum::WORKLOG, $worklogEntry->getEntryType()); $this->assertSame(0.0, (float) $worklogEntry->getAmount()); // 6. Attach worklogs to the WORKLOG entry. $worklogRepository = static::getContainer()->get(WorklogRepository::class); + \assert($worklogRepository instanceof WorklogRepository); $unbilled = $worklogRepository->findBy( ['project' => $project, 'isBilled' => false], ['id' => 'ASC'], @@ -212,8 +213,10 @@ public function testFullInvoiceLifecycle(): void // Worklogs attached to the WORKLOG entry should be marked billed. $selectedIds = array_map(static fn (Worklog $wl) => $wl->getId(), $selected); $em = static::getContainer()->get(EntityManagerInterface::class); + \assert($em instanceof EntityManagerInterface); $em->clear(); $worklogRepository = static::getContainer()->get(WorklogRepository::class); + \assert($worklogRepository instanceof WorklogRepository); foreach ($selectedIds as $wlId) { $wl = $worklogRepository->find($wlId); $this->assertNotNull($wl); @@ -267,31 +270,50 @@ private function submitNewEntry( if (str_contains($expectedRedirectPattern, '/entries/\d+/edit')) { preg_match('#/entries/(\d+)/edit$#', $location, $matches); + \assert(isset($matches[1])); return (int) $matches[1]; } // MANUAL redirects back to invoice edit — fetch the most recently created entry for this invoice. $repository = static::getContainer()->get(InvoiceEntryRepository::class); + \assert($repository instanceof InvoiceEntryRepository); $latest = $repository->findBy([], ['id' => 'DESC'], 1); $this->assertNotEmpty($latest); - return $latest[0]->getId(); + $id = $latest[0]->getId(); + $this->assertNotNull($id); + + return $id; } - private function reloadInvoice(int $id): ?Invoice + private function reloadInvoice(int $id): Invoice { $em = static::getContainer()->get(EntityManagerInterface::class); + \assert($em instanceof EntityManagerInterface); $em->clear(); - return static::getContainer()->get(InvoiceRepository::class)->find($id); + $invoiceRepository = static::getContainer()->get(InvoiceRepository::class); + \assert($invoiceRepository instanceof InvoiceRepository); + + $invoice = $invoiceRepository->find($id); + $this->assertInstanceOf(Invoice::class, $invoice); + + return $invoice; } - private function reloadInvoiceEntry(int $id): ?InvoiceEntry + private function reloadInvoiceEntry(int $id): InvoiceEntry { $em = static::getContainer()->get(EntityManagerInterface::class); + \assert($em instanceof EntityManagerInterface); $em->clear(); - return static::getContainer()->get(InvoiceEntryRepository::class)->find($id); + $invoiceEntryRepository = static::getContainer()->get(InvoiceEntryRepository::class); + \assert($invoiceEntryRepository instanceof InvoiceEntryRepository); + + $entry = $invoiceEntryRepository->find($id); + $this->assertInstanceOf(InvoiceEntry::class, $entry); + + return $entry; } } diff --git a/tests/Integration/Controller/ProjectBillingFlowTest.php b/tests/Integration/Controller/ProjectBillingFlowTest.php index 8f118101..d5b35b8f 100644 --- a/tests/Integration/Controller/ProjectBillingFlowTest.php +++ b/tests/Integration/Controller/ProjectBillingFlowTest.php @@ -14,7 +14,9 @@ public function testCreateProjectBillingPersistsAndRedirectsToEdit(): void $crawler = $client->request('GET', '/admin/project-billing/new'); $this->assertResponseIsSuccessful(); - $project = static::getContainer()->get(ProjectRepository::class)->getIncluded() + $projectRepository = static::getContainer()->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); + $project = $projectRepository->getIncluded() ->setMaxResults(1)->getQuery()->getOneOrNullResult(); $this->assertNotNull($project, 'Expected an included project from fixtures.'); @@ -27,10 +29,16 @@ public function testCreateProjectBillingPersistsAndRedirectsToEdit(): void $client->submit($form); $this->assertResponseRedirects(); - $this->assertMatchesRegularExpression('#/admin/project-billing/\d+/edit$#', $client->getResponse()->headers->get('Location')); + $location = $client->getResponse()->headers->get('Location'); + $this->assertNotNull($location); + $this->assertMatchesRegularExpression('#/admin/project-billing/\d+/edit$#', $location); - $created = static::getContainer()->get(ProjectBillingRepository::class)->findOneBy(['name' => $name]); + $projectBillingRepository = static::getContainer()->get(ProjectBillingRepository::class); + \assert($projectBillingRepository instanceof ProjectBillingRepository); + $created = $projectBillingRepository->findOneBy(['name' => $name]); $this->assertInstanceOf(ProjectBilling::class, $created); - $this->assertSame($project->getId(), $created->getProject()->getId()); + $createdProject = $created->getProject(); + $this->assertNotNull($createdProject); + $this->assertSame($project->getId(), $createdProject->getId()); } } diff --git a/tests/Integration/Controller/ProjectBillingFullFlowTest.php b/tests/Integration/Controller/ProjectBillingFullFlowTest.php index e78144c8..57daa8ba 100644 --- a/tests/Integration/Controller/ProjectBillingFullFlowTest.php +++ b/tests/Integration/Controller/ProjectBillingFullFlowTest.php @@ -20,7 +20,9 @@ public function testFullProjectBillingLifecycle(): void // versions `PB-0-0`/`PB-0-1` (matching the data-provider-0 clients) and have // DONE-status issues with worklogs, but ProjectBillingServiceTest depends on // `project-0-0`'s worklogs staying unbilled, so we must not record against it. - $project = $container->get(ProjectRepository::class)->findOneBy(['name' => 'project-0-2']); + $projectRepository = $container->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); + $project = $projectRepository->findOneBy(['name' => 'project-0-2']); $this->assertNotNull($project, 'Expected fixture project `project-0-2`.'); $periodStart = '2020-01-01'; @@ -44,12 +46,12 @@ public function testFullProjectBillingLifecycle(): void $location = (string) $client->getResponse()->headers->get('Location'); $this->assertMatchesRegularExpression('#/admin/project-billing/(\d+)/edit$#', $location); preg_match('#/admin/project-billing/(\d+)/edit$#', $location, $matches); + \assert(isset($matches[1])); $projectBillingId = (int) $matches[1]; // The CreateProjectBillingMessage handler runs synchronously (no async routing in test env) // and should have generated invoices from the project's PB-* version issues. $projectBilling = $this->reload($projectBillingId); - $this->assertInstanceOf(ProjectBilling::class, $projectBilling); $this->assertSame($initialName, $projectBilling->getName()); $this->assertFalse((bool) $projectBilling->isRecorded()); $this->assertSame($initialDescription, $projectBilling->getDescription()); @@ -93,9 +95,9 @@ public function testFullProjectBillingLifecycle(): void $projectBilling = $this->reload($projectBillingId); $this->assertSame($updatedName, $projectBilling->getName()); $this->assertSame($updatedDescription, $projectBilling->getDescription()); - $this->assertSame( + $this->assertCount( $initialInvoiceCount, - $projectBilling->getInvoices()->count(), + $projectBilling->getInvoices(), 'Update should regenerate the same set of invoices for unchanged fixture data.', ); foreach ($projectBilling->getInvoices() as $invoice) { @@ -158,17 +160,19 @@ private function submitRecordForm(KernelBrowser $client, int $projectBillingId, $client->submit($form); } - private function reload(int $id): ?ProjectBilling + private function reload(int $id): ProjectBilling { $em = static::getContainer()->get(EntityManagerInterface::class); + \assert($em instanceof EntityManagerInterface); $em->clear(); - $pb = static::getContainer()->get(ProjectBillingRepository::class)->find($id); - if (null !== $pb) { - $pb->getInvoices()->count(); - foreach ($pb->getInvoices() as $invoice) { - $invoice->getInvoiceEntries()->count(); - } + $projectBillingRepository = static::getContainer()->get(ProjectBillingRepository::class); + \assert($projectBillingRepository instanceof ProjectBillingRepository); + $pb = $projectBillingRepository->find($id); + $this->assertInstanceOf(ProjectBilling::class, $pb); + $pb->getInvoices()->count(); + foreach ($pb->getInvoices() as $invoice) { + $invoice->getInvoiceEntries()->count(); } return $pb; diff --git a/tests/Integration/Repository/AccountRepositoryTest.php b/tests/Integration/Repository/AccountRepositoryTest.php index 25e14996..15bba934 100644 --- a/tests/Integration/Repository/AccountRepositoryTest.php +++ b/tests/Integration/Repository/AccountRepositoryTest.php @@ -4,7 +4,6 @@ use App\Model\Invoices\NameFilterData; use App\Repository\AccountRepository; -use Knp\Component\Pager\Pagination\PaginationInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class AccountRepositoryTest extends KernelTestCase @@ -14,14 +13,15 @@ class AccountRepositoryTest extends KernelTestCase protected function setUp(): void { self::bootKernel(); - $this->repository = self::getContainer()->get(AccountRepository::class); + $repository = self::getContainer()->get(AccountRepository::class); + \assert($repository instanceof AccountRepository); + $this->repository = $repository; } public function testGetAllChoices(): void { $result = $this->repository->getAllChoices(); - $this->assertIsArray($result); $this->assertGreaterThanOrEqual(2, \count($result)); $this->assertArrayHasKey('ACC001: Test Account 1', $result); @@ -36,7 +36,6 @@ public function testGetFilteredPaginationNoFilter(): void $filterData = new NameFilterData(); $result = $this->repository->getFilteredPagination($filterData); - $this->assertInstanceOf(PaginationInterface::class, $result); $this->assertGreaterThanOrEqual(2, $result->getTotalItemCount()); } diff --git a/tests/Integration/Repository/ClientRepositoryTest.php b/tests/Integration/Repository/ClientRepositoryTest.php index 9032dfff..c34d0e4d 100644 --- a/tests/Integration/Repository/ClientRepositoryTest.php +++ b/tests/Integration/Repository/ClientRepositoryTest.php @@ -4,7 +4,6 @@ use App\Model\Invoices\ClientFilterData; use App\Repository\ClientRepository; -use Knp\Component\Pager\Pagination\PaginationInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class ClientRepositoryTest extends KernelTestCase @@ -14,7 +13,9 @@ class ClientRepositoryTest extends KernelTestCase protected function setUp(): void { self::bootKernel(); - $this->repository = self::getContainer()->get(ClientRepository::class); + $repository = self::getContainer()->get(ClientRepository::class); + \assert($repository instanceof ClientRepository); + $this->repository = $repository; } public function testGetFilteredPaginationNoFilter(): void @@ -22,7 +23,6 @@ public function testGetFilteredPaginationNoFilter(): void $filterData = new ClientFilterData(); $result = $this->repository->getFilteredPagination($filterData); - $this->assertInstanceOf(PaginationInterface::class, $result); $this->assertGreaterThanOrEqual(4, $result->getTotalItemCount()); } @@ -34,7 +34,7 @@ public function testGetFilteredPaginationByName(): void $this->assertGreaterThanOrEqual(2, $result->getTotalItemCount()); foreach ($result as $client) { - $this->assertStringContainsString('client 0', $client->getName()); + $this->assertStringContainsString('client 0', (string) $client->getName()); } } @@ -46,7 +46,7 @@ public function testGetFilteredPaginationByContact(): void $this->assertGreaterThan(0, $result->getTotalItemCount()); foreach ($result as $client) { - $this->assertStringContainsString('Kontakt Kontaktesen 0', $client->getContact()); + $this->assertStringContainsString('Kontakt Kontaktesen 0', (string) $client->getContact()); } } } diff --git a/tests/Integration/Repository/CybersecurityAgreementRepositoryTest.php b/tests/Integration/Repository/CybersecurityAgreementRepositoryTest.php index 152467bb..c87d9f1e 100644 --- a/tests/Integration/Repository/CybersecurityAgreementRepositoryTest.php +++ b/tests/Integration/Repository/CybersecurityAgreementRepositoryTest.php @@ -2,7 +2,6 @@ namespace App\Tests\Integration\Repository; -use App\Entity\CybersecurityAgreement; use App\Repository\CybersecurityAgreementRepository; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -13,7 +12,9 @@ class CybersecurityAgreementRepositoryTest extends KernelTestCase protected function setUp(): void { self::bootKernel(); - $this->repository = self::getContainer()->get(CybersecurityAgreementRepository::class); + $repository = self::getContainer()->get(CybersecurityAgreementRepository::class); + \assert($repository instanceof CybersecurityAgreementRepository); + $this->repository = $repository; } public function testFindAllIndexed(): void @@ -21,10 +22,8 @@ public function testFindAllIndexed(): void $result = $this->repository->findAllIndexed(); $this->assertNotEmpty($result); - $this->assertIsArray($result); foreach ($result as $key => $entity) { - $this->assertInstanceOf(CybersecurityAgreement::class, $entity); $this->assertEquals($entity->getId(), $key); } } diff --git a/tests/Integration/Repository/InvoiceRepositoryTest.php b/tests/Integration/Repository/InvoiceRepositoryTest.php index f8e6ca53..ac239e4f 100644 --- a/tests/Integration/Repository/InvoiceRepositoryTest.php +++ b/tests/Integration/Repository/InvoiceRepositoryTest.php @@ -2,10 +2,8 @@ namespace App\Tests\Integration\Repository; -use App\Entity\Invoice; use App\Model\Invoices\InvoiceFilterData; use App\Repository\InvoiceRepository; -use Knp\Component\Pager\Pagination\PaginationInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class InvoiceRepositoryTest extends KernelTestCase @@ -15,7 +13,9 @@ class InvoiceRepositoryTest extends KernelTestCase protected function setUp(): void { self::bootKernel(); - $this->repository = self::getContainer()->get(InvoiceRepository::class); + $repository = self::getContainer()->get(InvoiceRepository::class); + \assert($repository instanceof InvoiceRepository); + $this->repository = $repository; } public function testGetByRecordedDateBetween(): void @@ -27,7 +27,6 @@ public function testGetByRecordedDateBetween(): void $this->assertNotEmpty($result); foreach ($result as $invoice) { - $this->assertInstanceOf(Invoice::class, $invoice); $this->assertTrue($invoice->isRecorded()); $this->assertNotNull($invoice->getRecordedDate()); } @@ -49,7 +48,6 @@ public function testGetFilteredPaginationRecorded(): void $filterData->recorded = true; $result = $this->repository->getFilteredPagination($filterData); - $this->assertInstanceOf(PaginationInterface::class, $result); $this->assertGreaterThanOrEqual(2, $result->getTotalItemCount()); foreach ($result as $invoice) { $this->assertTrue($invoice->isRecorded()); @@ -77,7 +75,7 @@ public function testGetFilteredPaginationByQuery(): void $this->assertGreaterThanOrEqual(1, $result->getTotalItemCount()); foreach ($result as $invoice) { - $this->assertStringContainsString('Invoice Beta', $invoice->getName()); + $this->assertStringContainsString('Invoice Beta', (string) $invoice->getName()); } } diff --git a/tests/Integration/Repository/IssueRepositoryTest.php b/tests/Integration/Repository/IssueRepositoryTest.php index 27998bf0..4da94e0b 100644 --- a/tests/Integration/Repository/IssueRepositoryTest.php +++ b/tests/Integration/Repository/IssueRepositoryTest.php @@ -2,7 +2,6 @@ namespace App\Tests\Integration\Repository; -use App\Entity\Issue; use App\Entity\Version; use App\Entity\WorkerGroup; use App\Enum\IssueStatusEnum; @@ -11,13 +10,10 @@ use App\Repository\ProjectRepository; use App\Repository\VersionRepository; use App\Repository\WorkerGroupRepository; -use Doctrine\ORM\EntityManagerInterface; -use Knp\Component\Pager\Pagination\PaginationInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class IssueRepositoryTest extends KernelTestCase { - private EntityManagerInterface $entityManager; private IssueRepository $repository; private ProjectRepository $projectRepository; @@ -25,9 +21,12 @@ protected function setUp(): void { self::bootKernel(); $container = self::getContainer(); - $this->entityManager = $container->get(EntityManagerInterface::class); - $this->repository = $container->get(IssueRepository::class); - $this->projectRepository = $container->get(ProjectRepository::class); + $repository = $container->get(IssueRepository::class); + \assert($repository instanceof IssueRepository); + $this->repository = $repository; + $projectRepository = $container->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); + $this->projectRepository = $projectRepository; } public function testGetFilteredPaginationNoFilter(): void @@ -35,7 +34,6 @@ public function testGetFilteredPaginationNoFilter(): void $filterData = new IssueFilterData(); $result = $this->repository->getFilteredPagination($filterData); - $this->assertInstanceOf(PaginationInterface::class, $result); $this->assertGreaterThan(0, $result->getTotalItemCount()); } @@ -47,20 +45,23 @@ public function testGetFilteredPaginationByName(): void $this->assertGreaterThan(0, $result->getTotalItemCount()); foreach ($result as $issue) { - $this->assertStringContains('issue-0-0', $issue->getName()); + $this->assertStringContains('issue-0-0', (string) $issue->getName()); } } public function testGetFilteredPaginationByProject(): void { $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $filterData = new IssueFilterData(); $filterData->project = $project; $result = $this->repository->getFilteredPagination($filterData); $this->assertGreaterThan(0, $result->getTotalItemCount()); foreach ($result as $issue) { - $this->assertEquals($project->getId(), $issue->getProject()->getId()); + $issueProject = $issue->getProject(); + $this->assertNotNull($issueProject); + $this->assertEquals($project->getId(), $issueProject->getId()); } } @@ -68,9 +69,9 @@ public function testFindEpicOptionsByProject(): void { // project-0-0 has issue-0-0 linked to 'Epic 1' $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $result = $this->repository->findEpicOptionsByProject($project); - $this->assertIsArray($result); $this->assertArrayHasKey('Epic 1', $result); } @@ -78,6 +79,7 @@ public function testGetClosedIssuesFromInterval(): void { // Even-index projects have DONE status issues with resolutionDate=today $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $periodStart = new \DateTime('-1 day'); $periodEnd = new \DateTime('+1 day'); @@ -93,6 +95,7 @@ public function testGetClosedIssuesFromIntervalNoResults(): void { // Odd-index projects have NEW status issues $project = $this->projectRepository->findOneBy(['name' => 'project-0-1']); + $this->assertNotNull($project); $periodStart = new \DateTime('-1 day'); $periodEnd = new \DateTime('+1 day'); @@ -104,13 +107,14 @@ public function testGetClosedIssuesFromIntervalNoResults(): void public function testIssuesContainingVersion(): void { $versionRepository = self::getContainer()->get(VersionRepository::class); + \assert($versionRepository instanceof VersionRepository); $version = $versionRepository->findOneBy([], ['id' => 'ASC']); + $this->assertNotNull($version); $result = $this->repository->issuesContainingVersion($version); $this->assertNotEmpty($result); foreach ($result as $issue) { - $this->assertInstanceOf(Issue::class, $issue); $versionIds = $issue->getVersions()->map(fn (Version $v) => $v->getId())->toArray(); $this->assertContains($version->getId(), $versionIds); } @@ -124,7 +128,6 @@ public function testIssuesContainingVersionTitle(): void $this->assertNotEmpty($result); foreach ($result as $issue) { - $this->assertInstanceOf(Issue::class, $issue); $versionNames = $issue->getVersions()->map(fn (Version $v) => $v->getName())->toArray(); $this->assertContains('PB-0-0', $versionNames); } @@ -151,6 +154,7 @@ public function testFindIssuesInDateRange(): void public function testFindIssuesInDateRangeWithWorkerGroup(): void { $workerGroupRepo = self::getContainer()->get(WorkerGroupRepository::class); + \assert($workerGroupRepo instanceof WorkerGroupRepository); $group = $workerGroupRepo->findOneBy(['name' => 'Group Alpha']); $this->assertInstanceOf(WorkerGroup::class, $group); @@ -168,6 +172,7 @@ public function testFindIssuesInDateRangeWithWorkerGroup(): void public function testFindIssuesInDateRangeWithProjects(): void { $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $startDate = (new \DateTime('-1 day'))->format('Y-m-d'); $endDate = (new \DateTime('+2 days'))->format('Y-m-d'); @@ -175,7 +180,9 @@ public function testFindIssuesInDateRangeWithProjects(): void $this->assertNotEmpty($result); foreach ($result as $issue) { - $this->assertEquals($project->getId(), $issue->getProject()->getId()); + $issueProject = $issue->getProject(); + $this->assertNotNull($issueProject); + $this->assertEquals($project->getId(), $issueProject->getId()); } } diff --git a/tests/Integration/Repository/ProductRepositoryTest.php b/tests/Integration/Repository/ProductRepositoryTest.php index 18e6faff..455e70eb 100644 --- a/tests/Integration/Repository/ProductRepositoryTest.php +++ b/tests/Integration/Repository/ProductRepositoryTest.php @@ -5,7 +5,6 @@ use App\Model\Invoices\ProductFilterData; use App\Repository\ProductRepository; use App\Repository\ProjectRepository; -use Knp\Component\Pager\Pagination\PaginationInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class ProductRepositoryTest extends KernelTestCase @@ -15,7 +14,9 @@ class ProductRepositoryTest extends KernelTestCase protected function setUp(): void { self::bootKernel(); - $this->repository = self::getContainer()->get(ProductRepository::class); + $repository = self::getContainer()->get(ProductRepository::class); + \assert($repository instanceof ProductRepository); + $this->repository = $repository; } public function testGetFilteredPaginationNoFilter(): void @@ -23,7 +24,6 @@ public function testGetFilteredPaginationNoFilter(): void $filterData = new ProductFilterData(); $result = $this->repository->getFilteredPagination($filterData); - $this->assertInstanceOf(PaginationInterface::class, $result); $this->assertGreaterThanOrEqual(3, $result->getTotalItemCount()); } @@ -35,14 +35,16 @@ public function testGetFilteredPaginationByName(): void $this->assertGreaterThanOrEqual(1, $result->getTotalItemCount()); foreach ($result as $product) { - $this->assertStringContainsString('Alpha', $product->getName()); + $this->assertStringContainsString('Alpha', (string) $product->getName()); } } public function testGetFilteredPaginationByProject(): void { $projectRepo = self::getContainer()->get(ProjectRepository::class); + \assert($projectRepo instanceof ProjectRepository); $project = $projectRepo->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $filterData = new ProductFilterData(); $filterData->project = $project; @@ -50,7 +52,9 @@ public function testGetFilteredPaginationByProject(): void $this->assertEquals(2, $result->getTotalItemCount()); foreach ($result as $product) { - $this->assertEquals($project->getId(), $product->getProject()->getId()); + $productProject = $product->getProject(); + $this->assertNotNull($productProject); + $this->assertEquals($project->getId(), $productProject->getId()); } } } diff --git a/tests/Integration/Repository/ProjectBillingRepositoryTest.php b/tests/Integration/Repository/ProjectBillingRepositoryTest.php index d2cd72b2..ae840492 100644 --- a/tests/Integration/Repository/ProjectBillingRepositoryTest.php +++ b/tests/Integration/Repository/ProjectBillingRepositoryTest.php @@ -4,7 +4,6 @@ use App\Model\Invoices\ProjectBillingFilterData; use App\Repository\ProjectBillingRepository; -use Knp\Component\Pager\Pagination\PaginationInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class ProjectBillingRepositoryTest extends KernelTestCase @@ -14,7 +13,9 @@ class ProjectBillingRepositoryTest extends KernelTestCase protected function setUp(): void { self::bootKernel(); - $this->repository = self::getContainer()->get(ProjectBillingRepository::class); + $repository = self::getContainer()->get(ProjectBillingRepository::class); + \assert($repository instanceof ProjectBillingRepository); + $this->repository = $repository; } public function testGetFilteredPaginationRecorded(): void @@ -23,7 +24,6 @@ public function testGetFilteredPaginationRecorded(): void $filterData->recorded = true; $result = $this->repository->getFilteredPagination($filterData); - $this->assertInstanceOf(PaginationInterface::class, $result); $this->assertGreaterThanOrEqual(1, $result->getTotalItemCount()); foreach ($result as $pb) { $this->assertTrue($pb->isRecorded()); diff --git a/tests/Integration/Repository/ProjectRepositoryTest.php b/tests/Integration/Repository/ProjectRepositoryTest.php index 1334a008..9c68144b 100644 --- a/tests/Integration/Repository/ProjectRepositoryTest.php +++ b/tests/Integration/Repository/ProjectRepositoryTest.php @@ -7,30 +7,25 @@ use App\Repository\DataProviderRepository; use App\Repository\ProjectRepository; use App\Service\LeantimeUrlGenerator; -use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\QueryBuilder; -use Knp\Component\Pager\Pagination\PaginationInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class ProjectRepositoryTest extends KernelTestCase { - private EntityManagerInterface $entityManager; private ProjectRepository $repository; protected function setUp(): void { self::bootKernel(); $container = self::getContainer(); - $this->entityManager = $container->get(EntityManagerInterface::class); - $this->repository = $container->get(ProjectRepository::class); + $repository = $container->get(ProjectRepository::class); + \assert($repository instanceof ProjectRepository); + $this->repository = $repository; } public function testGetIncluded(): void { $qb = $this->repository->getIncluded(); - $this->assertInstanceOf(QueryBuilder::class, $qb); - $results = $qb->getQuery()->getResult(); $this->assertNotEmpty($results); @@ -51,7 +46,6 @@ public function testGetFilteredPaginationIncluded(): void $filterData->include = true; $result = $this->repository->getFilteredPagination($filterData); - $this->assertInstanceOf(PaginationInterface::class, $result); $this->assertGreaterThanOrEqual(20, $result->getTotalItemCount()); } @@ -77,7 +71,7 @@ public function testGetFilteredPaginationByName(): void $this->assertGreaterThan(0, $result->getTotalItemCount()); foreach ($result as $project) { - $this->assertStringContainsString('project-0-0', $project->getName()); + $this->assertStringContainsString('project-0-0', (string) $project->getName()); } } @@ -90,7 +84,7 @@ public function testGetFilteredPaginationByKey(): void $this->assertGreaterThan(0, $result->getTotalItemCount()); foreach ($result as $project) { - $this->assertStringContainsString('project-1-0', $project->getProjectTrackerKey()); + $this->assertStringContainsString('project-1-0', (string) $project->getProjectTrackerKey()); } } @@ -100,23 +94,23 @@ public function testGetProjectIdsWithCybersecurityAgreement(): void // project-0-0; no other project carries one. $result = $this->repository->getProjectIdsWithCybersecurityAgreement(); - $this->assertIsArray($result); $this->assertCount(1, $result); $project = $this->repository->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $this->assertEquals([$project->getId()], $result); } public function testGetProjectTrackerIdsByDataProviders(): void { $dpRepo = self::getContainer()->get(DataProviderRepository::class); + \assert($dpRepo instanceof DataProviderRepository); $dataProviders = $dpRepo->findAll(); $this->assertNotEmpty($dataProviders); $result = $this->repository->getProjectTrackerIdsByDataProviders($dataProviders); $this->assertNotEmpty($result); - $this->assertIsArray($result); // Verify results are sorted $sorted = $result; diff --git a/tests/Integration/Repository/ServiceAgreementRepositoryTest.php b/tests/Integration/Repository/ServiceAgreementRepositoryTest.php index a68cdf8d..93c77248 100644 --- a/tests/Integration/Repository/ServiceAgreementRepositoryTest.php +++ b/tests/Integration/Repository/ServiceAgreementRepositoryTest.php @@ -5,7 +5,6 @@ use App\Enum\HostingProviderEnum; use App\Model\Invoices\ServiceAgreementFilterData; use App\Repository\ServiceAgreementRepository; -use Knp\Component\Pager\Pagination\PaginationInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class ServiceAgreementRepositoryTest extends KernelTestCase @@ -15,7 +14,9 @@ class ServiceAgreementRepositoryTest extends KernelTestCase protected function setUp(): void { self::bootKernel(); - $this->repository = self::getContainer()->get(ServiceAgreementRepository::class); + $repository = self::getContainer()->get(ServiceAgreementRepository::class); + \assert($repository instanceof ServiceAgreementRepository); + $this->repository = $repository; } public function testGetFilteredPaginationNoFilter(): void @@ -23,7 +24,6 @@ public function testGetFilteredPaginationNoFilter(): void $filterData = new ServiceAgreementFilterData(); $result = $this->repository->getFilteredPagination($filterData); - $this->assertInstanceOf(PaginationInterface::class, $result); $this->assertGreaterThanOrEqual(3, $result->getTotalItemCount()); } diff --git a/tests/Integration/Repository/SubscriptionRepositoryTest.php b/tests/Integration/Repository/SubscriptionRepositoryTest.php index 944d84d4..f22c6083 100644 --- a/tests/Integration/Repository/SubscriptionRepositoryTest.php +++ b/tests/Integration/Repository/SubscriptionRepositoryTest.php @@ -14,7 +14,9 @@ class SubscriptionRepositoryTest extends KernelTestCase protected function setUp(): void { self::bootKernel(); - $this->repository = self::getContainer()->get(SubscriptionRepository::class); + $repository = self::getContainer()->get(SubscriptionRepository::class); + \assert($repository instanceof SubscriptionRepository); + $this->repository = $repository; } public function testFindByCustom(): void @@ -23,7 +25,6 @@ public function testFindByCustom(): void $this->assertCount(2, $result); foreach ($result as $subscription) { - $this->assertInstanceOf(Subscription::class, $subscription); $this->assertEquals('subscriber@test.com', $subscription->getEmail()); } } @@ -39,7 +40,7 @@ public function testFindOneByCustom(): void { $result = $this->repository->findOneByCustom( 'subscriber@test.com', - SubscriptionFrequencyEnum::FREQUENCY_MONTHLY, + SubscriptionFrequencyEnum::FREQUENCY_MONTHLY->value, ['param1' => 'value1'] ); @@ -52,7 +53,7 @@ public function testFindOneByCustomNoMatch(): void { $result = $this->repository->findOneByCustom( 'nonexistent@test.com', - SubscriptionFrequencyEnum::FREQUENCY_MONTHLY, + SubscriptionFrequencyEnum::FREQUENCY_MONTHLY->value, ['param1' => 'value1'] ); diff --git a/tests/Integration/Repository/WorkerGroupRepositoryTest.php b/tests/Integration/Repository/WorkerGroupRepositoryTest.php index 6623743a..6c677513 100644 --- a/tests/Integration/Repository/WorkerGroupRepositoryTest.php +++ b/tests/Integration/Repository/WorkerGroupRepositoryTest.php @@ -4,7 +4,6 @@ use App\Model\Invoices\NameFilterData; use App\Repository\WorkerGroupRepository; -use Knp\Component\Pager\Pagination\PaginationInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class WorkerGroupRepositoryTest extends KernelTestCase @@ -14,7 +13,9 @@ class WorkerGroupRepositoryTest extends KernelTestCase protected function setUp(): void { self::bootKernel(); - $this->repository = self::getContainer()->get(WorkerGroupRepository::class); + $repository = self::getContainer()->get(WorkerGroupRepository::class); + \assert($repository instanceof WorkerGroupRepository); + $this->repository = $repository; } public function testGetFilteredPaginationNoFilter(): void @@ -22,7 +23,6 @@ public function testGetFilteredPaginationNoFilter(): void $filterData = new NameFilterData(); $result = $this->repository->getFilteredPagination($filterData); - $this->assertInstanceOf(PaginationInterface::class, $result); $this->assertGreaterThanOrEqual(2, $result->getTotalItemCount()); } diff --git a/tests/Integration/Repository/WorkerRepositoryTest.php b/tests/Integration/Repository/WorkerRepositoryTest.php index b2a9a77b..674fcd84 100644 --- a/tests/Integration/Repository/WorkerRepositoryTest.php +++ b/tests/Integration/Repository/WorkerRepositoryTest.php @@ -16,8 +16,12 @@ protected function setUp(): void { self::bootKernel(); $container = self::getContainer(); - $this->entityManager = $container->get(EntityManagerInterface::class); - $this->repository = $container->get(WorkerRepository::class); + $entityManager = $container->get(EntityManagerInterface::class); + \assert($entityManager instanceof EntityManagerInterface); + $this->entityManager = $entityManager; + $repository = $container->get(WorkerRepository::class); + \assert($repository instanceof WorkerRepository); + $this->repository = $repository; } public function testFindAllIncludedInReports(): void @@ -26,10 +30,6 @@ public function testFindAllIncludedInReports(): void $this->assertNotEmpty($results); $this->assertGreaterThanOrEqual(10, \count($results)); - - foreach ($results as $worker) { - $this->assertInstanceOf(Worker::class, $worker); - } } public function testFindAllIncludedInReportsExcludesDisabled(): void diff --git a/tests/Integration/Repository/WorklogRepositoryTest.php b/tests/Integration/Repository/WorklogRepositoryTest.php index 8a32efaf..1c6631ce 100644 --- a/tests/Integration/Repository/WorklogRepositoryTest.php +++ b/tests/Integration/Repository/WorklogRepositoryTest.php @@ -2,18 +2,15 @@ namespace App\Tests\Integration\Repository; -use App\Entity\Worklog; use App\Model\Invoices\InvoiceEntryWorklogsFilterData; use App\Repository\InvoiceEntryRepository; use App\Repository\IssueRepository; use App\Repository\ProjectRepository; use App\Repository\WorklogRepository; -use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class WorklogRepositoryTest extends KernelTestCase { - private EntityManagerInterface $entityManager; private WorklogRepository $repository; private ProjectRepository $projectRepository; @@ -21,16 +18,22 @@ protected function setUp(): void { self::bootKernel(); $container = self::getContainer(); - $this->entityManager = $container->get(EntityManagerInterface::class); - $this->repository = $container->get(WorklogRepository::class); - $this->projectRepository = $container->get(ProjectRepository::class); + $repository = $container->get(WorklogRepository::class); + \assert($repository instanceof WorklogRepository); + $this->repository = $repository; + $projectRepository = $container->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); + $this->projectRepository = $projectRepository; } public function testFindByFilterDataBasic(): void { $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $invoiceEntryRepo = self::getContainer()->get(InvoiceEntryRepository::class); + \assert($invoiceEntryRepo instanceof InvoiceEntryRepository); $invoiceEntry = $invoiceEntryRepo->findOneBy([], ['id' => 'ASC']); + $this->assertNotNull($invoiceEntry); $filterData = new InvoiceEntryWorklogsFilterData(); $filterData->onlyAvailable = false; @@ -38,16 +41,16 @@ public function testFindByFilterDataBasic(): void $result = $this->repository->findByFilterData($project, $invoiceEntry, $filterData); $this->assertNotEmpty($result); - foreach ($result as $worklog) { - $this->assertInstanceOf(Worklog::class, $worklog); - } } public function testFindByFilterDataByWorker(): void { $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $invoiceEntryRepo = self::getContainer()->get(InvoiceEntryRepository::class); + \assert($invoiceEntryRepo instanceof InvoiceEntryRepository); $invoiceEntry = $invoiceEntryRepo->findOneBy([], ['id' => 'ASC']); + $this->assertNotNull($invoiceEntry); $filterData = new InvoiceEntryWorklogsFilterData(); $filterData->onlyAvailable = false; @@ -57,15 +60,18 @@ public function testFindByFilterDataByWorker(): void $this->assertNotEmpty($result); foreach ($result as $worklog) { - $this->assertStringContainsString('admin@test.local', $worklog->getWorker()); + $this->assertStringContainsString('admin@test.local', (string) $worklog->getWorker()); } } public function testFindByFilterDataByDateRange(): void { $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $invoiceEntryRepo = self::getContainer()->get(InvoiceEntryRepository::class); + \assert($invoiceEntryRepo instanceof InvoiceEntryRepository); $invoiceEntry = $invoiceEntryRepo->findOneBy([], ['id' => 'ASC']); + $this->assertNotNull($invoiceEntry); $year = (new \DateTime())->format('Y'); $filterData = new InvoiceEntryWorklogsFilterData(); @@ -87,8 +93,11 @@ public function testFindByFilterDataByDateRange(): void public function testFindByFilterDataByBilled(): void { $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $invoiceEntryRepo = self::getContainer()->get(InvoiceEntryRepository::class); + \assert($invoiceEntryRepo instanceof InvoiceEntryRepository); $invoiceEntry = $invoiceEntryRepo->findOneBy([], ['id' => 'ASC']); + $this->assertNotNull($invoiceEntry); $filterData = new InvoiceEntryWorklogsFilterData(); $filterData->onlyAvailable = false; @@ -105,8 +114,11 @@ public function testFindByFilterDataByBilled(): void public function testFindByFilterDataOnlyAvailable(): void { $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); + $this->assertNotNull($project); $invoiceEntryRepo = self::getContainer()->get(InvoiceEntryRepository::class); + \assert($invoiceEntryRepo instanceof InvoiceEntryRepository); $invoiceEntry = $invoiceEntryRepo->findOneBy([], ['id' => 'ASC']); + $this->assertNotNull($invoiceEntry); $filterData = new InvoiceEntryWorklogsFilterData(); $filterData->onlyAvailable = true; @@ -178,9 +190,6 @@ public function testFindBillableWorklogsByWorkerAndDateRange(): void ); $this->assertNotEmpty($result); - foreach ($result as $worklog) { - $this->assertInstanceOf(Worklog::class, $worklog); - } } public function testFindBillableWorklogsByWorkerAndDateRangeFilteredByWorker(): void @@ -224,12 +233,6 @@ public function testGetWorklogsAttachedToInvoiceInDateRange(): void new \DateTime("$year-12-31") ); - $this->assertIsArray($result); - $this->assertArrayHasKey('total_count', $result); - $this->assertArrayHasKey('pages_count', $result); - $this->assertArrayHasKey('current_page', $result); - $this->assertArrayHasKey('page_size', $result); - $this->assertArrayHasKey('paginator', $result); $this->assertEquals(1, $result['current_page']); $this->assertEquals(50, $result['page_size']); $this->assertGreaterThan(0, $result['total_count']); @@ -238,24 +241,33 @@ public function testGetWorklogsAttachedToInvoiceInDateRange(): void public function testGetWorklogsByIssueAndPeriodReturnsAllWhenNoDates(): void { $issueRepository = self::getContainer()->get(IssueRepository::class); + \assert($issueRepository instanceof IssueRepository); $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); $issue = $issueRepository->findOneBy(['project' => $project], ['id' => 'ASC']); + $this->assertNotNull($issue); + $issueId = $issue->getId(); + $this->assertNotNull($issueId); - $result = $this->repository->getWorklogsByIssueAndPeriod($issue->getId(), null, null); + $result = $this->repository->getWorklogsByIssueAndPeriod($issueId, null, null); // Fixtures attach 100 worklogs per issue. $this->assertCount(100, $result); foreach ($result as $worklog) { - $this->assertInstanceOf(Worklog::class, $worklog); - $this->assertSame($issue->getId(), $worklog->getIssue()->getId()); + $worklogIssue = $worklog->getIssue(); + $this->assertNotNull($worklogIssue); + $this->assertSame($issue->getId(), $worklogIssue->getId()); } } public function testGetWorklogsByIssueAndPeriodFiltersByPeriod(): void { $issueRepository = self::getContainer()->get(IssueRepository::class); + \assert($issueRepository instanceof IssueRepository); $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); $issue = $issueRepository->findOneBy(['project' => $project], ['id' => 'ASC']); + $this->assertNotNull($issue); + $issueId = $issue->getId(); + $this->assertNotNull($issueId); $year = (new \DateTime())->format('Y'); @@ -263,14 +275,16 @@ public function testGetWorklogsByIssueAndPeriodFiltersByPeriod(): void // limiting to January should match worklogs where (k % 12) == 0, i.e. // k ∈ {0,12,24,36,48,60,72,84,96} — 9 worklogs. $result = $this->repository->getWorklogsByIssueAndPeriod( - $issue->getId(), + $issueId, new \DateTime("$year-01-01"), new \DateTime("$year-01-31"), ); $this->assertCount(9, $result); foreach ($result as $worklog) { - $this->assertSame('01', $worklog->getStarted()->format('m')); + $started = $worklog->getStarted(); + $this->assertNotNull($started); + $this->assertSame('01', $started->format('m')); } } @@ -284,21 +298,27 @@ public function testGetWorklogsByIssueAndPeriodEmptyForUnknownIssue(): void public function testGetWorklogsByIssueAndPeriodReturnsOrderedByStarted(): void { $issueRepository = self::getContainer()->get(IssueRepository::class); + \assert($issueRepository instanceof IssueRepository); $project = $this->projectRepository->findOneBy(['name' => 'project-0-0']); $issue = $issueRepository->findOneBy(['project' => $project], ['id' => 'ASC']); + $this->assertNotNull($issue); + $issueId = $issue->getId(); + $this->assertNotNull($issueId); - $result = $this->repository->getWorklogsByIssueAndPeriod($issue->getId(), null, null); + $result = $this->repository->getWorklogsByIssueAndPeriod($issueId, null, null); $previous = null; foreach ($result as $worklog) { + $started = $worklog->getStarted(); + $this->assertNotNull($started); if (null !== $previous) { $this->assertGreaterThanOrEqual( $previous->getTimestamp(), - $worklog->getStarted()->getTimestamp(), + $started->getTimestamp(), 'Worklogs should be returned ordered by started ASC.' ); } - $previous = $worklog->getStarted(); + $previous = $started; } } } diff --git a/tests/Integration/Service/BillableUnbilledHoursReportServiceTest.php b/tests/Integration/Service/BillableUnbilledHoursReportServiceTest.php index 36b4f5d7..a46b9a7f 100644 --- a/tests/Integration/Service/BillableUnbilledHoursReportServiceTest.php +++ b/tests/Integration/Service/BillableUnbilledHoursReportServiceTest.php @@ -2,7 +2,6 @@ namespace App\Tests\Integration\Service; -use App\Model\Reports\BillableUnbilledHoursReportData; use App\Service\BillableUnbilledHoursReportService; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -13,14 +12,13 @@ public function testGetBillableUnbilledHoursReportAggregatesBillableUnbilledWork self::bootKernel(); $container = self::getContainer(); - /** @var BillableUnbilledHoursReportService $service */ $service = $container->get(BillableUnbilledHoursReportService::class); + \assert($service instanceof BillableUnbilledHoursReportService); $year = (int) (new \DateTime())->format('Y'); $report = $service->getBillableUnbilledHoursReport($year); - $this->assertInstanceOf(BillableUnbilledHoursReportData::class, $report); $this->assertGreaterThan(0, $report->totalHoursForAllProjects, 'Fixtures contain billable unbilled worklogs for the current year.'); $this->assertCount(1, $report->projectData, 'Report wraps projectData as a single-element collection of project arrays.'); $this->assertNotEmpty($report->projectTotals); @@ -43,8 +41,8 @@ public function testGetBillableUnbilledHoursReportRestrictsToQuarter(): void self::bootKernel(); $container = self::getContainer(); - /** @var BillableUnbilledHoursReportService $service */ $service = $container->get(BillableUnbilledHoursReportService::class); + \assert($service instanceof BillableUnbilledHoursReportService); $year = (int) (new \DateTime())->format('Y'); diff --git a/tests/Integration/Service/CybersecurityReportServiceTest.php b/tests/Integration/Service/CybersecurityReportServiceTest.php index c3df8f66..41e440ae 100644 --- a/tests/Integration/Service/CybersecurityReportServiceTest.php +++ b/tests/Integration/Service/CybersecurityReportServiceTest.php @@ -3,7 +3,6 @@ namespace App\Tests\Integration\Service; use App\Model\Reports\CybersecurityProjectData; -use App\Model\Reports\CybersecurityReportData; use App\Service\CybersecurityReportService; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -14,7 +13,9 @@ class CybersecurityReportServiceTest extends KernelTestCase protected function setUp(): void { self::bootKernel(); - $this->service = self::getContainer()->get(CybersecurityReportService::class); + $service = self::getContainer()->get(CybersecurityReportService::class); + \assert($service instanceof CybersecurityReportService); + $this->service = $service; } public function testGetDefaultFromDateIsFirstOfCurrentMonth(): void @@ -33,7 +34,6 @@ public function testGetCybersecurityReportUnknownVersionReturnsEmpty(): void { $report = $this->service->getCybersecurityReport(null, null, 'no-such-version'); - $this->assertInstanceOf(CybersecurityReportData::class, $report); $this->assertSame([], $report->projects); $this->assertSame(0.0, $report->totalSpent); } diff --git a/tests/Integration/Service/ForecastReportServiceTest.php b/tests/Integration/Service/ForecastReportServiceTest.php index 3a5af1a9..ec0454ad 100644 --- a/tests/Integration/Service/ForecastReportServiceTest.php +++ b/tests/Integration/Service/ForecastReportServiceTest.php @@ -2,7 +2,6 @@ namespace App\Tests\Integration\Service; -use App\Model\Reports\ForecastReportData; use App\Service\ForecastReportService; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -13,8 +12,8 @@ public function testGetForecastReportRunsAgainstFixturesAndReturnsConsistentTota self::bootKernel(); $container = self::getContainer(); - /** @var ForecastReportService $service */ $service = $container->get(ForecastReportService::class); + \assert($service instanceof ForecastReportService); // Cover the entire fixture year plus a year of headroom on either side. $year = (int) (new \DateTime())->format('Y'); @@ -23,7 +22,6 @@ public function testGetForecastReportRunsAgainstFixturesAndReturnsConsistentTota $report = $service->getForecastReport($fromDate, $toDate); - $this->assertInstanceOf(ForecastReportData::class, $report); $this->assertGreaterThanOrEqual(0.0, $report->totalInvoiced); $this->assertGreaterThanOrEqual(0.0, $report->totalInvoicedAndRecorded); diff --git a/tests/Integration/Service/HourReportServiceTest.php b/tests/Integration/Service/HourReportServiceTest.php index 0d0111be..3cc0de55 100644 --- a/tests/Integration/Service/HourReportServiceTest.php +++ b/tests/Integration/Service/HourReportServiceTest.php @@ -3,8 +3,6 @@ namespace App\Tests\Integration\Service; use App\Entity\Project; -use App\Model\Reports\HourReportData; -use App\Model\Reports\HourReportProjectTag; use App\Repository\ProjectRepository; use App\Service\HourReportService; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -16,10 +14,10 @@ public function testGetHourReportAggregatesWorklogsAcrossIssuesAndEpicTag(): voi self::bootKernel(); $container = self::getContainer(); - /** @var ProjectRepository $projectRepository */ $projectRepository = $container->get(ProjectRepository::class); - /** @var HourReportService $service */ + \assert($projectRepository instanceof ProjectRepository); $service = $container->get(HourReportService::class); + \assert($service instanceof HourReportService); // project-0-0 is the only project whose first issue is tagged with "Epic 1" in fixtures. $project = $projectRepository->findOneBy(['name' => 'project-0-0']); @@ -27,8 +25,6 @@ public function testGetHourReportAggregatesWorklogsAcrossIssuesAndEpicTag(): voi $report = $service->getHourReport($project, null, null); - $this->assertInstanceOf(HourReportData::class, $report); - // Each project has 10 issues × 100 worklogs; worklog k contributes (k+1)*15 minutes. // Total = 10 issues × 900s × Σ(1..100) = 10 × 900 × 5050 = 45,450,000s = 12_625h. $this->assertEqualsWithDelta(12625.0, $report->projectTotalSpent, 0.001); @@ -48,12 +44,10 @@ public function testGetHourReportAggregatesWorklogsAcrossIssuesAndEpicTag(): voi $this->assertArrayHasKey('noTag', $tagsByLabel); $epicTag = $tagsByLabel['Epic 1']; - $this->assertInstanceOf(HourReportProjectTag::class, $epicTag); $this->assertCount(1, $epicTag->projectTickets); $this->assertEqualsWithDelta(1262.5, $epicTag->totalSpent, 0.001); $noTag = $tagsByLabel['noTag']; - $this->assertInstanceOf(HourReportProjectTag::class, $noTag); $this->assertCount(9, $noTag->projectTickets); } @@ -62,10 +56,10 @@ public function testGetHourReportDateRangeExcludesOutOfRangeWorklogs(): void self::bootKernel(); $container = self::getContainer(); - /** @var ProjectRepository $projectRepository */ $projectRepository = $container->get(ProjectRepository::class); - /** @var HourReportService $service */ + \assert($projectRepository instanceof ProjectRepository); $service = $container->get(HourReportService::class); + \assert($service instanceof HourReportService); $project = $projectRepository->findOneBy(['name' => 'project-0-0']); $this->assertInstanceOf(Project::class, $project); diff --git a/tests/Integration/Service/InvoicingRateReportServiceTest.php b/tests/Integration/Service/InvoicingRateReportServiceTest.php index 5352cd38..5d3c2a80 100644 --- a/tests/Integration/Service/InvoicingRateReportServiceTest.php +++ b/tests/Integration/Service/InvoicingRateReportServiceTest.php @@ -2,7 +2,6 @@ namespace App\Tests\Integration\Service; -use App\Model\Reports\InvoicingRateReportData; use App\Model\Reports\InvoicingRateReportViewModeEnum; use App\Model\Reports\InvoicingRateReportWorker; use App\Model\Reports\WorkloadReportPeriodTypeEnum as PeriodTypeEnum; @@ -16,8 +15,8 @@ public function testGetInvoicingRateReportProducesPeriodsForEveryIncludedWorker( self::bootKernel(); $container = self::getContainer(); - /** @var InvoicingRateReportService $service */ $service = $container->get(InvoicingRateReportService::class); + \assert($service instanceof InvoicingRateReportService); $year = (int) (new \DateTime())->format('Y'); @@ -27,8 +26,6 @@ public function testGetInvoicingRateReportProducesPeriodsForEveryIncludedWorker( InvoicingRateReportViewModeEnum::SUMMARY, ); - $this->assertInstanceOf(InvoicingRateReportData::class, $report); - // Fixtures create 10 workers all included in reports. $this->assertCount(10, $report->workers); @@ -45,9 +42,8 @@ public function testGetInvoicingRateReportProducesPeriodsForEveryIncludedWorker( /** @var InvoicingRateReportWorker $worker */ $worker = $report->workers->first(); - $this->assertInstanceOf(InvoicingRateReportWorker::class, $worker); $this->assertGreaterThanOrEqual(0.0, $worker->average); $this->assertLessThanOrEqual(100.0, $worker->average); - $this->assertSame($report->period->count(), $worker->dataByPeriod->count()); + $this->assertCount($report->period->count(), $worker->dataByPeriod); } } diff --git a/tests/Integration/Service/LeantimeApiServiceTest.php b/tests/Integration/Service/LeantimeApiServiceTest.php index 6215c3ee..64d64448 100644 --- a/tests/Integration/Service/LeantimeApiServiceTest.php +++ b/tests/Integration/Service/LeantimeApiServiceTest.php @@ -33,12 +33,19 @@ public function testUpdate(): void $container = self::getContainer(); $messageBus = $container->get(MessageBusInterface::class); + \assert($messageBus instanceof MessageBusInterface); $dataProviderRepository = $container->get(DataProviderRepository::class); + \assert($dataProviderRepository instanceof DataProviderRepository); $projectRepository = $container->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); $versionRepository = $container->get(VersionRepository::class); + \assert($versionRepository instanceof VersionRepository); $issueRepository = $container->get(IssueRepository::class); + \assert($issueRepository instanceof IssueRepository); $worklogRepository = $container->get(WorklogRepository::class); + \assert($worklogRepository instanceof WorklogRepository); $entityManager = $container->get(EntityManagerInterface::class); + \assert($entityManager instanceof EntityManagerInterface); $loggerMock = $this->createMock(LoggerInterface::class); @@ -97,65 +104,83 @@ public function testUpdate(): void $dataProvider->setSecret('Not so secret'); $entityManager->persist($dataProvider); $entityManager->flush(); + $dataProviderId = $dataProvider->getId(); + \assert(null !== $dataProviderId); // Projects $before = count($projectRepository->findAll()); - $service->updateAsJob(Project::class, 0, 100, $dataProvider->getId()); + $service->updateAsJob(Project::class, 0, 100, $dataProviderId); $after = count($projectRepository->findAll()); $this->assertEquals($before + 2, $after); $project = $projectRepository->findOneBy(['projectTrackerId' => 50]); + $this->assertNotNull($project); + \assert(null !== $project->getSourceModifiedDate()); $this->assertEquals((new \DateTime('2024-10-03T13:47:30.000000Z'))->getTimestamp(), $project->getSourceModifiedDate()->getTimestamp()); // Repeat process to test that no extra entries are added and test modifiedAfter - $service->updateAsJob(Project::class, 0, 100, $dataProvider->getId(), [], false, new \DateTime('2025-01-01')); + $service->updateAsJob(Project::class, 0, 100, $dataProviderId, [], false, new \DateTime('2025-01-01')); $after = count($projectRepository->findAll()); $this->assertEquals($before + 2, $after); $project = $projectRepository->findOneBy(['projectTrackerId' => 50]); + $this->assertNotNull($project); + \assert(null !== $project->getSourceModifiedDate()); $this->assertEquals((new \DateTime('2025-10-03T13:47:30.000000Z'))->getTimestamp(), $project->getSourceModifiedDate()->getTimestamp()); // Milestones $before = count($versionRepository->findAll()); - $service->updateAsJob(Version::class, 0, 100, $dataProvider->getId()); + $service->updateAsJob(Version::class, 0, 100, $dataProviderId); $after = count($versionRepository->findAll()); $this->assertEquals($before + 2, $after); $version = $versionRepository->findOneBy(['projectTrackerId' => 10, 'dataProvider' => $dataProvider]); + $this->assertNotNull($version); + \assert(null !== $version->getSourceModifiedDate()); $this->assertEquals((new \DateTime('2024-10-03T13:47:30.000000Z'))->getTimestamp(), $version->getSourceModifiedDate()->getTimestamp()); // Repeat process to test that no extra entries are added and test modifiedAfter - $service->updateAsJob(Version::class, 0, 100, $dataProvider->getId(), [], false, new \DateTime('2025-01-01')); + $service->updateAsJob(Version::class, 0, 100, $dataProviderId, [], false, new \DateTime('2025-01-01')); $after = count($versionRepository->findAll()); $this->assertEquals($before + 2, $after); $version = $versionRepository->findOneBy(['projectTrackerId' => 10, 'dataProvider' => $dataProvider]); + $this->assertNotNull($version); + \assert(null !== $version->getSourceModifiedDate()); $this->assertEquals((new \DateTime('2025-10-03T13:47:30.000000Z'))->getTimestamp(), $version->getSourceModifiedDate()->getTimestamp()); // Tickets $before = count($issueRepository->findAll()); - $service->updateAsJob(Issue::class, 0, 100, $dataProvider->getId()); + $service->updateAsJob(Issue::class, 0, 100, $dataProviderId); $after = count($issueRepository->findAll()); $this->assertEquals($before + 2, $after); $issue = $issueRepository->findOneBy(['projectTrackerId' => 10, 'dataProvider' => $dataProvider]); + $this->assertNotNull($issue); + \assert(null !== $issue->getSourceModifiedDate()); $this->assertEquals((new \DateTime('2024-10-03T13:47:30.000000Z'))->getTimestamp(), $issue->getSourceModifiedDate()->getTimestamp()); // Repeat process to test that no extra entries are added and test modifiedAfter - $service->updateAsJob(Issue::class, 0, 100, $dataProvider->getId(), [], false, new \DateTime('2025-01-01')); + $service->updateAsJob(Issue::class, 0, 100, $dataProviderId, [], false, new \DateTime('2025-01-01')); $after = count($issueRepository->findAll()); $this->assertEquals($before + 2, $after); $issue = $issueRepository->findOneBy(['projectTrackerId' => 10, 'dataProvider' => $dataProvider]); + $this->assertNotNull($issue); + \assert(null !== $issue->getSourceModifiedDate()); $this->assertEquals((new \DateTime('2025-10-03T13:47:30.000000Z'))->getTimestamp(), $issue->getSourceModifiedDate()->getTimestamp()); // Timesheets $before = count($worklogRepository->findAll()); - $service->updateAsJob(Worklog::class, 0, 100, $dataProvider->getId()); + $service->updateAsJob(Worklog::class, 0, 100, $dataProviderId); $after = count($worklogRepository->findAll()); $this->assertEquals($before + 2, $after); $worklog = $worklogRepository->findOneBy(['worklogId' => 1, 'dataProvider' => $dataProvider]); + $this->assertNotNull($worklog); + \assert(null !== $worklog->getSourceModifiedDate()); $this->assertEquals((new \DateTime('2024-10-03T13:47:30.000000Z'))->getTimestamp(), $worklog->getSourceModifiedDate()->getTimestamp()); // Repeat process to test that no extra entries are added and test modifiedAfter - $service->updateAsJob(Worklog::class, 0, 100, $dataProvider->getId(), [], false, new \DateTime('2025-01-01')); + $service->updateAsJob(Worklog::class, 0, 100, $dataProviderId, [], false, new \DateTime('2025-01-01')); $after = count($worklogRepository->findAll()); $this->assertEquals($before + 2, $after); $worklog = $worklogRepository->findOneBy(['worklogId' => 1, 'dataProvider' => $dataProvider]); + $this->assertNotNull($worklog); + \assert(null !== $worklog->getSourceModifiedDate()); $this->assertEquals((new \DateTime('2025-10-03T13:47:30.000000Z'))->getTimestamp(), $worklog->getSourceModifiedDate()->getTimestamp()); } @@ -165,12 +190,19 @@ public function testDeleted(): void $container = self::getContainer(); $messageBus = $container->get(MessageBusInterface::class); + \assert($messageBus instanceof MessageBusInterface); $dataProviderRepository = $container->get(DataProviderRepository::class); + \assert($dataProviderRepository instanceof DataProviderRepository); $projectRepository = $container->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); $versionRepository = $container->get(VersionRepository::class); + \assert($versionRepository instanceof VersionRepository); $issueRepository = $container->get(IssueRepository::class); + \assert($issueRepository instanceof IssueRepository); $worklogRepository = $container->get(WorklogRepository::class); + \assert($worklogRepository instanceof WorklogRepository); $entityManager = $container->get(EntityManagerInterface::class); + \assert($entityManager instanceof EntityManagerInterface); $loggerMock = $this->createMock(LoggerInterface::class); @@ -206,9 +238,9 @@ public function testDeleted(): void $project1 = new Project(); $project1->setDataProvider($dataProvider); - $project1->setProjectTrackerId(64); + $project1->setProjectTrackerId('64'); $project1->setName('Project to delete - protected'); - $project1->setProjectTrackerKey(64); + $project1->setProjectTrackerKey('64'); $project1->setProjectTrackerProjectUrl('http://localhost/'); $project1->setInclude(true); $project1->setProjectLeadMail('test@economics.local.itkdev.dk'); @@ -218,9 +250,9 @@ public function testDeleted(): void $project2 = new Project(); $project2->setDataProvider($dataProvider); - $project2->setProjectTrackerId(65); + $project2->setProjectTrackerId('65'); $project2->setName('Project to delete'); - $project2->setProjectTrackerKey(65); + $project2->setProjectTrackerKey('65'); $project2->setProjectTrackerProjectUrl('http://localhost/'); $project2->setInclude(true); $project2->setProjectLeadMail('test@economics.local.itkdev.dk'); @@ -232,21 +264,21 @@ public function testDeleted(): void $version1->setDataProvider($dataProvider); $version1->setName('Version 1'); $version1->setProject($project1); - $version1->setProjectTrackerId(6724); + $version1->setProjectTrackerId('6724'); $entityManager->persist($version1); $version2 = new Version(); $version2->setDataProvider($dataProvider); $version2->setName('Version 2'); $version2->setProject($project2); - $version2->setProjectTrackerId(6725); + $version2->setProjectTrackerId('6725'); $entityManager->persist($version2); $issue1 = new Issue(); $issue1->setDataProvider($dataProvider); $issue1->setProject($project1); - $issue1->setProjectTrackerId(6723); - $issue1->setProjectTrackerKey(6723); + $issue1->setProjectTrackerId('6723'); + $issue1->setProjectTrackerKey('6723'); $issue1->setName('issue 1 - protected'); $issue1->setAccountId('Account 1'); $issue1->setAccountKey('Account 1'); @@ -264,8 +296,8 @@ public function testDeleted(): void $issue2 = new Issue(); $issue2->setDataProvider($dataProvider); $issue2->setProject($project1); - $issue2->setProjectTrackerId(6726); - $issue2->setProjectTrackerKey(6726); + $issue2->setProjectTrackerId('6726'); + $issue2->setProjectTrackerKey('6726'); $issue2->setName('issue 2'); $issue2->setAccountId('Account 1'); $issue2->setAccountKey('Account 1'); @@ -283,13 +315,15 @@ public function testDeleted(): void $worklog1 = new Worklog(); $worklog1->setProject($project1); $worklog1->setDataProvider($dataProvider); - $worklog1->setProjectTrackerIssueId(6723); + $worklog1->setProjectTrackerIssueId('6723'); $worklog1->setWorklogId(66937); $worklog1->setDescription('Beskrivelse af worklog - protected'); $worklog1->setIsBilled(false); $worklog1->setWorker('admin@example.com'); $worklog1->setTimeSpentSeconds(60 * 15); - $worklog1->setStarted(\DateTime::createFromFormat('U', (string) strtotime('2024-01-01'), new \DateTimeZone('Europe/Copenhagen'))); + $worklog1Started = \DateTime::createFromFormat('U', (string) strtotime('2024-01-01'), new \DateTimeZone('Europe/Copenhagen')); + \assert($worklog1Started instanceof \DateTime); + $worklog1->setStarted($worklog1Started); $worklog1->setIssue($issue1); $worklog1->setDataProvider($dataProvider); $worklog1->setKind(BillableKindsEnum::GENERAL_BILLABLE); @@ -298,13 +332,15 @@ public function testDeleted(): void $worklog2 = new Worklog(); $worklog2->setProject($project1); $worklog2->setDataProvider($dataProvider); - $worklog2->setProjectTrackerIssueId(6726); + $worklog2->setProjectTrackerIssueId('6726'); $worklog2->setWorklogId(66938); $worklog2->setDescription('Beskrivelse af worklog'); $worklog2->setIsBilled(false); $worklog2->setWorker('admin@example.com'); $worklog2->setTimeSpentSeconds(60 * 15); - $worklog2->setStarted(\DateTime::createFromFormat('U', (string) strtotime('2024-01-01'), new \DateTimeZone('Europe/Copenhagen'))); + $worklog2Started = \DateTime::createFromFormat('U', (string) strtotime('2024-01-01'), new \DateTimeZone('Europe/Copenhagen')); + \assert($worklog2Started instanceof \DateTime); + $worklog2->setStarted($worklog2Started); $worklog2->setIssue($issue2); $worklog2->setDataProvider($dataProvider); $worklog2->setKind(BillableKindsEnum::GENERAL_BILLABLE); @@ -344,6 +380,7 @@ public function testDeleted(): void $entityManager->flush(); $id = $dataProvider->getId(); + \assert(null !== $id); $entityManager->clear(); @@ -361,12 +398,15 @@ public function testDeleted(): void $this->assertEquals($countProjectsBeforeCreate + 1, $countProjectsAfterDelete); $project1 = $projectRepository->find($projectId1); + $this->assertNotNull($project1); $this->assertEquals(new \DateTime('2025-10-24T11:36:08.000000Z'), $project1->getSourceDeletedDate()); $issue1 = $issueRepository->find($issueId1); + $this->assertNotNull($issue1); $this->assertEquals(new \DateTime('2025-10-24T11:36:08.000000Z'), $issue1->getSourceDeletedDate()); $worklog1 = $worklogRepository->find($worklogId1); + $this->assertNotNull($worklog1); $this->assertEquals(new \DateTime('2025-10-24T11:36:08.000000Z'), $worklog1->getSourceDeletedDate()); } @@ -429,7 +469,7 @@ private function getDeletedData(): object ', null, 512, JSON_THROW_ON_ERROR); } - private function getProjects($modifiedYear = 2024): object + private function getProjects(int $modifiedYear = 2024): object { return json_decode(' { @@ -454,7 +494,7 @@ private function getProjects($modifiedYear = 2024): object '); } - private function getMilestones($modifiedYear = 2024): object + private function getMilestones(int $modifiedYear = 2024): object { return json_decode(' { @@ -481,7 +521,7 @@ private function getMilestones($modifiedYear = 2024): object '); } - private function getTickets($modifiedYear = 2024): object + private function getTickets(int $modifiedYear = 2024): object { return json_decode(' { @@ -524,7 +564,7 @@ private function getTickets($modifiedYear = 2024): object '); } - private function getTimesheets($modifiedYear = 2024): object + private function getTimesheets(int $modifiedYear = 2024): object { return json_decode(' { diff --git a/tests/Integration/Service/ProjectBillingServiceTest.php b/tests/Integration/Service/ProjectBillingServiceTest.php index 5f8c374f..2c6b63d8 100644 --- a/tests/Integration/Service/ProjectBillingServiceTest.php +++ b/tests/Integration/Service/ProjectBillingServiceTest.php @@ -18,35 +18,44 @@ public function testGetIssuesNotIncludedInProjectBilling(): void $container = self::getContainer(); - /** @var EntityManagerInterface $entityManager */ $entityManager = $container->get(EntityManagerInterface::class); + \assert($entityManager instanceof EntityManagerInterface); - /** @var ProjectRepository $projectRepository */ $projectRepository = $container->get(ProjectRepository::class); + \assert($projectRepository instanceof ProjectRepository); - /** @var ProjectBillingService $projectBillingService */ $projectBillingService = $container->get(ProjectBillingService::class); + \assert($projectBillingService instanceof ProjectBillingService); - /** @var BillingService $projectBillingService */ $billingService = $container->get(BillingService::class); + \assert($billingService instanceof BillingService); + + $issueRepository = $container->get(IssueRepository::class); + \assert($issueRepository instanceof IssueRepository); $project = $projectRepository->findOneBy([], ['id' => 'asc']); + $this->assertNotNull($project); + + $periodStart = (new \DateTime())->sub(new \DateInterval('P1D')); + $periodEnd = (new \DateTime())->add(new \DateInterval('P1D')); $projectBilling = new ProjectBilling(); - $projectBilling->setPeriodStart((new \DateTime())->sub(new \DateInterval('P1D'))); - $projectBilling->setPeriodEnd((new \DateTime())->add(new \DateInterval('P1D'))); + $projectBilling->setPeriodStart($periodStart); + $projectBilling->setPeriodEnd($periodEnd); $projectBilling->setName('Project Billing 1'); $projectBilling->setProject($project); $projectBilling->setRecorded(false); $projectBilling->setDescription('Project billing'); - $issues = $container->get(IssueRepository::class)->getClosedIssuesFromInterval($projectBilling->getProject(), $projectBilling->getPeriodStart(), $projectBilling->getPeriodEnd()); + $issues = $issueRepository->getClosedIssuesFromInterval($project, $periodStart, $periodEnd); $this->assertCount(10, $issues); $entityManager->persist($projectBilling); $entityManager->flush(); - $projectBillingService->createProjectBilling($projectBilling->getId()); + $projectBillingId = $projectBilling->getId(); + \assert(null !== $projectBillingId); + $projectBillingService->createProjectBilling($projectBillingId); $this->assertCount(2, $projectBilling->getInvoices()); @@ -54,12 +63,13 @@ public function testGetIssuesNotIncludedInProjectBilling(): void $this->assertCount(4, $issues); - $ids = $projectBilling->getInvoices()->map(fn ($invoice) => $invoice->getId())->toArray(); + $ids = array_values(array_filter( + $projectBilling->getInvoices()->map(fn ($invoice) => $invoice->getId())->toArray(), + fn (?int $id) => null !== $id + )); $spreadsheet = $billingService->exportInvoicesToSpreadsheet($ids); - $this->assertNotNull($spreadsheet); - $spreadsheetArray = $spreadsheet->getActiveSheet()->toArray(null, false, false); $this->assertCount(8, $spreadsheetArray); diff --git a/tests/Integration/Service/WorkloadReportServiceTest.php b/tests/Integration/Service/WorkloadReportServiceTest.php index 153a5011..e44d2768 100644 --- a/tests/Integration/Service/WorkloadReportServiceTest.php +++ b/tests/Integration/Service/WorkloadReportServiceTest.php @@ -2,7 +2,6 @@ namespace App\Tests\Integration\Service; -use App\Model\Reports\WorkloadReportData; use App\Model\Reports\WorkloadReportPeriodTypeEnum as PeriodTypeEnum; use App\Model\Reports\WorkloadReportViewModeEnum as ViewModeEnum; use App\Model\Reports\WorkloadReportWorker; @@ -16,8 +15,8 @@ public function testGetWorkloadReportProducesPeriodsForEveryIncludedWorker(): vo self::bootKernel(); $container = self::getContainer(); - /** @var WorkloadReportService $service */ $service = $container->get(WorkloadReportService::class); + \assert($service instanceof WorkloadReportService); $year = (int) (new \DateTime())->format('Y'); @@ -27,8 +26,6 @@ public function testGetWorkloadReportProducesPeriodsForEveryIncludedWorker(): vo ViewModeEnum::WORKLOAD, ); - $this->assertInstanceOf(WorkloadReportData::class, $report); - // 10 fixture workers, all included in reports. $this->assertCount(10, $report->workers); @@ -40,8 +37,7 @@ public function testGetWorkloadReportProducesPeriodsForEveryIncludedWorker(): vo /** @var WorkloadReportWorker $worker */ $worker = $report->workers->first(); - $this->assertInstanceOf(WorkloadReportWorker::class, $worker); - $this->assertSame($report->period->count(), $worker->loggedPercentage->count()); + $this->assertCount($report->period->count(), $worker->loggedPercentage); $this->assertGreaterThanOrEqual(0.0, $worker->average); } } diff --git a/tests/Unit/Command/SubscriptionHandlerCommandTest.php b/tests/Unit/Command/SubscriptionHandlerCommandTest.php index e0e70d26..c92f53c2 100644 --- a/tests/Unit/Command/SubscriptionHandlerCommandTest.php +++ b/tests/Unit/Command/SubscriptionHandlerCommandTest.php @@ -8,6 +8,7 @@ use App\Enum\SubscriptionSubjectEnum; use App\Repository\SubscriptionRepository; use App\Service\SubscriptionHandlerService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; @@ -15,9 +16,9 @@ class SubscriptionHandlerCommandTest extends TestCase { - private SubscriptionRepository $subscriptionRepository; - private SubscriptionHandlerService $subscriptionHandlerService; - private LoggerInterface $logger; + private SubscriptionRepository&MockObject $subscriptionRepository; + private SubscriptionHandlerService&MockObject $subscriptionHandlerService; + private LoggerInterface&MockObject $logger; protected function setUp(): void { diff --git a/tests/Unit/Command/SyncCommandTest.php b/tests/Unit/Command/SyncCommandTest.php index 637e52ba..e087c83e 100644 --- a/tests/Unit/Command/SyncCommandTest.php +++ b/tests/Unit/Command/SyncCommandTest.php @@ -5,6 +5,7 @@ use App\Command\SyncCommand; use App\Entity\Project; use App\Service\LeantimeApiService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; @@ -13,9 +14,9 @@ class SyncCommandTest extends TestCase { - private LeantimeApiService $leantimeApiService; - private HttpClientInterface $httpClient; - private LoggerInterface $logger; + private LeantimeApiService&MockObject $leantimeApiService; + private HttpClientInterface&MockObject $httpClient; + private LoggerInterface&MockObject $logger; private CommandTester $commandTester; protected function setUp(): void diff --git a/tests/Unit/Command/SyncDeletedCommandTest.php b/tests/Unit/Command/SyncDeletedCommandTest.php index bf1b92f6..834950f2 100644 --- a/tests/Unit/Command/SyncDeletedCommandTest.php +++ b/tests/Unit/Command/SyncDeletedCommandTest.php @@ -4,13 +4,14 @@ use App\Command\SyncDeletedCommand; use App\Service\LeantimeApiService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Tester\CommandTester; class SyncDeletedCommandTest extends TestCase { - private LeantimeApiService $leantimeApiService; + private LeantimeApiService&MockObject $leantimeApiService; private CommandTester $commandTester; protected function setUp(): void diff --git a/tests/Unit/Command/SyncModifiedCommandTest.php b/tests/Unit/Command/SyncModifiedCommandTest.php index 05a0f42c..139beb38 100644 --- a/tests/Unit/Command/SyncModifiedCommandTest.php +++ b/tests/Unit/Command/SyncModifiedCommandTest.php @@ -4,13 +4,14 @@ use App\Command\SyncModifiedCommand; use App\Service\LeantimeApiService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Tester\CommandTester; class SyncModifiedCommandTest extends TestCase { - private LeantimeApiService $leantimeApiService; + private LeantimeApiService&MockObject $leantimeApiService; private CommandTester $commandTester; protected function setUp(): void diff --git a/tests/Unit/MessageHandler/EntityRemovedFromDataProviderHandlerTest.php b/tests/Unit/MessageHandler/EntityRemovedFromDataProviderHandlerTest.php index 165f8af7..1a9d79ea 100644 --- a/tests/Unit/MessageHandler/EntityRemovedFromDataProviderHandlerTest.php +++ b/tests/Unit/MessageHandler/EntityRemovedFromDataProviderHandlerTest.php @@ -9,13 +9,14 @@ use App\Message\EntityRemovedFromDataProviderMessage; use App\MessageHandler\EntityRemovedFromDataProviderHandler; use App\Service\DataProviderService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException; class EntityRemovedFromDataProviderHandlerTest extends TestCase { - private DataProviderService $service; + private DataProviderService&MockObject $service; private EntityRemovedFromDataProviderHandler $handler; protected function setUp(): void diff --git a/tests/Unit/Service/BillableUnbilledHoursReportServiceTest.php b/tests/Unit/Service/BillableUnbilledHoursReportServiceTest.php index 2a404573..3b224d7c 100644 --- a/tests/Unit/Service/BillableUnbilledHoursReportServiceTest.php +++ b/tests/Unit/Service/BillableUnbilledHoursReportServiceTest.php @@ -5,20 +5,20 @@ use App\Entity\Issue; use App\Entity\Project; use App\Entity\Worklog; -use App\Model\Reports\BillableUnbilledHoursReportData; use App\Repository\WorkerRepository; use App\Repository\WorklogRepository; use App\Service\BillableUnbilledHoursReportService; use App\Service\DateTimeHelper; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Contracts\Translation\TranslatorInterface; class BillableUnbilledHoursReportServiceTest extends TestCase { - private WorklogRepository $worklogRepository; - private DateTimeHelper $dateTimeHelper; - private WorkerRepository $workerRepository; - private TranslatorInterface $translator; + private WorklogRepository&MockObject $worklogRepository; + private DateTimeHelper&MockObject $dateTimeHelper; + private WorkerRepository&MockObject $workerRepository; + private TranslatorInterface&MockObject $translator; private BillableUnbilledHoursReportService $service; protected function setUp(): void @@ -54,8 +54,6 @@ public function testFullYearUsesYearDateRange(): void ->willReturn([]); $result = $this->service->getBillableUnbilledHoursReport(2024); - - $this->assertInstanceOf(BillableUnbilledHoursReportData::class, $result); } public function testQuarterUsesQuarterDateRange(): void @@ -75,8 +73,6 @@ public function testQuarterUsesQuarterDateRange(): void ->willReturn([]); $result = $this->service->getBillableUnbilledHoursReport(2024, 2); - - $this->assertInstanceOf(BillableUnbilledHoursReportData::class, $result); } public function testAggregatesPerProject(): void diff --git a/tests/Unit/Service/BillingServiceTest.php b/tests/Unit/Service/BillingServiceTest.php index b0f0c662..5624d6d4 100644 --- a/tests/Unit/Service/BillingServiceTest.php +++ b/tests/Unit/Service/BillingServiceTest.php @@ -16,14 +16,15 @@ use App\Repository\InvoiceEntryRepository; use App\Repository\InvoiceRepository; use App\Service\BillingService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Contracts\Translation\TranslatorInterface; class BillingServiceTest extends TestCase { - private InvoiceRepository $invoiceRepository; - private InvoiceEntryRepository $invoiceEntryRepository; - private TranslatorInterface $translator; + private InvoiceRepository&MockObject $invoiceRepository; + private InvoiceEntryRepository&MockObject $invoiceEntryRepository; + private TranslatorInterface&MockObject $translator; private BillingService $billingService; protected function setUp(): void @@ -595,8 +596,8 @@ public function testGenerateSpreadsheetCsvResponseHeaders(): void $response = $this->billingService->generateSpreadsheetCsvResponse([1]); $this->assertSame('text/csv', $response->headers->get('Content-Type')); - $this->assertStringContainsString('attachment', $response->headers->get('Content-Disposition')); - $this->assertStringContainsString('.csv', $response->headers->get('Content-Disposition')); + $this->assertStringContainsString('attachment', (string) $response->headers->get('Content-Disposition')); + $this->assertStringContainsString('.csv', (string) $response->headers->get('Content-Disposition')); } public function testGenerateSpreadsheetCsvResponseUsesSemicolonDelimiter(): void @@ -624,7 +625,7 @@ public function testGenerateSpreadsheetCsvResponseUsesSemicolonDelimiter(): void $response = $this->billingService->generateSpreadsheetCsvResponse([1]); - $content = $response->getContent(); + $content = (string) $response->getContent(); $this->assertStringContainsString(';', $content); } diff --git a/tests/Unit/Service/CybersecurityReportServiceTest.php b/tests/Unit/Service/CybersecurityReportServiceTest.php index c267a97e..06e0f1b4 100644 --- a/tests/Unit/Service/CybersecurityReportServiceTest.php +++ b/tests/Unit/Service/CybersecurityReportServiceTest.php @@ -5,18 +5,18 @@ use App\Entity\Issue; use App\Entity\Project; use App\Entity\Worklog; -use App\Model\Reports\CybersecurityReportData; use App\Repository\IssueRepository; use App\Repository\ProjectRepository; use App\Repository\WorklogRepository; use App\Service\CybersecurityReportService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class CybersecurityReportServiceTest extends TestCase { - private IssueRepository $issueRepository; - private WorklogRepository $worklogRepository; - private ProjectRepository $projectRepository; + private IssueRepository&MockObject $issueRepository; + private WorklogRepository&MockObject $worklogRepository; + private ProjectRepository&MockObject $projectRepository; private CybersecurityReportService $service; protected function setUp(): void @@ -109,7 +109,6 @@ public function testReturnsEmptyReportWhenNoIssues(): void $result = $this->service->getCybersecurityReport(null, null, 'Cybersikkerhedsaftale'); - $this->assertInstanceOf(CybersecurityReportData::class, $result); $this->assertSame([], $result->projects); $this->assertSame(0.0, $result->totalSpent); } diff --git a/tests/Unit/Service/DanishHolidayHelperTest.php b/tests/Unit/Service/DanishHolidayHelperTest.php index 90cc0996..8f5553a8 100644 --- a/tests/Unit/Service/DanishHolidayHelperTest.php +++ b/tests/Unit/Service/DanishHolidayHelperTest.php @@ -15,7 +15,7 @@ protected function setUp(): void $this->helper = DanishHolidayHelper::getInstance(); } - public function testHmm() + public function testHmm(): void { $this->assertNotEquals( $this->helper->getHolidays(2024), @@ -26,7 +26,7 @@ public function testHmm() /** * @dataProvider dataEaster */ - public function testEaster(int $year, \DateTimeInterface $expected) + public function testEaster(int $year, \DateTimeInterface $expected): void { $this->assertSameDate( $expected, @@ -34,6 +34,9 @@ public function testEaster(int $year, \DateTimeInterface $expected) ); } + /** + * @return iterable + */ public static function dataEaster(): iterable { yield '[2023]' => [ @@ -52,7 +55,7 @@ public static function dataEaster(): iterable ]; } - public function testHolidayNames() + public function testHolidayNames(): void { $year = 2024; $expected = [ @@ -76,12 +79,15 @@ public function testHolidayNames() /** * @dataProvider dataNextNonHoliday */ - public function testNextNonHoliday(\DateTimeInterface $date, ?\DateTimeInterface $expected) + public function testNextNonHoliday(\DateTimeInterface $date, \DateTimeInterface $expected): void { $actual = $this->helper->getNextNonHoliday($date); $this->assertSameDate($expected, $actual); } + /** + * @return iterable + */ public static function dataNextNonHoliday(): iterable { yield '2024-03-31' => [ @@ -93,12 +99,15 @@ public static function dataNextNonHoliday(): iterable /** * @dataProvider dataNextBankDay */ - public function testNextBankDay(\DateTimeInterface $date, ?\DateTimeInterface $expected) + public function testNextBankDay(\DateTimeInterface $date, \DateTimeInterface $expected): void { $actual = $this->helper->getNextBankDay($date); $this->assertSameDate($expected, $actual); } + /** + * @return iterable + */ public static function dataNextBankDay(): iterable { yield '2024-03-29' => [ @@ -125,12 +134,15 @@ public static function dataNextBankDay(): iterable /** * @dataProvider dataNextBankDay30 */ - public function testNextBankDay30(\DateTimeInterface $date, ?\DateTimeInterface $expected) + public function testNextBankDay30(\DateTimeInterface $date, \DateTimeInterface $expected): void { $actual = $this->helper->getNextBankDay($date, 30); $this->assertSameDate($expected, $actual); } + /** + * @return iterable + */ public static function dataNextBankDay30(): iterable { yield '2024-03-29' => [ @@ -152,12 +164,15 @@ public static function dataNextBankDay30(): iterable /** * @dataProvider dataIsBankHoliday */ - public function testIsBankHoliday(\DateTimeInterface $date, bool $expected) + public function testIsBankHoliday(\DateTimeInterface $date, bool $expected): void { $actual = $this->helper->isBankHoliday($date); $this->assertSame($expected, $actual); } + /** + * @return iterable + */ public static function dataIsBankHoliday(): iterable { yield '2024-12-31' => [ @@ -196,10 +211,10 @@ public static function dataIsBankHoliday(): iterable ]; } - private function assertSameDate(\DateTimeInterface $expected, \DateTimeInterface $actual, string $message = '') + private function assertSameDate(\DateTimeInterface $expected, \DateTimeInterface $actual, string $message = ''): void { try { - return $this->assertEquals($expected->getTimestamp(), $actual->getTimestamp(), $message); + $this->assertEquals($expected->getTimestamp(), $actual->getTimestamp(), $message); } catch (ExpectationFailedException $exception) { throw new ExpectationFailedException(sprintf('Failed asserting that %s matches expected %s.', $expected->format(\DateTimeInterface::ATOM), $actual->format(\DateTimeInterface::ATOM)), $exception->getComparisonFailure(), $exception); } diff --git a/tests/Unit/Service/DashboardServiceTest.php b/tests/Unit/Service/DashboardServiceTest.php index a6ef180d..96d74e00 100644 --- a/tests/Unit/Service/DashboardServiceTest.php +++ b/tests/Unit/Service/DashboardServiceTest.php @@ -9,13 +9,14 @@ use App\Repository\WorklogRepository; use App\Service\DashboardService; use App\Service\DateTimeHelper; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class DashboardServiceTest extends TestCase { - private WorkerRepository $workerRepository; - private WorklogRepository $worklogRepository; - private DateTimeHelper $dateTimeHelper; + private WorkerRepository&MockObject $workerRepository; + private WorklogRepository&MockObject $worklogRepository; + private DateTimeHelper&MockObject $dateTimeHelper; private DashboardService $dashboardService; protected function setUp(): void @@ -148,6 +149,5 @@ public function testGetUserDashboardYearStatusCalculation(): void $this->assertInstanceOf(DashboardData::class, $result); // yearStatus = (totalTimeSpent - yearNormToDate) / 3600 // The exact value depends on how many weekdays in 2023, but it should be a number - $this->assertIsFloat($result->workHours); } } diff --git a/tests/Unit/Service/DataProviderServiceTest.php b/tests/Unit/Service/DataProviderServiceTest.php index 04438414..4f369ce0 100644 --- a/tests/Unit/Service/DataProviderServiceTest.php +++ b/tests/Unit/Service/DataProviderServiceTest.php @@ -25,22 +25,23 @@ use App\Service\DataProviderService; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; class DataProviderServiceTest extends TestCase { - private EntityManagerInterface $entityManager; - private ProjectRepository $projectRepository; - private IssueRepository $issueRepository; - private WorklogRepository $worklogRepository; - private DataProviderRepository $dataProviderRepository; - private VersionRepository $versionRepository; - private WorkerRepository $workerRepository; - private EpicRepository $epicRepository; - private ContainerInterface $transportLocator; - private LoggerInterface $logger; + private EntityManagerInterface&MockObject $entityManager; + private ProjectRepository&MockObject $projectRepository; + private IssueRepository&MockObject $issueRepository; + private WorklogRepository&MockObject $worklogRepository; + private DataProviderRepository&MockObject $dataProviderRepository; + private VersionRepository&MockObject $versionRepository; + private WorkerRepository&MockObject $workerRepository; + private EpicRepository&MockObject $epicRepository; + private ContainerInterface&MockObject $transportLocator; + private LoggerInterface&MockObject $logger; private DataProviderService $service; private DataProvider $dataProvider; diff --git a/tests/Unit/Service/DateTimeHelperTest.php b/tests/Unit/Service/DateTimeHelperTest.php index 5ff1afd9..d82d1254 100644 --- a/tests/Unit/Service/DateTimeHelperTest.php +++ b/tests/Unit/Service/DateTimeHelperTest.php @@ -18,6 +18,8 @@ public function setUp(): void /** * @dataProvider weekYearProvider + * + * @param array{dateFrom: \DateTime, dateTo: \DateTime} $expected */ public function testGetFirstAndLastDateOfWeek(int $weekNumber, int $year, array $expected): void { @@ -25,6 +27,9 @@ public function testGetFirstAndLastDateOfWeek(int $weekNumber, int $year, array $this->assertEquals($expected, $result); } + /** + * @return array + */ public static function weekYearProvider(): array { return [ @@ -65,6 +70,8 @@ public static function weekYearProvider(): array /** * @dataProvider monthYearProvider + * + * @param array{dateFrom: \DateTime, dateTo: \DateTime} $expected */ public function testGetFirstAndLastDateOfMonth(int $monthNumber, int $year, array $expected): void { @@ -72,6 +79,9 @@ public function testGetFirstAndLastDateOfMonth(int $monthNumber, int $year, arra $this->assertEquals($expected, $result); } + /** + * @return array + */ public static function monthYearProvider(): array { return [ @@ -112,6 +122,8 @@ public static function monthYearProvider(): array /** * @dataProvider weeksOfYearProvider + * + * @param array $expected */ public function testGetWeeksOfYear(int $year, array $expected): void { @@ -119,6 +131,9 @@ public function testGetWeeksOfYear(int $year, array $expected): void $this->assertEquals($expected, $result); } + /** + * @return array}> + */ public static function weeksOfYearProvider(): array { return [ @@ -146,6 +161,9 @@ public function testGetMonthName(int $monthNumber, string $expectedMonthName): v $this->assertEquals($expectedMonthName, $monthName); } + /** + * @return array + */ public static function monthNameDataProvider(): array { return [ @@ -166,6 +184,8 @@ public static function monthNameDataProvider(): array /** * @dataProvider yearProvider + * + * @param array{dateFrom: \DateTime, dateTo: \DateTime} $expected */ public function testGetFirstAndLastDateOfYear(int $year, array $expected): void { @@ -173,6 +193,9 @@ public function testGetFirstAndLastDateOfYear(int $year, array $expected): void $this->assertEquals($expected, $result); } + /** + * @return array + */ public static function yearProvider(): array { return [ diff --git a/tests/Unit/Service/ForecastReportServiceTest.php b/tests/Unit/Service/ForecastReportServiceTest.php index 66b57251..44986c62 100644 --- a/tests/Unit/Service/ForecastReportServiceTest.php +++ b/tests/Unit/Service/ForecastReportServiceTest.php @@ -6,19 +6,19 @@ use App\Entity\Project; use App\Entity\Worker; use App\Entity\Worklog; -use App\Model\Reports\ForecastReportData; use App\Repository\WorkerRepository; use App\Repository\WorklogRepository; use App\Service\ForecastReportService; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class ForecastReportServiceTest extends TestCase { - private WorklogRepository $worklogRepository; - private WorkerRepository $workerRepository; - private EntityManagerInterface $entityManager; + private WorklogRepository&MockObject $worklogRepository; + private WorkerRepository&MockObject $workerRepository; + private EntityManagerInterface&MockObject $entityManager; private ForecastReportService $service; protected function setUp(): void @@ -64,7 +64,6 @@ public function testEmptyWorklogsReturnsEmptyReport(): void new \DateTime('2024-01-31'), ); - $this->assertInstanceOf(ForecastReportData::class, $result); $this->assertEqualsWithDelta(0.0, $result->totalInvoiced, 0.001); $this->assertEqualsWithDelta(0.0, $result->totalInvoicedAndRecorded, 0.001); } @@ -106,7 +105,9 @@ public function testAggregatesProjectHours(): void $this->assertEqualsWithDelta(2.0, $result->totalInvoiced, 0.001); $this->assertEqualsWithDelta(0.0, $result->totalInvoicedAndRecorded, 0.001); $this->assertArrayHasKey(1, $result->projects); - $this->assertEqualsWithDelta(2.0, $result->projects[1]->invoiced, 0.001); + $projectData = $result->projects[1]; + $this->assertNotNull($projectData); + $this->assertEqualsWithDelta(2.0, $projectData->invoiced, 0.001); } public function testBilledWorklogsCountAsRecorded(): void @@ -186,6 +187,7 @@ public function testWorkerNameMapping(): void ); $projectData = $result->projects[1]; + $this->assertNotNull($projectData); $issueData = $projectData->issues['[no tag]']; $versionData = $issueData->versions['[no version]']; $worklogData = $versionData->worklogs[1]; diff --git a/tests/Unit/Service/HourReportServiceTest.php b/tests/Unit/Service/HourReportServiceTest.php index a3d1ad4d..570be5d5 100644 --- a/tests/Unit/Service/HourReportServiceTest.php +++ b/tests/Unit/Service/HourReportServiceTest.php @@ -7,16 +7,16 @@ use App\Entity\Project; use App\Entity\Version; use App\Entity\Worklog; -use App\Model\Reports\HourReportData; use App\Repository\IssueRepository; use App\Repository\WorklogRepository; use App\Service\HourReportService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class HourReportServiceTest extends TestCase { - private IssueRepository $issueRepository; - private WorklogRepository $worklogRepository; + private IssueRepository&MockObject $issueRepository; + private WorklogRepository&MockObject $worklogRepository; private HourReportService $hourReportService; public function setUp(): void @@ -78,8 +78,6 @@ public function testGetHourReportWithVersionFilter(): void new \DateTime('2024-01-31'), $version, ); - - $this->assertInstanceOf(HourReportData::class, $result); } public function testGetHourReportWithoutVersionUsesProjectFilter(): void @@ -99,8 +97,6 @@ public function testGetHourReportWithoutVersionUsesProjectFilter(): void new \DateTime('2024-01-01'), new \DateTime('2024-01-31'), ); - - $this->assertInstanceOf(HourReportData::class, $result); } public function testGetHourReportSkipsIssuesWithNoWorklogsInRange(): void @@ -208,6 +204,7 @@ public function testGetHourReportGroupsByEpic(): void $this->assertTrue($result->projectTags->containsKey('Backend Work')); $tag = $result->projectTags->get('Backend Work'); + $this->assertNotNull($tag); $this->assertSame('Backend Work', $tag->tag); } @@ -336,6 +333,7 @@ public function testGetHourReportMultipleIssuesSameEpicAggregated(): void $this->assertEqualsWithDelta(8.0, $result->projectTotalEstimated, 0.001); $tag = $result->projectTags->get('Frontend'); + $this->assertNotNull($tag); $this->assertEqualsWithDelta(8.0, $tag->totalEstimated, 0.001); $this->assertEqualsWithDelta(3.0, $tag->totalSpent, 0.001); $this->assertCount(2, $tag->projectTickets); diff --git a/tests/Unit/Service/InvoicingRateReportServiceTest.php b/tests/Unit/Service/InvoicingRateReportServiceTest.php index a2132e9e..1f00a63b 100644 --- a/tests/Unit/Service/InvoicingRateReportServiceTest.php +++ b/tests/Unit/Service/InvoicingRateReportServiceTest.php @@ -3,19 +3,19 @@ namespace App\Tests\Unit\Service; use App\Entity\Worker; -use App\Model\Reports\InvoicingRateReportData; use App\Model\Reports\WorkloadReportPeriodTypeEnum as PeriodTypeEnum; use App\Repository\WorkerRepository; use App\Repository\WorklogRepository; use App\Service\DateTimeHelper; use App\Service\InvoicingRateReportService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class InvoicingRateReportServiceTest extends TestCase { - private WorkerRepository $workerRepository; - private WorklogRepository $worklogRepository; - private DateTimeHelper $dateTimeHelper; + private WorkerRepository&MockObject $workerRepository; + private WorklogRepository&MockObject $worklogRepository; + private DateTimeHelper&MockObject $dateTimeHelper; private InvoicingRateReportService $service; protected function setUp(): void @@ -34,11 +34,10 @@ protected function setUp(): void public function testMonthPeriodReturns12Periods(): void { $this->workerRepository->method('findAllIncludedInReports')->willReturn([]); - $this->dateTimeHelper->method('getMonthName')->willReturnCallback(fn ($m) => date('F', mktime(0, 0, 0, $m, 10))); + $this->dateTimeHelper->method('getMonthName')->willReturnCallback(fn ($m) => date('F', (int) mktime(0, 0, 0, $m, 10))); $result = $this->service->getInvoicingRateReport(2024, PeriodTypeEnum::MONTH); - $this->assertInstanceOf(InvoicingRateReportData::class, $result); $this->assertCount(12, $result->period); } @@ -88,7 +87,7 @@ public function testPercentageCalculation(): void $this->worklogRepository->method('findBillableWorklogsByWorkerAndDateRange')->willReturn([]); $this->worklogRepository->method('findBilledWorklogsByWorkerAndDateRange')->willReturn([]); - $this->dateTimeHelper->method('getMonthName')->willReturnCallback(fn ($m) => date('F', mktime(0, 0, 0, $m, 10))); + $this->dateTimeHelper->method('getMonthName')->willReturnCallback(fn ($m) => date('F', (int) mktime(0, 0, 0, $m, 10))); $this->dateTimeHelper->method('getFirstAndLastDateOfMonth')->willReturn([ 'dateFrom' => new \DateTime('2024-01-01'), 'dateTo' => new \DateTime('2024-01-31'), @@ -96,11 +95,11 @@ public function testPercentageCalculation(): void $result = $this->service->getInvoicingRateReport(2024, PeriodTypeEnum::MONTH); - $this->assertInstanceOf(InvoicingRateReportData::class, $result); $this->assertCount(1, $result->workers); // With 0 logged hours, average should be 0 $workerData = $result->workers->first(); + $this->assertNotFalse($workerData); $this->assertEqualsWithDelta(0.0, $workerData->average, 0.001); } diff --git a/tests/Unit/Service/ManagementReportServiceTest.php b/tests/Unit/Service/ManagementReportServiceTest.php index d809a0e1..bed57ac9 100644 --- a/tests/Unit/Service/ManagementReportServiceTest.php +++ b/tests/Unit/Service/ManagementReportServiceTest.php @@ -3,13 +3,14 @@ namespace App\Tests\Unit\Service; use App\Service\ManagementReportService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Contracts\Translation\TranslatorInterface; class ManagementReportServiceTest extends TestCase { - private TranslatorInterface $translator; + private TranslatorInterface&MockObject $translator; private ManagementReportService $service; protected function setUp(): void @@ -46,8 +47,8 @@ public function testGenerateSpreadsheetCsvResponseReturnsStreamedResponse(): voi $response = $this->service->generateSpreadsheetCsvResponse($groupedInvoices, $dateInterval); $this->assertInstanceOf(StreamedResponse::class, $response); - $this->assertStringContainsString('application/vnd.ms-excel', $response->headers->get('Content-Type')); - $this->assertStringContainsString('management-report', $response->headers->get('Content-Disposition')); + $this->assertStringContainsString('application/vnd.ms-excel', (string) $response->headers->get('Content-Type')); + $this->assertStringContainsString('management-report', (string) $response->headers->get('Content-Disposition')); } public function testGenerateSpreadsheetCsvResponseCalculatesQuarterSums(): void diff --git a/tests/Unit/Service/PlanningServiceTest.php b/tests/Unit/Service/PlanningServiceTest.php index 3d115979..3001dd87 100644 --- a/tests/Unit/Service/PlanningServiceTest.php +++ b/tests/Unit/Service/PlanningServiceTest.php @@ -6,20 +6,20 @@ use App\Entity\Project as ProjectEntity; use App\Entity\Worker; use App\Enum\IssueStatusEnum; -use App\Model\Planning\PlanningData; use App\Repository\IssueRepository; use App\Repository\ProjectRepository; use App\Repository\WorkerRepository; use App\Service\DateTimeHelper; use App\Service\PlanningService; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class PlanningServiceTest extends TestCase { - private DateTimeHelper $dateTimeHelper; - private IssueRepository $issueRepository; - private WorkerRepository $workerRepository; - private ProjectRepository $projectRepository; + private DateTimeHelper&MockObject $dateTimeHelper; + private IssueRepository&MockObject $issueRepository; + private WorkerRepository&MockObject $workerRepository; + private ProjectRepository&MockObject $projectRepository; private PlanningService $service; protected function setUp(): void @@ -53,7 +53,6 @@ public function testGetPlanningDataBuildsWeeks(): void $result = $this->service->getPlanningData(2024, null); - $this->assertInstanceOf(PlanningData::class, $result); // With holidayPlanning=false (default), weeks are grouped into support+sprint periods // 52 weeks / (1 support + 3 sprint) = 13 groups * 2 entries per group = 26 $this->assertCount(26, $result->weeks); @@ -216,6 +215,7 @@ public function testUnassignedIssues(): void $this->assertTrue($result->assignees->containsKey('unassigned')); $assignee = $result->assignees->get('unassigned'); + $this->assertNotNull($assignee); $this->assertSame('Unassigned', $assignee->displayName); } diff --git a/tests/Unit/Service/ProjectBillingServiceTest.php b/tests/Unit/Service/ProjectBillingServiceTest.php index c6e1928c..f7f3c51d 100644 --- a/tests/Unit/Service/ProjectBillingServiceTest.php +++ b/tests/Unit/Service/ProjectBillingServiceTest.php @@ -23,19 +23,20 @@ use App\Service\InvoiceEntryHelper; use App\Service\ProjectBillingService; use Doctrine\ORM\EntityManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Contracts\Translation\TranslatorInterface; class ProjectBillingServiceTest extends TestCase { - private ProjectBillingRepository $projectBillingRepository; - private BillingService $billingService; - private IssueRepository $issueRepository; - private ClientRepository $clientRepository; - private ClientHelper $clientHelper; - private EntityManagerInterface $entityManager; - private TranslatorInterface $translator; - private InvoiceEntryHelper $invoiceEntryHelper; + private ProjectBillingRepository&MockObject $projectBillingRepository; + private BillingService&MockObject $billingService; + private IssueRepository&MockObject $issueRepository; + private ClientRepository&MockObject $clientRepository; + private ClientHelper&MockObject $clientHelper; + private EntityManagerInterface&MockObject $entityManager; + private TranslatorInterface&MockObject $translator; + private InvoiceEntryHelper&MockObject $invoiceEntryHelper; private ProjectBillingService $service; protected function setUp(): void @@ -419,7 +420,7 @@ public function testCreateProjectBillingCreatesProductInvoiceEntries(): void $this->service->createProjectBilling(1); - $productEntries = array_filter($persistedEntities, fn ($e) => $e instanceof InvoiceEntry && 'Widget' === str_contains($e->getProduct() ?? '', 'Widget')); + $productEntries = array_filter($persistedEntities, fn ($e) => $e instanceof InvoiceEntry && str_contains($e->getProduct() ?? '', 'Widget')); $this->assertNotEmpty($projectBilling->getInvoices()); } diff --git a/tests/Unit/Service/SubscriptionHandlerServiceTest.php b/tests/Unit/Service/SubscriptionHandlerServiceTest.php index 07877681..d3242c35 100644 --- a/tests/Unit/Service/SubscriptionHandlerServiceTest.php +++ b/tests/Unit/Service/SubscriptionHandlerServiceTest.php @@ -13,6 +13,7 @@ use App\Service\HourReportService; use App\Service\SubscriptionHandlerService; use Doctrine\ORM\EntityManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\Mailer\MailerInterface; @@ -21,14 +22,14 @@ class SubscriptionHandlerServiceTest extends TestCase { - private ProjectRepository $projectRepository; - private VersionRepository $versionRepository; - private HourReportService $hourReportService; - private Environment $twig; - private LoggerInterface $logger; - private MailerInterface $mailer; - private TranslatorInterface $translator; - private EntityManagerInterface $entityManager; + private ProjectRepository&MockObject $projectRepository; + private VersionRepository&MockObject $versionRepository; + private HourReportService&MockObject $hourReportService; + private Environment&MockObject $twig; + private LoggerInterface&MockObject $logger; + private MailerInterface&MockObject $mailer; + private TranslatorInterface&MockObject $translator; + private EntityManagerInterface&MockObject $entityManager; protected function setUp(): void { @@ -119,7 +120,7 @@ public function testHandleSubscriptionWithVersion(): void $version = new Version(); $this->projectRepository->method('find')->with(1)->willReturn($project); - $this->versionRepository->method('findOneBy')->with(['versionId' => 42])->willReturn($version); + $this->versionRepository->method('find')->with(42)->willReturn($version); $this->hourReportService->expects($this->once()) ->method('getHourReport') ->with($project, $this->anything(), $this->anything(), $version) diff --git a/tests/Unit/Service/WorkloadReportServiceTest.php b/tests/Unit/Service/WorkloadReportServiceTest.php index d754e84f..82cf8330 100644 --- a/tests/Unit/Service/WorkloadReportServiceTest.php +++ b/tests/Unit/Service/WorkloadReportServiceTest.php @@ -4,7 +4,6 @@ use App\Entity\Worker; use App\Entity\Worklog; -use App\Model\Reports\WorkloadReportData; use App\Model\Reports\WorkloadReportPeriodTypeEnum as PeriodTypeEnum; use App\Model\Reports\WorkloadReportViewModeEnum as ViewModeEnum; use App\Repository\WorkerRepository; @@ -19,7 +18,7 @@ class WorkloadReportServiceTest extends TestCase /** * @throws Exception */ - public function testGetWorkloadReport() + public function testGetWorkloadReport(): void { $workerMock1 = $this->createMock(Worker::class); $workerMock1->method('getUserIdentifier')->willReturn('test0@test'); @@ -51,7 +50,10 @@ public function testGetWorkloadReport() $dateTimeHelperMock = $this->createMock(DateTimeHelper::class); $dateTimeHelperMock->method('getWeeksOfYear')->willReturn(range(1, 52)); $dateTimeHelperMock->method('getMonthName')->willReturnCallback(function ($month) { - return date('F', mktime(0, 0, 0, $month, 10)); + $timestamp = mktime(0, 0, 0, $month, 10); + \assert(false !== $timestamp); + + return date('F', $timestamp); }); $dateTimeHelperMock->method('getFirstAndLastDateOfWeek')->willReturn([ 'dateFrom' => new \DateTime('2024-01-01 00:00:00'), @@ -72,16 +74,16 @@ public function testGetWorkloadReport() $workloadReportService = new WorkloadReportService($workerRepoMock, $worklogRepoMock, $dateTimeHelperMock); $result = $workloadReportService->getWorkloadReport(2024, PeriodTypeEnum::WEEK, ViewModeEnum::WORKLOAD); - $this->assertInstanceOf(WorkloadReportData::class, $result); + $this->assertSame(PeriodTypeEnum::WEEK->value, $result->viewmode); $result = $workloadReportService->getWorkloadReport(2024, PeriodTypeEnum::MONTH, ViewModeEnum::WORKLOAD); - $this->assertInstanceOf(WorkloadReportData::class, $result); + $this->assertSame(PeriodTypeEnum::MONTH->value, $result->viewmode); $result = $workloadReportService->getWorkloadReport(2024, PeriodTypeEnum::YEAR, ViewModeEnum::WORKLOAD); - $this->assertInstanceOf(WorkloadReportData::class, $result); + $this->assertSame(PeriodTypeEnum::YEAR->value, $result->viewmode); } - public function testExceptionIsThrownWhenWorkerIdentifierIsEmpty() + public function testExceptionIsThrownWhenWorkerIdentifierIsEmpty(): void { $workerMock1 = $this->createMock(Worker::class); $workerMock1->method('getUserIdentifier')->willReturn('test0@test'); @@ -116,7 +118,10 @@ public function testExceptionIsThrownWhenWorkerIdentifierIsEmpty() $dateTimeHelperMock = $this->createMock(DateTimeHelper::class); $dateTimeHelperMock->method('getWeeksOfYear')->willReturn(range(1, 52)); $dateTimeHelperMock->method('getMonthName')->willReturnCallback(function ($month) { - return date('F', mktime(0, 0, 0, $month, 10)); + $timestamp = mktime(0, 0, 0, $month, 10); + \assert(false !== $timestamp); + + return date('F', $timestamp); }); $dateTimeHelperMock->method('getFirstAndLastDateOfWeek')->willReturn([ 'dateFrom' => new \DateTime('2024-01-01 00:00:00'), @@ -140,7 +145,7 @@ public function testExceptionIsThrownWhenWorkerIdentifierIsEmpty() $workloadReportService->getWorkloadReport(2024, PeriodTypeEnum::WEEK, ViewModeEnum::WORKLOAD); } - public function testExceptionIsThrownWhenWorkerWorkloadIsUnset() + public function testExceptionIsThrownWhenWorkerWorkloadIsUnset(): void { $workerMock1 = $this->createMock(Worker::class); $workerMock1->method('getUserIdentifier')->willReturn('test0@test'); @@ -175,7 +180,10 @@ public function testExceptionIsThrownWhenWorkerWorkloadIsUnset() $dateTimeHelperMock = $this->createMock(DateTimeHelper::class); $dateTimeHelperMock->method('getWeeksOfYear')->willReturn(range(1, 52)); $dateTimeHelperMock->method('getMonthName')->willReturnCallback(function ($month) { - return date('F', mktime(0, 0, 0, $month, 10)); + $timestamp = mktime(0, 0, 0, $month, 10); + \assert(false !== $timestamp); + + return date('F', $timestamp); }); $dateTimeHelperMock->method('getFirstAndLastDateOfWeek')->willReturn([ 'dateFrom' => new \DateTime('2024-01-01 00:00:00'),