Support for changeable work contract types (#5069)
This commit is contained in:
@@ -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',
|
||||
]);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
52
src/Form/Model/UserContractModel.php
Normal file
52
src/Form/Model/UserContractModel.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Form\Model;
|
||||
|
||||
use App\Entity\User;
|
||||
|
||||
final class UserContractModel
|
||||
{
|
||||
public function __construct(private readonly User $user)
|
||||
{
|
||||
}
|
||||
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __set(string $name, mixed $value): void
|
||||
{
|
||||
$method = 'set' . ucfirst($name);
|
||||
|
||||
if (method_exists($this->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);
|
||||
}
|
||||
}
|
||||
@@ -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<User>
|
||||
* @extends AbstractType<UserContractModel>
|
||||
*/
|
||||
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',
|
||||
|
||||
20
src/WorkingTime/Calculator/WorkingTimeCalculator.php
Normal file
20
src/WorkingTime/Calculator/WorkingTimeCalculator.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\WorkingTime\Calculator;
|
||||
|
||||
interface WorkingTimeCalculator
|
||||
{
|
||||
/**
|
||||
* @return int seconds
|
||||
*/
|
||||
public function getWorkHoursForDay(\DateTimeInterface $dateTime): int;
|
||||
|
||||
public function isWorkDay(\DateTimeInterface $dateTime): bool;
|
||||
}
|
||||
46
src/WorkingTime/Calculator/WorkingTimeCalculatorDay.php
Normal file
46
src/WorkingTime/Calculator/WorkingTimeCalculatorDay.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\WorkingTime\Calculator;
|
||||
|
||||
use App\Entity\User;
|
||||
|
||||
final class WorkingTimeCalculatorDay implements WorkingTimeCalculator
|
||||
{
|
||||
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';
|
||||
|
||||
public function __construct(private readonly User $user)
|
||||
{
|
||||
}
|
||||
|
||||
public function getWorkHoursForDay(\DateTimeInterface $dateTime): int
|
||||
{
|
||||
return (int) match ($dateTime->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;
|
||||
}
|
||||
}
|
||||
24
src/WorkingTime/Calculator/WorkingTimeCalculatorNone.php
Normal file
24
src/WorkingTime/Calculator/WorkingTimeCalculatorNone.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\WorkingTime\Calculator;
|
||||
|
||||
final class WorkingTimeCalculatorNone implements WorkingTimeCalculator
|
||||
{
|
||||
public function getWorkHoursForDay(\DateTimeInterface $dateTime): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function isWorkDay(\DateTimeInterface $dateTime): bool
|
||||
{
|
||||
// we don't know it, so we must assume every day is a a working day
|
||||
return true;
|
||||
}
|
||||
}
|
||||
48
src/WorkingTime/Mode/WorkingTimeMode.php
Normal file
48
src/WorkingTime/Mode/WorkingTimeMode.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\WorkingTime\Mode;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Form\Model\UserContractModel;
|
||||
use App\WorkingTime\Calculator\WorkingTimeCalculator;
|
||||
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
#[AutoconfigureTag]
|
||||
interface WorkingTimeMode
|
||||
{
|
||||
/**
|
||||
* Short and unique identifier for this mode.
|
||||
*/
|
||||
public function getId(): string;
|
||||
|
||||
/**
|
||||
* @return int<0, 100>
|
||||
*/
|
||||
public function getOrder(): int;
|
||||
|
||||
/**
|
||||
* Translation key for the name of this mode.
|
||||
*/
|
||||
public function getName(): string;
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface<UserContractModel> $builder
|
||||
* @param array<mixed> $options
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void;
|
||||
|
||||
public function getCalculator(User $user): WorkingTimeCalculator;
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function getFormFields(): array;
|
||||
}
|
||||
77
src/WorkingTime/Mode/WorkingTimeModeDay.php
Normal file
77
src/WorkingTime/Mode/WorkingTimeModeDay.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\WorkingTime\Mode;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Form\Type\DurationType;
|
||||
use App\WorkingTime\Calculator\WorkingTimeCalculator;
|
||||
use App\WorkingTime\Calculator\WorkingTimeCalculatorDay;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
|
||||
use Symfony\Component\Validator\Constraints\LessThanOrEqual;
|
||||
|
||||
class WorkingTimeModeDay implements WorkingTimeMode
|
||||
{
|
||||
final public const ID = 'day';
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
55
src/WorkingTime/Mode/WorkingTimeModeFactory.php
Normal file
55
src/WorkingTime/Mode/WorkingTimeModeFactory.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\WorkingTime\Mode;
|
||||
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
|
||||
|
||||
final class WorkingTimeModeFactory
|
||||
{
|
||||
/**
|
||||
* @param iterable<WorkingTimeMode> $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);
|
||||
}
|
||||
}
|
||||
50
src/WorkingTime/Mode/WorkingTimeModeNone.php
Normal file
50
src/WorkingTime/Mode/WorkingTimeModeNone.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\WorkingTime\Mode;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\WorkingTime\Calculator\WorkingTimeCalculator;
|
||||
use App\WorkingTime\Calculator\WorkingTimeCalculatorNone;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class WorkingTimeModeNone implements WorkingTimeMode
|
||||
{
|
||||
final public const ID = 'none';
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return self::ID;
|
||||
}
|
||||
|
||||
public function getOrder(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getFormFields(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
public function getCalculator(User $user): WorkingTimeCalculator
|
||||
{
|
||||
return new WorkingTimeCalculatorNone();
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,15 @@ use App\Event\WorkingTimeYearSummaryEvent;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Repository\WorkingTimeRepository;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
use App\WorkingTime\Mode\WorkingTimeMode;
|
||||
use App\WorkingTime\Mode\WorkingTimeModeFactory;
|
||||
use App\WorkingTime\Model\Month;
|
||||
use App\WorkingTime\Model\Year;
|
||||
use App\WorkingTime\Model\YearPerUserSummary;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* @internal this API and the entire namespace is instable: you should expect changes!
|
||||
* @internal this API and the entire namespace is experimental: expect changes!
|
||||
*/
|
||||
final class WorkingTimeService
|
||||
{
|
||||
@@ -33,11 +35,17 @@ final class WorkingTimeService
|
||||
public function __construct(
|
||||
private readonly TimesheetRepository $timesheetRepository,
|
||||
private readonly WorkingTimeRepository $workingTimeRepository,
|
||||
private readonly EventDispatcherInterface $eventDispatcher
|
||||
private readonly EventDispatcherInterface $eventDispatcher,
|
||||
private readonly WorkingTimeModeFactory $contractModeService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
public function getContractMode(User $user): WorkingTimeMode
|
||||
{
|
||||
return $this->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)) {
|
||||
|
||||
Reference in New Issue
Block a user