diff --git a/config/services.yaml b/config/services.yaml index 3c8adb96..6e00aee7 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -23,13 +23,14 @@ services: - '../src/API/Model/' - '../src/DependencyInjection/' - '../src/Entity/' + - '../src/Event/' - '../src/Form/Model/' + - '../src/Model/' - '../src/Repository/Loader/' - '../src/Repository/Paginator/' - '../src/Repository/Query/' - '../src/Repository/Result/' - - '../src/Event/' - - '../src/Model/' + - '../src/WorkingTime/Calculator/' - '../src/Kernel.php' - '../src/Constants.php' diff --git a/migrations/Version20240920105524.php b/migrations/Version20240920105524.php new file mode 100644 index 00000000..6bf7f7f9 --- /dev/null +++ b/migrations/Version20240920105524.php @@ -0,0 +1,57 @@ +connection->fetchFirstColumn(" +SELECT DISTINCT user_id +FROM kimai2_user_preferences AS kp +WHERE kp.value > 0 + AND kp.name IN ('work_monday', 'work_tuesday', 'work_wednesday', 'work_thursday', 'work_friday', 'work_saturday', 'work_sunday') + AND NOT EXISTS ( + SELECT 1 + FROM kimai2_user_preferences AS kp2 + WHERE kp2.user_id = kp.user_id + AND kp2.name = 'work_contract_type' +);"); + + foreach ($ids as $id) { + $this->addSql('INSERT INTO kimai2_user_preferences (`user_id`, `name`, `value`) VALUES (:id, :name, :value)', [ + 'id' => $id, + 'name' => 'work_contract_type', + 'value' => 'day', + ]); + } + } + + public function down(Schema $schema): void + { + $this->addSql("DELETE FROM kimai2_user_preferences WHERE `name` = 'work_contract_type'"); + } + + public function isTransactional(): bool + { + return true; + } +} diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index 0848a4b5..0af468b3 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -15,6 +15,7 @@ use App\Entity\UserPreference; use App\Event\PrepareUserEvent; use App\Form\AccessTokenForm; use App\Form\Model\TotpActivation; +use App\Form\Model\UserContractModel; use App\Form\UserApiPasswordType; use App\Form\UserContractType; use App\Form\UserEditType; @@ -267,7 +268,7 @@ final class ProfileController extends AbstractController #[IsGranted('contract', 'profile')] public function contractAction(User $profile, Request $request, UserRepository $userRepository): Response { - $form = $this->createForm(UserContractType::class, $profile, [ + $form = $this->createForm(UserContractType::class, new UserContractModel($profile), [ 'action' => $this->generateUrl('user_profile_contract', ['username' => $profile->getUserIdentifier()]), 'method' => 'POST', ]); diff --git a/src/Entity/User.php b/src/Entity/User.php index e911748c..190a0355 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -12,6 +12,7 @@ namespace App\Entity; use App\Export\Annotation as Exporter; use App\Utils\StringHelper; use App\Validator\Constraints as Constraints; +use App\WorkingTime\Mode\WorkingTimeModeNone; use DateTime; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; @@ -1232,36 +1233,57 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas return new TotpConfiguration($this->totpSecret, TotpConfiguration::ALGORITHM_SHA1, 30, 6); } + /** + * @deprecated since 2.22.0 + */ public function getWorkHoursMonday(): int { return (int) $this->getPreferenceValue(UserPreference::WORK_HOURS_MONDAY, 0); } + /** + * @deprecated since 2.22.0 + */ public function getWorkHoursTuesday(): int { return (int) $this->getPreferenceValue(UserPreference::WORK_HOURS_TUESDAY, 0); } + /** + * @deprecated since 2.22.0 + */ public function getWorkHoursWednesday(): int { return (int) $this->getPreferenceValue(UserPreference::WORK_HOURS_WEDNESDAY, 0); } + /** + * @deprecated since 2.22.0 + */ public function getWorkHoursThursday(): int { return (int) $this->getPreferenceValue(UserPreference::WORK_HOURS_THURSDAY, 0); } + /** + * @deprecated since 2.22.0 + */ public function getWorkHoursFriday(): int { return (int) $this->getPreferenceValue(UserPreference::WORK_HOURS_FRIDAY, 0); } + /** + * @deprecated since 2.22.0 + */ public function getWorkHoursSaturday(): int { return (int) $this->getPreferenceValue(UserPreference::WORK_HOURS_SATURDAY, 0); } + /** + * @deprecated since 2.22.0 + */ public function getWorkHoursSunday(): int { return (int) $this->getPreferenceValue(UserPreference::WORK_HOURS_SUNDAY, 0); @@ -1302,39 +1324,60 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas return $this->getFormattedHoliday(is_numeric($holidays) ? $holidays : 0.0); } - public function setWorkHoursMonday(int $seconds): void + /** + * @deprecated since 2.22.0 + */ + public function setWorkHoursMonday(?int $seconds): void { - $this->setPreferenceValue(UserPreference::WORK_HOURS_MONDAY, $seconds); + $this->setPreferenceValue(UserPreference::WORK_HOURS_MONDAY, $seconds ?? 0); } - public function setWorkHoursTuesday(int $seconds): void + /** + * @deprecated since 2.22.0 + */ + public function setWorkHoursTuesday(?int $seconds): void { - $this->setPreferenceValue(UserPreference::WORK_HOURS_TUESDAY, $seconds); + $this->setPreferenceValue(UserPreference::WORK_HOURS_TUESDAY, $seconds ?? 0); } - public function setWorkHoursWednesday(int $seconds): void + /** + * @deprecated since 2.22.0 + */ + public function setWorkHoursWednesday(?int $seconds): void { - $this->setPreferenceValue(UserPreference::WORK_HOURS_WEDNESDAY, $seconds); + $this->setPreferenceValue(UserPreference::WORK_HOURS_WEDNESDAY, $seconds ?? 0); } - public function setWorkHoursThursday(int $seconds): void + /** + * @deprecated since 2.22.0 + */ + public function setWorkHoursThursday(?int $seconds): void { - $this->setPreferenceValue(UserPreference::WORK_HOURS_THURSDAY, $seconds); + $this->setPreferenceValue(UserPreference::WORK_HOURS_THURSDAY, $seconds ?? 0); } - public function setWorkHoursFriday(int $seconds): void + /** + * @deprecated since 2.22.0 + */ + public function setWorkHoursFriday(?int $seconds): void { - $this->setPreferenceValue(UserPreference::WORK_HOURS_FRIDAY, $seconds); + $this->setPreferenceValue(UserPreference::WORK_HOURS_FRIDAY, $seconds ?? 0); } - public function setWorkHoursSaturday(int $seconds): void + /** + * @deprecated since 2.22.0 + */ + public function setWorkHoursSaturday(?int $seconds): void { - $this->setPreferenceValue(UserPreference::WORK_HOURS_SATURDAY, $seconds); + $this->setPreferenceValue(UserPreference::WORK_HOURS_SATURDAY, $seconds ?? 0); } - public function setWorkHoursSunday(int $seconds): void + /** + * @deprecated since 2.22.0 + */ + public function setWorkHoursSunday(?int $seconds): void { - $this->setPreferenceValue(UserPreference::WORK_HOURS_SUNDAY, $seconds); + $this->setPreferenceValue(UserPreference::WORK_HOURS_SUNDAY, $seconds ?? 0); } public function setPublicHolidayGroup(null|string $group = null): void @@ -1361,6 +1404,9 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas return (float) number_format((round($holidays * 2) / 2), 1); } + /** + * @deprecated since 2.22.0 + */ public function hasContractSettings(): bool { return $this->hasWorkHourConfiguration() || $this->getHolidaysPerYear() !== 0.0; @@ -1368,15 +1414,12 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas public function hasWorkHourConfiguration(): bool { - return $this->getWorkHoursMonday() !== 0 || - $this->getWorkHoursTuesday() !== 0 || - $this->getWorkHoursWednesday() !== 0 || - $this->getWorkHoursThursday() !== 0 || - $this->getWorkHoursFriday() !== 0 || - $this->getWorkHoursSaturday() !== 0 || - $this->getWorkHoursSunday() !== 0; + return $this->getWorkContractMode() !== WorkingTimeModeNone::ID; } + /** + * @deprecated since 2.22.0 + */ public function getWorkHoursForDay(\DateTimeInterface $dateTime): int { return match ($dateTime->format('N')) { @@ -1391,6 +1434,9 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas }; } + /** + * @deprecated since 2.22.0 + */ public function isWorkDay(\DateTimeInterface $dateTime): bool { return $this->getWorkHoursForDay($dateTime) > 0; @@ -1410,4 +1456,14 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas { $this->supervisor = $supervisor; } + + public function getWorkContractMode(): string + { + return (string) $this->getPreferenceValue(UserPreference::WORK_CONTRACT_TYPE, WorkingTimeModeNone::ID); + } + + public function setWorkContractMode(string $mode): void + { + $this->setPreferenceValue(UserPreference::WORK_CONTRACT_TYPE, $mode); + } } diff --git a/src/Entity/UserPreference.php b/src/Entity/UserPreference.php index 9cb55ad9..dfc666e5 100644 --- a/src/Entity/UserPreference.php +++ b/src/Entity/UserPreference.php @@ -10,6 +10,7 @@ namespace App\Entity; use App\Form\Type\YesNoType; +use App\WorkingTime\Calculator\WorkingTimeCalculatorDay; use Doctrine\ORM\Mapping as ORM; use JMS\Serializer\Annotation as Serializer; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; @@ -32,16 +33,24 @@ class UserPreference public const LOCALE = 'locale'; public const TIMEZONE = 'timezone'; public const FIRST_WEEKDAY = 'first_weekday'; - public const WORK_HOURS_MONDAY = 'work_monday'; - public const WORK_HOURS_TUESDAY = 'work_tuesday'; - public const WORK_HOURS_WEDNESDAY = 'work_wednesday'; - public const WORK_HOURS_THURSDAY = 'work_thursday'; - public const WORK_HOURS_FRIDAY = 'work_friday'; - public const WORK_HOURS_SATURDAY = 'work_saturday'; - public const WORK_HOURS_SUNDAY = 'work_sunday'; + /** @deprecated since 2.22*/ + public const WORK_HOURS_MONDAY = WorkingTimeCalculatorDay::WORK_HOURS_MONDAY; + /** @deprecated since 2.22*/ + public const WORK_HOURS_TUESDAY = WorkingTimeCalculatorDay::WORK_HOURS_TUESDAY; + /** @deprecated since 2.22*/ + public const WORK_HOURS_WEDNESDAY = WorkingTimeCalculatorDay::WORK_HOURS_WEDNESDAY; + /** @deprecated since 2.22*/ + public const WORK_HOURS_THURSDAY = WorkingTimeCalculatorDay::WORK_HOURS_THURSDAY; + /** @deprecated since 2.22*/ + public const WORK_HOURS_FRIDAY = WorkingTimeCalculatorDay::WORK_HOURS_FRIDAY; + /** @deprecated since 2.22*/ + public const WORK_HOURS_SATURDAY = WorkingTimeCalculatorDay::WORK_HOURS_SATURDAY; + /** @deprecated since 2.22*/ + public const WORK_HOURS_SUNDAY = WorkingTimeCalculatorDay::WORK_HOURS_SUNDAY; public const WORK_STARTING_DAY = 'work_start_day'; public const PUBLIC_HOLIDAY_GROUP = 'public_holiday_group'; public const HOLIDAYS_PER_YEAR = 'holidays'; + public const WORK_CONTRACT_TYPE = 'work_contract_type'; #[ORM\Id] #[ORM\GeneratedValue] diff --git a/src/Form/Model/UserContractModel.php b/src/Form/Model/UserContractModel.php new file mode 100644 index 00000000..5f354351 --- /dev/null +++ b/src/Form/Model/UserContractModel.php @@ -0,0 +1,52 @@ +user, $method)) { + $this->user->$method($value); + + return; + } + + if (!\is_scalar($value) && $value !== null) { + throw new \InvalidArgumentException('Invalid value passed'); + } + + $this->user->setPreferenceValue($name, $value); + } + + public function __get(string $name): mixed + { + $method = 'get' . ucfirst($name); + + if (method_exists($this->user, $method)) { + return $this->user->$method(); + } + + return $this->user->getPreferenceValue($name); + } +} diff --git a/src/Form/UserContractType.php b/src/Form/UserContractType.php index 4ae227f4..8151634e 100644 --- a/src/Form/UserContractType.php +++ b/src/Form/UserContractType.php @@ -9,42 +9,56 @@ namespace App\Form; -use App\Entity\User; -use App\Form\Type\DurationType; +use App\Form\Model\UserContractModel; +use App\WorkingTime\Mode\WorkingTimeMode; +use App\WorkingTime\Mode\WorkingTimeModeFactory; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; /** - * @extends AbstractType + * @extends AbstractType */ final class UserContractType extends AbstractType { + public function __construct(private readonly WorkingTimeModeFactory $contractModeService) + { + } + + public function buildView(FormView $view, FormInterface $form, array $options): void + { + $view->vars['workContractModes'] = $this->contractModeService->getAll(); + } + public function buildForm(FormBuilderInterface $builder, array $options): void { - $dayOptions = [ - 'translation_domain' => 'system-configuration', - 'constraints' => [ - new GreaterThanOrEqual(0) - ], - ]; + $sorted = $this->contractModeService->getAll(); - $builder - ->add('workHoursMonday', DurationType::class, array_merge(['label' => 'Monday'], $dayOptions)) - ->add('workHoursTuesday', DurationType::class, array_merge(['label' => 'Tuesday'], $dayOptions)) - ->add('workHoursWednesday', DurationType::class, array_merge(['label' => 'Wednesday'], $dayOptions)) - ->add('workHoursThursday', DurationType::class, array_merge(['label' => 'Thursday'], $dayOptions)) - ->add('workHoursFriday', DurationType::class, array_merge(['label' => 'Friday'], $dayOptions)) - ->add('workHoursSaturday', DurationType::class, array_merge(['label' => 'Saturday'], $dayOptions)) - ->add('workHoursSunday', DurationType::class, array_merge(['label' => 'Sunday'], $dayOptions)) - ; + usort($sorted, function (WorkingTimeMode $a, WorkingTimeMode $b) { + return $a->getOrder() <=> $b->getOrder(); + }); + + $modes = []; + foreach ($sorted as $mode) { + $modes[$mode->getName()] = $mode->getId(); + } + + if (\count($modes) > 1) { + $builder->add('workContractMode', ChoiceType::class, ['label' => 'work_hours_mode', 'choices' => $modes]); + } + + foreach ($modes as $mode) { + $this->contractModeService->getMode($mode)->buildForm($builder, $options); // @phpstan-ignore-line + } } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - 'data_class' => User::class, + 'data_class' => UserContractModel::class, 'csrf_protection' => true, 'csrf_field_name' => '_token', 'csrf_token_id' => 'edit_user_contract', diff --git a/src/WorkingTime/Calculator/WorkingTimeCalculator.php b/src/WorkingTime/Calculator/WorkingTimeCalculator.php new file mode 100644 index 00000000..729aa1c3 --- /dev/null +++ b/src/WorkingTime/Calculator/WorkingTimeCalculator.php @@ -0,0 +1,20 @@ +format('N')) { + '1' => $this->user->getPreferenceValue(self::WORK_HOURS_MONDAY, 0), + '2' => $this->user->getPreferenceValue(self::WORK_HOURS_TUESDAY, 0), + '3' => $this->user->getPreferenceValue(self::WORK_HOURS_WEDNESDAY, 0), + '4' => $this->user->getPreferenceValue(self::WORK_HOURS_THURSDAY, 0), + '5' => $this->user->getPreferenceValue(self::WORK_HOURS_FRIDAY, 0), + '6' => $this->user->getPreferenceValue(self::WORK_HOURS_SATURDAY, 0), + '7' => $this->user->getPreferenceValue(self::WORK_HOURS_SUNDAY, 0), + default => throw new \Exception('Unknown day: ' . $dateTime->format('Y-m-d')) + }; + } + + public function isWorkDay(\DateTimeInterface $dateTime): bool + { + return $this->getWorkHoursForDay($dateTime) > 0; + } +} diff --git a/src/WorkingTime/Calculator/WorkingTimeCalculatorNone.php b/src/WorkingTime/Calculator/WorkingTimeCalculatorNone.php new file mode 100644 index 00000000..00ba9208 --- /dev/null +++ b/src/WorkingTime/Calculator/WorkingTimeCalculatorNone.php @@ -0,0 +1,24 @@ + + */ + public function getOrder(): int; + + /** + * Translation key for the name of this mode. + */ + public function getName(): string; + + /** + * @param FormBuilderInterface $builder + * @param array $options + */ + public function buildForm(FormBuilderInterface $builder, array $options): void; + + public function getCalculator(User $user): WorkingTimeCalculator; + + /** + * @return array + */ + public function getFormFields(): array; +} diff --git a/src/WorkingTime/Mode/WorkingTimeModeDay.php b/src/WorkingTime/Mode/WorkingTimeModeDay.php new file mode 100644 index 00000000..3627d025 --- /dev/null +++ b/src/WorkingTime/Mode/WorkingTimeModeDay.php @@ -0,0 +1,77 @@ + + */ + private array $fields = [ + 'workHoursMonday' => 'Monday', + 'workHoursTuesday' => 'Tuesday', + 'workHoursWednesday' => 'Wednesday', + 'workHoursThursday' => 'Thursday', + 'workHoursFriday' => 'Friday', + 'workHoursSaturday' => 'Saturday', + 'workHoursSunday' => 'Sunday', + ]; + + public function getId(): string + { + return self::ID; + } + + public function getOrder(): int + { + return 10; + } + + public function getName(): string + { + return 'hours_per_day'; + } + + public function getFormFields(): array + { + return array_keys($this->fields); + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $durationOptions = [ + 'required' => false, + 'translation_domain' => 'system-configuration', + 'constraints' => [ + new GreaterThanOrEqual(0), + new LessThanOrEqual(86400), + ], + ]; + + foreach ($this->fields as $field => $label) { + $builder->add($field, DurationType::class, array_merge(['label' => $label], $durationOptions)); + } + } + + public function getCalculator(User $user): WorkingTimeCalculator + { + return new WorkingTimeCalculatorDay($user); + } +} diff --git a/src/WorkingTime/Mode/WorkingTimeModeFactory.php b/src/WorkingTime/Mode/WorkingTimeModeFactory.php new file mode 100644 index 00000000..71336498 --- /dev/null +++ b/src/WorkingTime/Mode/WorkingTimeModeFactory.php @@ -0,0 +1,55 @@ + $modes + */ + public function __construct( + #[TaggedIterator(WorkingTimeMode::class)] + private readonly iterable $modes + ) + { + } + + /** + * @return WorkingTimeMode[] + */ + public function getAll(): array + { + $modes = []; + foreach ($this->modes as $mode) { + $modes[] = $mode; + } + + return $modes; + } + + public function getModeForUser(User $user): WorkingTimeMode + { + return $this->getMode($user->getWorkContractMode()); + } + + public function getMode(string $contractMode): WorkingTimeMode + { + foreach ($this->modes as $mode) { + if ($mode->getId() === $contractMode) { + return $mode; + } + } + + throw new \InvalidArgumentException('Unknown working contract mode: ' . $contractMode); + } +} diff --git a/src/WorkingTime/Mode/WorkingTimeModeNone.php b/src/WorkingTime/Mode/WorkingTimeModeNone.php new file mode 100644 index 00000000..c3c5c6a1 --- /dev/null +++ b/src/WorkingTime/Mode/WorkingTimeModeNone.php @@ -0,0 +1,50 @@ +contractModeService->getModeForUser($user); + } + public function getYearSummary(Year $year, \DateTimeInterface $until): YearPerUserSummary { $yearPerUserSummary = new YearPerUserSummary($year); @@ -94,6 +102,7 @@ final class WorkingTimeService $stats = null; $firstDay = $user->getWorkStartingDay(); + $calculator = $this->getContractMode($user)->getCalculator($user); foreach ($year->getMonths() as $month) { foreach ($month->getDays() as $day) { @@ -111,7 +120,7 @@ final class WorkingTimeService $result = new WorkingTime($user, $dayDate); if ($firstDay === null || $firstDay <= $dayDate) { - $result->setExpectedTime($user->getWorkHoursForDay($dayDate)); + $result->setExpectedTime($calculator->getWorkHoursForDay($dayDate)); } if (\array_key_exists($key, $stats)) { diff --git a/templates/user/contract.html.twig b/templates/user/contract.html.twig index 1956b8a0..e991347f 100644 --- a/templates/user/contract.html.twig +++ b/templates/user/contract.html.twig @@ -2,20 +2,39 @@ {% block form_content %} + + {% form_theme form 'form/horizontal.html.twig' %} {{ form_row(form._token) }} -
- {{ 'work_times_should'|trans }} - {{ form_row(form.workHoursMonday) }} - {{ form_row(form.workHoursTuesday) }} - {{ form_row(form.workHoursWednesday) }} - {{ form_row(form.workHoursThursday) }} - {{ form_row(form.workHoursFriday) }} - {{ form_row(form.workHoursSaturday) }} - {{ form_row(form.workHoursSunday) }} -
+ {% if form.workContractMode is defined %} +
+ {{ 'work_times_should'|trans }} + {{ form_row(form.workContractMode, {attr:{'onchange': 'toggleWorkingContractModeForm(this.value)'}}) }} +
+ {% endif %} + + {% for mode in form.vars.workContractModes %} +
+ {% for field in mode.getFormFields() %} + {{ form_row(form.children[field]) }} + {% endfor %} +
+ {% endfor %}
{{- form_rest(form) -}} diff --git a/tests/Controller/ProfileControllerTest.php b/tests/Controller/ProfileControllerTest.php index 9a5c3f1d..91b5ce4e 100644 --- a/tests/Controller/ProfileControllerTest.php +++ b/tests/Controller/ProfileControllerTest.php @@ -14,6 +14,7 @@ use App\Entity\User; use App\Entity\UserPreference; use App\Tests\DataFixtures\TeamFixtures; use App\Tests\DataFixtures\TimesheetFixtures; +use App\WorkingTime\Mode\WorkingTimeModeDay; use Symfony\Component\DomCrawler\Field\ChoiceFormField; use Symfony\Component\HttpKernel\HttpKernelBrowser; use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; @@ -552,14 +553,15 @@ class ProfileControllerTest extends ControllerBaseTest $this->assertTrue($client->getResponse()->isSuccessful()); $user = $this->getUserByRole(User::ROLE_USER); + $calculator = (new WorkingTimeModeDay())->getCalculator($user); - $this->assertEquals(0, $user->getWorkHoursMonday()); - $this->assertEquals(0, $user->getWorkHoursTuesday()); - $this->assertEquals(0, $user->getWorkHoursWednesday()); - $this->assertEquals(0, $user->getWorkHoursThursday()); - $this->assertEquals(0, $user->getWorkHoursFriday()); - $this->assertEquals(0, $user->getWorkHoursSaturday()); - $this->assertEquals(0, $user->getWorkHoursSunday()); + $this->assertEquals(0, $calculator->getWorkHoursForDay(new \DateTime('monday this week'))); + $this->assertEquals(0, $calculator->getWorkHoursForDay(new \DateTime('tuesday this week'))); + $this->assertEquals(0, $calculator->getWorkHoursForDay(new \DateTime('wednesday this week'))); + $this->assertEquals(0, $calculator->getWorkHoursForDay(new \DateTime('thursday this week'))); + $this->assertEquals(0, $calculator->getWorkHoursForDay(new \DateTime('friday this week'))); + $this->assertEquals(0, $calculator->getWorkHoursForDay(new \DateTime('saturday this week'))); + $this->assertEquals(0, $calculator->getWorkHoursForDay(new \DateTime('sunday this week'))); $form = $client->getCrawler()->filter('form[name=user_contract]')->form(); @@ -580,13 +582,14 @@ class ProfileControllerTest extends ControllerBaseTest $this->assertHasFlashSuccess($client); $user = $this->getUserByRole(User::ROLE_USER); + $calculator = (new WorkingTimeModeDay())->getCalculator($user); - $this->assertEquals(3600, $user->getWorkHoursMonday()); - $this->assertEquals(7200, $user->getWorkHoursTuesday()); - $this->assertEquals(10800, $user->getWorkHoursWednesday()); - $this->assertEquals(16200, $user->getWorkHoursThursday()); - $this->assertEquals(18720, $user->getWorkHoursFriday()); - $this->assertEquals(25140, $user->getWorkHoursSaturday()); - $this->assertEquals(60, $user->getWorkHoursSunday()); + $this->assertEquals(3600, $calculator->getWorkHoursForDay(new \DateTime('monday this week'))); + $this->assertEquals(7200, $calculator->getWorkHoursForDay(new \DateTime('tuesday this week'))); + $this->assertEquals(10800, $calculator->getWorkHoursForDay(new \DateTime('wednesday this week'))); + $this->assertEquals(16200, $calculator->getWorkHoursForDay(new \DateTime('thursday this week'))); + $this->assertEquals(18720, $calculator->getWorkHoursForDay(new \DateTime('friday this week'))); + $this->assertEquals(25140, $calculator->getWorkHoursForDay(new \DateTime('saturday this week'))); + $this->assertEquals(60, $calculator->getWorkHoursForDay(new \DateTime('sunday this week'))); } } diff --git a/tests/Entity/UserTest.php b/tests/Entity/UserTest.php index 1a249f86..b8b328c6 100644 --- a/tests/Entity/UserTest.php +++ b/tests/Entity/UserTest.php @@ -17,6 +17,7 @@ use App\Entity\UserPreference; use App\Export\Spreadsheet\ColumnDefinition; use App\Export\Spreadsheet\Extractor\AnnotationExtractor; use App\Tests\Security\TestUserEntity; +use App\WorkingTime\Mode\WorkingTimeModeDay; use Doctrine\Common\Collections\ArrayCollection; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\User\EquatableInterface; @@ -74,13 +75,6 @@ class UserTest extends TestCase $user->setAccountNumber('A-058375'); self::assertEquals('A-058375', $user->getAccountNumber()); - self::assertEquals(0, $user->getWorkHoursMonday()); - self::assertEquals(0, $user->getWorkHoursTuesday()); - self::assertEquals(0, $user->getWorkHoursWednesday()); - self::assertEquals(0, $user->getWorkHoursThursday()); - self::assertEquals(0, $user->getWorkHoursFriday()); - self::assertEquals(0, $user->getWorkHoursSaturday()); - self::assertEquals(0, $user->getWorkHoursSunday()); self::assertEquals(0, $user->getHolidaysPerYear()); self::assertFalse($user->hasWorkHourConfiguration()); self::assertNull($user->getPublicHolidayGroup()); @@ -88,10 +82,23 @@ class UserTest extends TestCase self::assertNull($user->getSupervisor()); } + /** + * @deprecated + * @group legacy + */ public function testWorkContract(): void { $user = new User(); + self::assertEquals(0, $user->getWorkHoursMonday()); + self::assertEquals(0, $user->getWorkHoursTuesday()); + self::assertEquals(0, $user->getWorkHoursWednesday()); + self::assertEquals(0, $user->getWorkHoursThursday()); + self::assertEquals(0, $user->getWorkHoursFriday()); + self::assertEquals(0, $user->getWorkHoursSaturday()); + self::assertEquals(0, $user->getWorkHoursSunday()); + self::assertFalse($user->hasWorkHourConfiguration()); + $monday = new \DateTime('2023-05-08 12:00:00', new \DateTimeZone('Europe/Berlin')); $tuesday = new \DateTime('2023-05-09 12:00:00', new \DateTimeZone('Europe/Berlin')); $wednesday = new \DateTime('2023-05-10 12:00:00', new \DateTimeZone('Europe/Berlin')); @@ -108,6 +115,8 @@ class UserTest extends TestCase self::assertFalse($user->isWorkDay($saturday)); self::assertFalse($user->isWorkDay($sunday)); + $user->setWorkContractMode(WorkingTimeModeDay::ID); + $user->setWorkHoursMonday(7200); self::assertTrue($user->hasWorkHourConfiguration()); $user->setWorkHoursTuesday(7300); diff --git a/tests/WorkingTime/Calculator/WorkingTimeCalculatorDayTest.php b/tests/WorkingTime/Calculator/WorkingTimeCalculatorDayTest.php new file mode 100644 index 00000000..c28056bd --- /dev/null +++ b/tests/WorkingTime/Calculator/WorkingTimeCalculatorDayTest.php @@ -0,0 +1,58 @@ +modify('+1 day'); + $wednesday = $tuesday->modify('+1 day'); + $thursday = $wednesday->modify('+1 day'); + $friday = $thursday->modify('+1 day'); + $saturday = $friday->modify('+1 day'); + $sunday = $saturday->modify('+1 day'); + + $user = new User(); + $user->setPreferenceValue(WorkingTimeCalculatorDay::WORK_HOURS_MONDAY, 0); + $user->setPreferenceValue(WorkingTimeCalculatorDay::WORK_HOURS_TUESDAY, 3600); + $user->setPreferenceValue(WorkingTimeCalculatorDay::WORK_HOURS_WEDNESDAY, 0); + $user->setPreferenceValue(WorkingTimeCalculatorDay::WORK_HOURS_THURSDAY, 7200); + $user->setPreferenceValue(WorkingTimeCalculatorDay::WORK_HOURS_FRIDAY, 0); + $user->setPreferenceValue(WorkingTimeCalculatorDay::WORK_HOURS_SATURDAY, 1800); + $user->setPreferenceValue(WorkingTimeCalculatorDay::WORK_HOURS_SUNDAY, 9000); + + $sut = new WorkingTimeCalculatorDay($user); + + self::assertFalse($sut->isWorkDay($monday)); + self::assertTrue($sut->isWorkDay($tuesday)); + self::assertFalse($sut->isWorkDay($wednesday)); + self::assertTrue($sut->isWorkDay($thursday)); + self::assertFalse($sut->isWorkDay($friday)); + self::assertTrue($sut->isWorkDay($saturday)); + self::assertTrue($sut->isWorkDay($sunday)); + + self::assertEquals(0, $sut->getWorkHoursForDay($monday)); + self::assertEquals(3600, $sut->getWorkHoursForDay($tuesday)); + self::assertEquals(0, $sut->getWorkHoursForDay($wednesday)); + self::assertEquals(7200, $sut->getWorkHoursForDay($thursday)); + self::assertEquals(0, $sut->getWorkHoursForDay($friday)); + self::assertEquals(1800, $sut->getWorkHoursForDay($saturday)); + self::assertEquals(9000, $sut->getWorkHoursForDay($sunday)); + } +} diff --git a/tests/WorkingTime/Calculator/WorkingTimeCalculatorNoneTest.php b/tests/WorkingTime/Calculator/WorkingTimeCalculatorNoneTest.php new file mode 100644 index 00000000..88545e50 --- /dev/null +++ b/tests/WorkingTime/Calculator/WorkingTimeCalculatorNoneTest.php @@ -0,0 +1,27 @@ +isWorkDay($date)); + self::assertEquals(0, $sut->getWorkHoursForDay($date)); + } +} diff --git a/tests/WorkingTime/Mode/WorkingTimeModeDayTest.php b/tests/WorkingTime/Mode/WorkingTimeModeDayTest.php new file mode 100644 index 00000000..c7c25ba2 --- /dev/null +++ b/tests/WorkingTime/Mode/WorkingTimeModeDayTest.php @@ -0,0 +1,34 @@ +setWorkContractMode('day'); + $sut = new WorkingTimeModeDay(); + $this->assertEquals('day', $sut->getId()); + $this->assertEquals(10, $sut->getOrder()); + $this->assertEquals('hours_per_day', $sut->getName()); + $this->assertInstanceOf(WorkingTimeCalculatorDay::class, $sut->getCalculator($user)); + $fields = $sut->getFormFields(); + $this->assertCount(7, $fields); + } +} diff --git a/tests/WorkingTime/Mode/WorkingTimeModeFactoryTest.php b/tests/WorkingTime/Mode/WorkingTimeModeFactoryTest.php new file mode 100644 index 00000000..5214b852 --- /dev/null +++ b/tests/WorkingTime/Mode/WorkingTimeModeFactoryTest.php @@ -0,0 +1,46 @@ +assertEquals($modes, $sut->getAll()); + $this->assertSame($none, $sut->getMode('none')); + $this->assertSame($day, $sut->getMode('day')); + + $user = new User(); + $user->setWorkContractMode('day'); + $this->assertSame($day, $sut->getModeForUser($user)); + } + + public function testException(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Unknown working contract mode: foo'); + + $sut = new WorkingTimeModeFactory([]); + $sut->getMode('foo'); + } +} diff --git a/tests/WorkingTime/Mode/WorkingTimeModeNoneTest.php b/tests/WorkingTime/Mode/WorkingTimeModeNoneTest.php new file mode 100644 index 00000000..5c2e9a5b --- /dev/null +++ b/tests/WorkingTime/Mode/WorkingTimeModeNoneTest.php @@ -0,0 +1,30 @@ +assertEquals('none', $sut->getId()); + $this->assertEquals(0, $sut->getOrder()); + $this->assertEquals('', $sut->getName()); + $this->assertInstanceOf(WorkingTimeCalculatorNone::class, $sut->getCalculator(new User())); + } +}