diff --git a/.env.dist b/.env.dist index 0a9bdd3f..881374f8 100644 --- a/.env.dist +++ b/.env.dist @@ -40,7 +40,7 @@ APP_SECRET=change_this_to_something_unique #================================================================================ # Running behind reverse proxies? Try these: # TRUSTED_PROXIES=127.0.0.1,127.0.0.2 -# TRUSTED_HOSTS=localhost,example.com +# TRUSTED_HOSTS=localhost|example.com #================================================================================ # unlikely, that you need to change this one diff --git a/assets/js/plugins/KimaiRemoteModal.js b/assets/js/plugins/KimaiRemoteModal.js index c84e8536..da80c7a8 100644 --- a/assets/js/plugins/KimaiRemoteModal.js +++ b/assets/js/plugins/KimaiRemoteModal.js @@ -6,7 +6,7 @@ */ /*! - * [KIMAI] KimaiRecentActivities: responsible to reload the users recent activities + * [KIMAI] KimaiRemoteModal: load remote content (without forms) into a modal */ import KimaiPlugin from '../KimaiPlugin'; diff --git a/composer.lock b/composer.lock index db5fcebb..f22f8636 100644 --- a/composer.lock +++ b/composer.lock @@ -3463,16 +3463,16 @@ }, { "name": "nelmio/api-doc-bundle", - "version": "v5.7.0", + "version": "v5.7.1", "source": { "type": "git", "url": "https://github.com/nelmio/NelmioApiDocBundle.git", - "reference": "2a79732f8d10a5a0faf3934d906ac9e4c720aa2f" + "reference": "772d26a9d940c9a8d173f75f0fdbaab47c1c87f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/2a79732f8d10a5a0faf3934d906ac9e4c720aa2f", - "reference": "2a79732f8d10a5a0faf3934d906ac9e4c720aa2f", + "url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/772d26a9d940c9a8d173f75f0fdbaab47c1c87f2", + "reference": "772d26a9d940c9a8d173f75f0fdbaab47c1c87f2", "shasum": "" }, "require": { @@ -3574,7 +3574,7 @@ ], "support": { "issues": "https://github.com/nelmio/NelmioApiDocBundle/issues", - "source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v5.7.0" + "source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v5.7.1" }, "funding": [ { @@ -3582,7 +3582,7 @@ "type": "github" } ], - "time": "2025-11-10T09:23:31+00:00" + "time": "2025-11-13T10:32:15+00:00" }, { "name": "nelmio/cors-bundle", @@ -14489,16 +14489,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/d74205c497bfbca49f34d4bc4c19c17e22db4ebb", + "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb", "shasum": "" }, "require": { @@ -14527,7 +14527,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.0" }, "funding": [ { @@ -14535,7 +14535,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-13T13:44:09+00:00" } ], "aliases": [], diff --git a/config/packages/nelmio_api_doc.yaml b/config/packages/nelmio_api_doc.yaml index 455ac9c8..5ec15a0d 100644 --- a/config/packages/nelmio_api_doc.yaml +++ b/config/packages/nelmio_api_doc.yaml @@ -3,20 +3,20 @@ nelmio_api_doc: models: use_jms: true names: - - { alias: CustomerEditForm, type: App\Form\API\CustomerApiEditForm, groups: [Default, Entity, Customer] } + - { alias: CustomerEditForm, type: App\Form\API\CustomerApiEditForm, groups: [Default, Entity, Customer, Customer_Entity] } - { alias: CustomerEntity, type: App\Entity\Customer, groups: [Default, Entity, Customer, Customer_Entity, Not_Expanded] } - { alias: Customer, type: App\Entity\Customer, groups: [Default, Not_Expanded] } - { alias: CustomerRate, type: App\Entity\CustomerRate, groups: [Default, Entity, Customer_Rate] } - { alias: CustomerRateForm, type: App\Form\API\CustomerRateApiForm, groups: [Default, Entity, Customer_Rate] } - { alias: CustomerCollection, type: App\Entity\Customer, groups: [Default, Collection, Customer] } - - { alias: ProjectEditForm, type: App\Form\API\ProjectApiEditForm, groups: [Default, Entity, Project] } + - { alias: ProjectEditForm, type: App\Form\API\ProjectApiEditForm, groups: [Default, Entity, Project, Project_Entity] } - { alias: ProjectEntity, type: App\Entity\Project, groups: [Default, Entity, Project, Project_Entity, Not_Expanded] } - { alias: Project, type: App\Entity\Project, groups: [Default, Not_Expanded] } - { alias: ProjectExpanded, type: App\Entity\Project, groups: [Default, Expanded] } - { alias: ProjectRate, type: App\Entity\ProjectRate, groups: [Default, Entity, Project_Rate] } - { alias: ProjectRateForm, type: App\Form\API\ProjectRateApiForm, groups: [Default, Entity, Project_Rate] } - { alias: ProjectCollection, type: App\Entity\Project, groups: [Default, Collection, Project] } - - { alias: ActivityEditForm, type: App\Form\API\ActivityApiEditForm, groups: [Default, Entity, Activity] } + - { alias: ActivityEditForm, type: App\Form\API\ActivityApiEditForm, groups: [Default, Entity, Activity, Activity_Entity] } - { alias: ActivityEntity, type: App\Entity\Activity, groups: [Default, Entity, Activity, Activity_Entity, Not_Expanded] } - { alias: Activity, type: App\Entity\Activity, groups: [Default, Not_Expanded] } - { alias: ActivityExpanded, type: App\Entity\Activity, groups: [Default, Expanded] } diff --git a/src/API/ActivityController.php b/src/API/ActivityController.php index 8114e7bc..0cbe5f0b 100644 --- a/src/API/ActivityController.php +++ b/src/API/ActivityController.php @@ -36,7 +36,6 @@ use Symfony\Component\Security\Http\Attribute\IsGranted; final class ActivityController extends BaseApiController { public const GROUPS_ENTITY = ['Default', 'Entity', 'Activity', 'Activity_Entity']; - public const GROUPS_FORM = ['Default', 'Entity', 'Activity']; public const GROUPS_COLLECTION = ['Default', 'Collection', 'Activity']; public const GROUPS_RATE = ['Default', 'Entity', 'Activity_Rate']; @@ -130,8 +129,7 @@ final class ActivityController extends BaseApiController throw $this->createAccessDeniedException('User cannot create activities'); } - $activity = new Activity(); - $this->activityService->loadMetaFields($activity); + $activity = $this->activityService->createNewActivity(); $form = $this->createForm(ActivityApiEditForm::class, $activity, [ 'include_budget' => $this->isGranted('budget', $activity), @@ -150,7 +148,7 @@ final class ActivityController extends BaseApiController } $view = new View($form); - $view->getContext()->setGroups(self::GROUPS_FORM); + $view->getContext()->setGroups(self::GROUPS_ENTITY); return $this->viewHandler->handle($view); } @@ -177,7 +175,7 @@ final class ActivityController extends BaseApiController if (false === $form->isValid()) { $view = new View($form, Response::HTTP_OK); - $view->getContext()->setGroups(self::GROUPS_FORM); + $view->getContext()->setGroups(self::GROUPS_ENTITY); return $this->viewHandler->handle($view); } diff --git a/src/API/CustomerController.php b/src/API/CustomerController.php index e7542df7..ac2bf089 100644 --- a/src/API/CustomerController.php +++ b/src/API/CustomerController.php @@ -36,7 +36,6 @@ use Symfony\Component\Security\Http\Attribute\IsGranted; final class CustomerController extends BaseApiController { public const GROUPS_ENTITY = ['Default', 'Entity', 'Customer', 'Customer_Entity']; - public const GROUPS_FORM = ['Default', 'Entity', 'Customer']; public const GROUPS_COLLECTION = ['Default', 'Collection', 'Customer']; public const GROUPS_RATE = ['Default', 'Entity', 'Customer_Rate']; @@ -139,7 +138,7 @@ final class CustomerController extends BaseApiController } $view = new View($form); - $view->getContext()->setGroups(self::GROUPS_FORM); + $view->getContext()->setGroups(self::GROUPS_ENTITY); return $this->viewHandler->handle($view); } @@ -166,7 +165,7 @@ final class CustomerController extends BaseApiController if (false === $form->isValid()) { $view = new View($form, Response::HTTP_OK); - $view->getContext()->setGroups(self::GROUPS_FORM); + $view->getContext()->setGroups(self::GROUPS_ENTITY); return $this->viewHandler->handle($view); } diff --git a/src/API/ProjectController.php b/src/API/ProjectController.php index 6d82a1b7..e64e6fc3 100644 --- a/src/API/ProjectController.php +++ b/src/API/ProjectController.php @@ -38,7 +38,6 @@ use Symfony\Component\Validator\Constraints; final class ProjectController extends BaseApiController { public const GROUPS_ENTITY = ['Default', 'Entity', 'Project', 'Project_Entity']; - public const GROUPS_FORM = ['Default', 'Entity', 'Project']; public const GROUPS_COLLECTION = ['Default', 'Collection', 'Project']; public const GROUPS_RATE = ['Default', 'Entity', 'Project_Rate']; @@ -194,7 +193,7 @@ final class ProjectController extends BaseApiController } $view = new View($form); - $view->getContext()->setGroups(self::GROUPS_FORM); + $view->getContext()->setGroups(self::GROUPS_ENTITY); return $this->viewHandler->handle($view); } @@ -223,7 +222,7 @@ final class ProjectController extends BaseApiController if (false === $form->isValid()) { $view = new View($form, Response::HTTP_OK); - $view->getContext()->setGroups(self::GROUPS_FORM); + $view->getContext()->setGroups(self::GROUPS_ENTITY); return $this->viewHandler->handle($view); } diff --git a/src/Command/ResetTestCommand.php b/src/Command/ResetTestCommand.php index 32d3df1a..99afb1ff 100644 --- a/src/Command/ResetTestCommand.php +++ b/src/Command/ResetTestCommand.php @@ -100,7 +100,7 @@ final class ResetTestCommand extends AbstractResetCommand $user2->setAvatar('https://www.gravatar.com/avatar/00000000000000000000000000000000?d=retro&f=y'); $user2->setEnabled(true); $user2->setRoles(['ROLE_USER']); - $user2->setUserIdentifier('john_user'); + $user2->setUserIdentifier(UserFixtures::USERNAME_USER); $user2->setEmail('john_user@example.com'); $token2 = new AccessToken($user2, UserFixtures::DEFAULT_API_TOKEN . '_user'); $token2->setName('Test fixture'); @@ -118,41 +118,41 @@ final class ResetTestCommand extends AbstractResetCommand $token3 = new AccessToken($user3, UserFixtures::DEFAULT_API_TOKEN . '_inactive'); $token3->setName('Test fixture'); - $user4 = new User(); - $user4->setPreferenceValue(UserPreference::HOURLY_RATE, 35); - $user4->setAlias('Tony Maier'); - $user4->setRegisteredAt(new \DateTime('2018-02-06 23:28:57')); - $user4->setTitle('Head of Development'); - $user4->setAvatar('https://en.gravatar.com/userimage/3533186/bf2163b1dd23f3107a028af0195624e9.jpeg'); - $user4->setEnabled(true); - $user4->setRoles(['ROLE_TEAMLEAD']); - $user4->setUserIdentifier('tony_teamlead'); - $user4->setEmail('tony_teamlead@example.com'); - $token4 = new AccessToken($user4, UserFixtures::DEFAULT_API_TOKEN . '_teamlead'); + $userTeamlead = new User(); + $userTeamlead->setPreferenceValue(UserPreference::HOURLY_RATE, 35); + $userTeamlead->setAlias('Tony Maier'); + $userTeamlead->setRegisteredAt(new \DateTime('2018-02-06 23:28:57')); + $userTeamlead->setTitle('Head of Development'); + $userTeamlead->setAvatar('https://en.gravatar.com/userimage/3533186/bf2163b1dd23f3107a028af0195624e9.jpeg'); + $userTeamlead->setEnabled(true); + $userTeamlead->setRoles(['ROLE_TEAMLEAD']); + $userTeamlead->setUserIdentifier(UserFixtures::USERNAME_TEAMLEAD); + $userTeamlead->setEmail('tony_teamlead@example.com'); + $token4 = new AccessToken($userTeamlead, UserFixtures::DEFAULT_API_TOKEN . '_teamlead'); $token4->setName('Test fixture'); - $user5 = new User(); - $user5->setPreferenceValue(UserPreference::HOURLY_RATE, 81); - $user5->setAlias('Anna Smith'); - $user5->setRegisteredAt(new \DateTime('2018-02-06 23:28:57')); - $user5->setTitle('Administrator'); - $user5->setEnabled(true); - $user5->setRoles(['ROLE_ADMIN']); - $user5->setUserIdentifier('anna_admin'); - $user5->setEmail('anna_admin@example.com'); - $token5 = new AccessToken($user5, UserFixtures::DEFAULT_API_TOKEN . '_admin'); + $userAdmin = new User(); + $userAdmin->setPreferenceValue(UserPreference::HOURLY_RATE, 81); + $userAdmin->setAlias('Anna Smith'); + $userAdmin->setRegisteredAt(new \DateTime('2018-02-06 23:28:57')); + $userAdmin->setTitle('Administrator'); + $userAdmin->setEnabled(true); + $userAdmin->setRoles(['ROLE_ADMIN']); + $userAdmin->setUserIdentifier(UserFixtures::USERNAME_ADMIN); + $userAdmin->setEmail('anna_admin@example.com'); + $token5 = new AccessToken($userAdmin, UserFixtures::DEFAULT_API_TOKEN . '_admin'); $token5->setName('Test fixture'); - $user6 = new User(); - $user6->setPreferenceValue(UserPreference::HOURLY_RATE, 46); - $user6->setRegisteredAt(new \DateTime('2018-02-06 23:28:57')); - $user6->setTitle('Super Administrator'); - $user6->setAvatar('/bundles/avanzuadmintheme/img/avatar.png'); - $user6->setEnabled(true); - $user6->setRoles(['ROLE_SUPER_ADMIN']); - $user6->setUserIdentifier('susan_super'); - $user6->setEmail('susan_super@example.com'); - $token6 = new AccessToken($user6, UserFixtures::DEFAULT_API_TOKEN . '_super'); + $userSuperAdmin = new User(); + $userSuperAdmin->setPreferenceValue(UserPreference::HOURLY_RATE, 46); + $userSuperAdmin->setRegisteredAt(new \DateTime('2018-02-06 23:28:57')); + $userSuperAdmin->setTitle('Super Administrator'); + $userSuperAdmin->setAvatar('/bundles/avanzuadmintheme/img/avatar.png'); + $userSuperAdmin->setEnabled(true); + $userSuperAdmin->setRoles(['ROLE_SUPER_ADMIN']); + $userSuperAdmin->setUserIdentifier(UserFixtures::USERNAME_SUPER_ADMIN); + $userSuperAdmin->setEmail('susan_super@example.com'); + $token6 = new AccessToken($userSuperAdmin, UserFixtures::DEFAULT_API_TOKEN . '_super'); $token6->setName('Test fixture'); $user7 = new User(); @@ -180,9 +180,9 @@ final class ResetTestCommand extends AbstractResetCommand [$user1, $token1], [$user2, $token2], [$user3, $token3], - [$user4, $token4], - [$user5, $token5], - [$user6, $token6], + [$userTeamlead, $token4], + [$userAdmin, $token5], + [$userSuperAdmin, $token6], [$user7, $token7], [$user8, $token8], ]; diff --git a/src/Constants.php b/src/Constants.php index 0649c4be..714102ad 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -17,11 +17,11 @@ final class Constants /** * The current release version */ - public const VERSION = '2.42.0'; + public const VERSION = '2.43.0'; /** * The current release: major * 10000 + minor * 100 + patch */ - public const VERSION_ID = 24200; + public const VERSION_ID = 24300; /** * The software name */ diff --git a/src/Entity/Customer.php b/src/Entity/Customer.php index ec192c52..effa5b69 100644 --- a/src/Entity/Customer.php +++ b/src/Entity/Customer.php @@ -96,7 +96,7 @@ class Customer implements EntityWithMetaFields, EntityWithBudget, CreatedAt #[Exporter\Expose(label: 'contact')] private ?string $contact = null; /** - * Unstructured address, better use the fields: address_line1-3, postcode, city, country + * Unstructured address, better use the fields: addressLine1-3, postcode, city, country */ #[ORM\Column(name: 'address', type: Types::TEXT, nullable: true)] #[Serializer\Expose] diff --git a/src/Form/API/ActivityApiEditForm.php b/src/Form/API/ActivityApiEditForm.php index 94a4ab2b..3e651c11 100644 --- a/src/Form/API/ActivityApiEditForm.php +++ b/src/Form/API/ActivityApiEditForm.php @@ -10,7 +10,22 @@ namespace App\Form\API; use App\Form\ActivityEditForm; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolver; -final class ActivityApiEditForm extends ActivityEditForm +final class ActivityApiEditForm extends AbstractType { + public function getParent(): string + { + return ActivityEditForm::class; + } + + public function configureOptions(OptionsResolver $resolver): void + { + // overwritten, so the docs show these fields + $resolver->setDefaults([ + 'include_budget' => true, + 'include_time' => true, + ]); + } } diff --git a/src/Form/API/CustomerApiEditForm.php b/src/Form/API/CustomerApiEditForm.php index a59ef71f..19739794 100644 --- a/src/Form/API/CustomerApiEditForm.php +++ b/src/Form/API/CustomerApiEditForm.php @@ -10,7 +10,22 @@ namespace App\Form\API; use App\Form\CustomerEditForm; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolver; -final class CustomerApiEditForm extends CustomerEditForm +final class CustomerApiEditForm extends AbstractType { + public function getParent(): string + { + return CustomerEditForm::class; + } + + public function configureOptions(OptionsResolver $resolver): void + { + // overwritten, so the docs show these fields + $resolver->setDefaults([ + 'include_budget' => true, + 'include_time' => true, + ]); + } } diff --git a/src/Form/API/ProjectApiEditForm.php b/src/Form/API/ProjectApiEditForm.php index 2f5d2ace..5b41c295 100644 --- a/src/Form/API/ProjectApiEditForm.php +++ b/src/Form/API/ProjectApiEditForm.php @@ -10,7 +10,22 @@ namespace App\Form\API; use App\Form\ProjectEditForm; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolver; -final class ProjectApiEditForm extends ProjectEditForm +final class ProjectApiEditForm extends AbstractType { + public function getParent(): string + { + return ProjectEditForm::class; + } + + public function configureOptions(OptionsResolver $resolver): void + { + // overwritten, so the docs show these fields + $resolver->setDefaults([ + 'include_budget' => true, + 'include_time' => true, + ]); + } } diff --git a/src/Form/CustomerEditForm.php b/src/Form/CustomerEditForm.php index e2b92260..f5469522 100644 --- a/src/Form/CustomerEditForm.php +++ b/src/Form/CustomerEditForm.php @@ -33,7 +33,7 @@ class CustomerEditForm extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { - $isNew = false; + $isNew = true; $hasAddress = false; if (isset($options['data'])) { @@ -54,6 +54,9 @@ class CustomerEditForm extends AbstractType ->add('number', TextType::class, [ 'label' => 'number', 'required' => false, + 'attr' => [ + 'maxlength' => 50, + ], ]) ->add('comment', TextareaType::class, [ 'label' => 'description', @@ -83,19 +86,19 @@ class CustomerEditForm extends AbstractType } $builder - ->add('address_line1', TextType::class, [ + ->add('addressLine1', TextType::class, [ 'label' => 'address', 'required' => false, ]) - ->add('address_line2', TextType::class, [ + ->add('addressLine2', TextType::class, [ 'label' => false, 'required' => false, ]) - ->add('address_line3', TextType::class, [ + ->add('addressLine3', TextType::class, [ 'label' => false, 'required' => false, ]) - ->add('postcode', TextType::class, [ + ->add('postCode', TextType::class, [ 'label' => 'postcode', 'required' => false, ]) diff --git a/src/Form/EntityFormTrait.php b/src/Form/EntityFormTrait.php index f7f31e7b..725db896 100644 --- a/src/Form/EntityFormTrait.php +++ b/src/Form/EntityFormTrait.php @@ -31,6 +31,9 @@ trait EntityFormTrait if ($showMoney) { $builder->add('budget', MoneyType::class, [ + 'documentation' => [ + 'description' => 'The money budget', + ], 'empty_data' => '0.00', 'label' => 'budget', 'required' => false, diff --git a/src/Form/ProjectEditForm.php b/src/Form/ProjectEditForm.php index 6d9ade3e..62f16ae8 100644 --- a/src/Form/ProjectEditForm.php +++ b/src/Form/ProjectEditForm.php @@ -29,7 +29,7 @@ class ProjectEditForm extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { $customer = null; - $isNew = false; + $isNew = true; $options['currency'] = null; $customerOptions = []; diff --git a/src/Form/Type/BudgetType.php b/src/Form/Type/BudgetType.php index 132a3c42..482c0d52 100644 --- a/src/Form/Type/BudgetType.php +++ b/src/Form/Type/BudgetType.php @@ -31,6 +31,9 @@ final class BudgetType extends AbstractType 'choices' => [ 'budgetType_month' => self::TYPE_MONTH, ], + 'documentation' => [ + 'description' => 'The type of budget. Only submit if you want to use a monthly budget.', + ], ]); } diff --git a/templates/customer/edit.html.twig b/templates/customer/edit.html.twig index 9f1844f9..30e3439d 100644 --- a/templates/customer/edit.html.twig +++ b/templates/customer/edit.html.twig @@ -96,22 +96,22 @@ {% endif %}
- {{ form_row(form.address_line1, {attr: {placeholder: 'address_row'|trans({'%row%': 1})}}) }} + {{ form_row(form.addressLine1, {attr: {placeholder: 'address_row'|trans({'%row%': 1})}}) }}
- {{ form_row(form.address_line2, {attr: {placeholder: 'address_row'|trans({'%row%': 2})}}) }} + {{ form_row(form.addressLine2, {attr: {placeholder: 'address_row'|trans({'%row%': 2})}}) }}
- {{ form_row(form.address_line3, {attr: {placeholder: 'address_row'|trans({'%row%': 3})}}) }} + {{ form_row(form.addressLine3, {attr: {placeholder: 'address_row'|trans({'%row%': 3})}}) }}
- {{ form_row(form.postcode) }} + {{ form_row(form.postCode) }}
{{ form_row(form.city) }} diff --git a/tests/API/APIControllerBaseTestCase.php b/tests/API/APIControllerBaseTestCase.php index cae77a01..31c2a049 100644 --- a/tests/API/APIControllerBaseTestCase.php +++ b/tests/API/APIControllerBaseTestCase.php @@ -504,7 +504,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase 'billable' => 'bool', 'color' => '@string', 'customer' => 'int', - 'number' => '@int', + 'number' => '@string', 'orderNumber' => '@string', 'globalActivities' => 'bool', 'comment' => '@string', @@ -521,7 +521,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase 'billable' => 'bool', 'color' => '@string', 'customer' => ['result' => 'object', 'type' => 'Customer'], - 'number' => '@int', + 'number' => '@string', 'orderNumber' => '@string', 'globalActivities' => 'bool', 'comment' => '@string', @@ -537,7 +537,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase 'visible' => 'bool', 'billable' => 'bool', 'customer' => 'int', - 'number' => '@int', + 'number' => '@string', 'orderNumber' => '@string', 'color' => '@string', 'metaFields' => ['result' => 'array', 'type' => 'ProjectMeta'], @@ -557,7 +557,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase 'visible' => 'bool', 'billable' => 'bool', 'customer' => 'int', - 'number' => '@int', + 'number' => '@string', 'color' => '@string', 'metaFields' => ['result' => 'array', 'type' => 'ProjectMeta'], 'parentTitle' => 'string', @@ -581,7 +581,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase 'visible' => 'bool', 'billable' => 'bool', 'project' => '@int', - 'number' => '@int', + 'number' => '@string', 'color' => '@string', 'comment' => '@string', ]; @@ -593,7 +593,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase 'visible' => 'bool', 'billable' => 'bool', 'project' => ['result' => 'object', 'type' => '@ProjectExpanded'], - 'number' => '@int', + 'number' => '@string', 'color' => '@string', 'comment' => '@string', ]; @@ -606,7 +606,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase 'visible' => 'bool', 'billable' => 'bool', 'project' => '@int', - 'number' => '@int', + 'number' => '@string', 'color' => '@string', 'metaFields' => ['result' => 'array', 'type' => 'ProjectMeta'], 'parentTitle' => '@string', @@ -622,7 +622,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase 'visible' => 'bool', 'billable' => 'bool', 'project' => '@int', - 'number' => '@int', + 'number' => '@string', 'color' => '@string', 'metaFields' => ['result' => 'array', 'type' => 'ProjectMeta'], 'parentTitle' => '@string', diff --git a/tests/API/ActivityControllerTest.php b/tests/API/ActivityControllerTest.php index 86351795..530b0a62 100644 --- a/tests/API/ActivityControllerTest.php +++ b/tests/API/ActivityControllerTest.php @@ -243,6 +243,24 @@ class ActivityControllerTest extends APIControllerBaseTestCase self::assertIsArray($result); self::assertApiResponseTypeStructure('ActivityEntity', $result); + + self::assertCount(14, array_keys($result)); + self::assertNull($result['parentTitle']); + self::assertNotEmpty($result['id']); + self::assertIsArray($result['teams']); + self::assertEquals([], $result['teams']); + self::assertIsArray($result['metaFields']); + self::assertEquals([], $result['metaFields']); + self::assertEquals('Test', $result['name']); + self::assertNull($result['project']); + self::assertEquals(1000, $result['budget']); + self::assertEquals(100000, $result['timeBudget']); + self::assertNull($result['budgetType']); + self::assertNull($result['number']); + self::assertEquals('Test comment', $result['comment']); + self::assertNull($result['color']); + self::assertTrue($result['visible']); + self::assertTrue($result['billable']); } public function testNotFound(): void @@ -256,9 +274,16 @@ class ActivityControllerTest extends APIControllerBaseTestCase $data = [ 'name' => 'foo', 'project' => 1, - 'visible' => true, 'budget' => '999', - 'timeBudget' => '7200', + 'timeBudget' => '10,25', + 'budgetType' => 'month', + 'number' => 'P-754', + 'comment' => 'Awesome activity since a short time', + 'invoiceText' => 'Some invoice text, pay slow please', + 'color' => '#000000', + 'visible' => true, + 'billable' => true, + 'teams' => [1], ]; $this->request($client, '/api/activities', 'POST', [], json_encode($data)); self::assertTrue($client->getResponse()->isSuccessful()); @@ -269,7 +294,22 @@ class ActivityControllerTest extends APIControllerBaseTestCase self::assertIsArray($result); self::assertApiResponseTypeStructure('ActivityEntity', $result); + self::assertEquals('Test', $result['parentTitle']); self::assertNotEmpty($result['id']); + self::assertIsArray($result['teams']); + self::assertEquals([['id' => 1, 'name' => 'Test team', 'color' => null]], $result['teams']); + self::assertIsArray($result['metaFields']); + self::assertEquals([], $result['metaFields']); + self::assertEquals('foo', $result['name']); + self::assertEquals(1, $result['project']); + self::assertEquals('999', $result['budget']); + self::assertEquals('36900', $result['timeBudget']); + self::assertEquals('month', $result['budgetType']); + self::assertEquals('P-754', $result['number']); + self::assertEquals('Awesome activity since a short time', $result['comment']); + self::assertEquals('#000000', $result['color']); + self::assertTrue($result['visible']); + self::assertTrue($result['billable']); } public function testPostActionWithLeastFields(): void diff --git a/tests/API/CustomerControllerTest.php b/tests/API/CustomerControllerTest.php index e66f9418..eb4e1689 100644 --- a/tests/API/CustomerControllerTest.php +++ b/tests/API/CustomerControllerTest.php @@ -228,6 +228,43 @@ class CustomerControllerTest extends APIControllerBaseTestCase self::assertIsArray($result); self::assertApiResponseTypeStructure('CustomerEntity', $result); + + self::assertCount(30, array_keys($result)); + self::assertNotEmpty($result['id']); + self::assertIsArray($result['teams']); + self::assertCount(1, $result['teams']); + self::assertIsArray($result['teams'][0]); + self::assertEquals('Testing customer 1 team', $result['teams'][0]['name']); + self::assertIsArray($result['metaFields']); + self::assertCount(1, $result['metaFields']); + self::assertEquals([['name' => 'foo', 'value' => 'bar']], $result['metaFields']); + self::assertEquals('Test', $result['name']); + self::assertEquals(1000, $result['budget']); + self::assertEquals(100000, $result['timeBudget']); + self::assertNull($result['budgetType']); + self::assertEquals('1', $result['number']); + self::assertEquals('Test comment', $result['comment']); + self::assertEquals('Test', $result['company']); + self::assertNull($result['vatId']); + self::assertEquals('Test', $result['contact']); + self::assertEquals('Test', $result['address']); + self::assertNull($result['addressLine1']); + self::assertNull($result['addressLine2']); + self::assertNull($result['addressLine3']); + self::assertNull($result['postCode']); + self::assertNull($result['city']); + self::assertEquals('DE', $result['country']); + self::assertEquals('EUR', $result['currency']); + self::assertEquals('111', $result['phone']); + self::assertEquals('222', $result['fax']); + self::assertEquals('333', $result['mobile']); + self::assertEquals('test@example.com', $result['email']); + self::assertNull($result['homepage']); + self::assertEquals('Europe/Berlin', $result['timezone']); + self::assertNull($result['buyerReference']); + self::assertNull($result['color']); + self::assertTrue($result['visible']); + self::assertTrue($result['billable']); } public function testNotFound(): void @@ -240,12 +277,33 @@ class CustomerControllerTest extends APIControllerBaseTestCase $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); $data = [ 'name' => 'foo', - 'visible' => true, + 'budget' => '999', + 'timeBudget' => '10,25', + 'budgetType' => 'month', + 'number' => 'C-754', + 'comment' => 'Awesome customer since a long time', + 'company' => 'IT Professional Comp.', + 'vatId' => 'DE0123456789', + 'contact' => 'Mr. John Doe', + 'addressLine1' => 'test address line 1', + 'addressLine2' => 'foo address line 2', + 'addressLine3' => 'bar address line 3', + 'postCode' => '12345', + 'city' => 'Acme Town', 'country' => 'DE', 'currency' => 'EUR', + 'phone' => '666667787778999909087', + 'fax' => '0987654321', + 'mobile' => '01234534567890', + 'email' => 'admin@example.com', + 'homepage' => 'https://www.example.com/', 'timezone' => 'Europe/Berlin', - 'budget' => '999', - 'timeBudget' => '7200', + 'invoiceText' => 'Some random text, pay fast please!', + 'buyerReference' => 'REF-0123456789', + 'color' => '#ff0000', + 'visible' => true, + 'billable' => true, + 'teams' => [1], ]; $this->request($client, '/api/customers', 'POST', [], json_encode($data)); self::assertTrue($client->getResponse()->isSuccessful()); @@ -257,6 +315,37 @@ class CustomerControllerTest extends APIControllerBaseTestCase self::assertIsArray($result); self::assertApiResponseTypeStructure('CustomerEntity', $result); self::assertNotEmpty($result['id']); + self::assertIsArray($result['teams']); + self::assertEquals([['id' => 1, 'name' => 'Test team', 'color' => null]], $result['teams']); + self::assertIsArray($result['metaFields']); + self::assertEquals([], $result['metaFields']); + self::assertEquals('foo', $result['name']); + self::assertEquals('999', $result['budget']); + self::assertEquals('36900', $result['timeBudget']); + self::assertEquals('month', $result['budgetType']); + self::assertEquals('C-754', $result['number']); + self::assertEquals('Awesome customer since a long time', $result['comment']); + self::assertEquals('IT Professional Comp.', $result['company']); + self::assertEquals('DE0123456789', $result['vatId']); + self::assertEquals('Mr. John Doe', $result['contact']); + self::assertNull($result['address']); + self::assertEquals('test address line 1', $result['addressLine1']); + self::assertEquals('foo address line 2', $result['addressLine2']); + self::assertEquals('bar address line 3', $result['addressLine3']); + self::assertEquals('12345', $result['postCode']); + self::assertEquals('Acme Town', $result['city']); + self::assertEquals('DE', $result['country']); + self::assertEquals('EUR', $result['currency']); + self::assertEquals('666667787778999909087', $result['phone']); + self::assertEquals('0987654321', $result['fax']); + self::assertEquals('01234534567890', $result['mobile']); + self::assertEquals('admin@example.com', $result['email']); + self::assertEquals('https://www.example.com/', $result['homepage']); + self::assertEquals('Europe/Berlin', $result['timezone']); + self::assertEquals('REF-0123456789', $result['buyerReference']); + self::assertEquals('#ff0000', $result['color']); + self::assertTrue($result['visible']); + self::assertTrue($result['billable']); } public function testPostActionWithLeastFields(): void diff --git a/tests/API/ProjectControllerTest.php b/tests/API/ProjectControllerTest.php index ab06547b..f9cc7e2a 100644 --- a/tests/API/ProjectControllerTest.php +++ b/tests/API/ProjectControllerTest.php @@ -311,28 +311,28 @@ class ProjectControllerTest extends APIControllerBaseTestCase self::assertIsArray($result); self::assertApiResponseTypeStructure('ProjectEntity', $result); - $expected = [ - 'parentTitle' => 'first one', - 'customer' => $customer->getId(), - 'id' => $project->getId(), - 'name' => 'first', - 'orderNumber' => null, - // make sure the timezone is properly applied in serializer (see #1858) - 'orderDate' => '2019-11-29', - 'start' => '2020-01-07', - 'end' => '2021-03-23', - 'comment' => null, - 'visible' => true, - 'budget' => 0.0, - 'timeBudget' => 0, - 'metaFields' => [], - 'teams' => [], - 'color' => null, - ]; - - foreach ($expected as $key => $value) { - self::assertEquals($value, $result[$key]); - } + self::assertCount(19, array_keys($result)); + self::assertEquals('first one', $result['parentTitle']); + self::assertEquals($project->getId(), $result['id']); + self::assertIsArray($result['teams']); + self::assertEquals([], $result['teams']); + self::assertIsArray($result['metaFields']); + self::assertEquals([], $result['metaFields']); + self::assertEquals('first', $result['name']); + self::assertEquals($customer->getId(), $result['customer']); + self::assertEquals('2019-11-29', $result['orderDate']); + self::assertEquals('2020-01-07', $result['start']); + self::assertEquals('2021-03-23', $result['end']); + self::assertEquals(0.0, $result['budget']); + self::assertEquals(0, $result['timeBudget']); + self::assertNull($result['budgetType']); + self::assertNull($result['orderNumber']); + self::assertNull($result['number']); + self::assertNull($result['comment']); + self::assertNull($result['color']); + self::assertTrue($result['globalActivities']); + self::assertTrue($result['billable']); + self::assertTrue($result['visible']); } public function testNotFound(): void @@ -350,8 +350,17 @@ class ProjectControllerTest extends APIControllerBaseTestCase 'start' => '2019-02-01', 'end' => '2020-02-08', 'budget' => '999', - 'timeBudget' => '7200', + 'timeBudget' => '10,25', + 'budgetType' => 'month', 'orderNumber' => '1234567890/WXYZ/SUBPROJECT/1234/CONTRACT/EMPLOYEE1', + 'number' => 'A-1234', + 'comment' => 'Awesome project since a short time', + 'invoiceText' => 'Some invoice text, pay now!', + 'color' => '#c0c0c0', + 'globalActivities' => true, + 'visible' => true, + 'billable' => true, + 'teams' => [1], ]; $this->request($client, '/api/projects', 'POST', [], json_encode($data)); self::assertTrue($client->getResponse()->isSuccessful()); @@ -362,14 +371,27 @@ class ProjectControllerTest extends APIControllerBaseTestCase self::assertIsArray($result); self::assertApiResponseTypeStructure('ProjectEntity', $result); + self::assertEquals('Test', $result['parentTitle']); self::assertNotEmpty($result['id']); + self::assertIsArray($result['teams']); + self::assertEquals([['id' => 1, 'name' => 'Test team', 'color' => null]], $result['teams']); + self::assertIsArray($result['metaFields']); + self::assertEquals([], $result['metaFields']); + self::assertEquals('foo', $result['name']); + self::assertEquals(1, $result['customer']); self::assertEquals('2018-04-17', $result['orderDate']); self::assertEquals('2019-02-01', $result['start']); self::assertEquals('2020-02-08', $result['end']); + self::assertEquals('999', $result['budget']); + self::assertEquals('36900', $result['timeBudget']); + self::assertEquals('month', $result['budgetType']); self::assertEquals('1234567890/WXYZ/SUBPROJECT/1234/CONTRACT/EMPLOYEE1', $result['orderNumber']); - self::assertFalse($result['globalActivities']); - self::assertFalse($result['billable']); - self::assertFalse($result['visible']); + self::assertEquals('A-1234', $result['number']); + self::assertEquals('Awesome project since a short time', $result['comment']); + self::assertEquals('#c0c0c0', $result['color']); + self::assertTrue($result['globalActivities']); + self::assertTrue($result['billable']); + self::assertTrue($result['visible']); } public function testPostActionWithOtherFields(): void