Support for changeable work contract types (#5069)
This commit is contained in:
@@ -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'
|
||||
|
||||
|
||||
57
migrations/Version20240920105524.php
Normal file
57
migrations/Version20240920105524.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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 DoctrineMigrations;
|
||||
|
||||
use App\Doctrine\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
/**
|
||||
* @version 2.22
|
||||
*/
|
||||
final class Version20240920105524 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Updates the user preferences for configurable work contract type';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$ids = $this->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;
|
||||
}
|
||||
}
|
||||
@@ -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)) {
|
||||
|
||||
@@ -2,20 +2,39 @@
|
||||
|
||||
{% block form_content %}
|
||||
|
||||
<script>
|
||||
function toggleWorkingContractModeForm(value)
|
||||
{
|
||||
document.querySelectorAll('.work_contract_mode').forEach(function (element) {
|
||||
element.classList.add('d-none');
|
||||
});
|
||||
|
||||
// Show the element with the class 'work_contract_mode_' + value
|
||||
var elementToShow = document.querySelector('.work_contract_mode_' + value);
|
||||
if (elementToShow) {
|
||||
elementToShow.classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{% form_theme form 'form/horizontal.html.twig' %}
|
||||
|
||||
{{ form_row(form._token) }}
|
||||
|
||||
<fieldset class="form-fieldset form-fieldset-light">
|
||||
<legend>{{ 'work_times_should'|trans }}</legend>
|
||||
{{ 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) }}
|
||||
</fieldset>
|
||||
{% if form.workContractMode is defined %}
|
||||
<fieldset class="form-fieldset form-fieldset-light border-0 pb-0">
|
||||
<legend>{{ 'work_times_should'|trans }}</legend>
|
||||
{{ form_row(form.workContractMode, {attr:{'onchange': 'toggleWorkingContractModeForm(this.value)'}}) }}
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
|
||||
{% for mode in form.vars.workContractModes %}
|
||||
<fieldset class="form-fieldset form-fieldset-light border-0 work_contract_mode work_contract_mode_{{ mode.getId() }} {% if user.getWorkContractMode() != mode.getId() %}d-none{% endif %}">
|
||||
{% for field in mode.getFormFields() %}
|
||||
{{ form_row(form.children[field]) }}
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
{% endfor %}
|
||||
|
||||
<fieldset class="form-fieldset form-fieldset-light">
|
||||
{{- form_rest(form) -}}
|
||||
|
||||
@@ -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')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?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\Tests\WorkingTime\Calculator;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\WorkingTime\Calculator\WorkingTimeCalculatorDay;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\WorkingTime\Calculator\WorkingTimeCalculatorDay
|
||||
*/
|
||||
class WorkingTimeCalculatorDayTest extends TestCase
|
||||
{
|
||||
public function testDefaults(): void
|
||||
{
|
||||
$monday = new \DateTimeImmutable('monday last week 12:00:00');
|
||||
$tuesday = $monday->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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?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\Tests\WorkingTime\Calculator;
|
||||
|
||||
use App\WorkingTime\Calculator\WorkingTimeCalculatorNone;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\WorkingTime\Calculator\WorkingTimeCalculatorNone
|
||||
*/
|
||||
class WorkingTimeCalculatorNoneTest extends TestCase
|
||||
{
|
||||
public function testDefaults(): void
|
||||
{
|
||||
$date = new \DateTime();
|
||||
$sut = new WorkingTimeCalculatorNone();
|
||||
self::assertTrue($sut->isWorkDay($date));
|
||||
self::assertEquals(0, $sut->getWorkHoursForDay($date));
|
||||
}
|
||||
}
|
||||
34
tests/WorkingTime/Mode/WorkingTimeModeDayTest.php
Normal file
34
tests/WorkingTime/Mode/WorkingTimeModeDayTest.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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\Tests\WorkingTime\Mode;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\WorkingTime\Calculator\WorkingTimeCalculatorDay;
|
||||
use App\WorkingTime\Mode\WorkingTimeModeDay;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\WorkingTime\Mode\WorkingTimeModeDay
|
||||
*/
|
||||
class WorkingTimeModeDayTest extends TestCase
|
||||
{
|
||||
public function testDefaults(): void
|
||||
{
|
||||
$user = new User();
|
||||
$user->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);
|
||||
}
|
||||
}
|
||||
46
tests/WorkingTime/Mode/WorkingTimeModeFactoryTest.php
Normal file
46
tests/WorkingTime/Mode/WorkingTimeModeFactoryTest.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\Tests\WorkingTime\Mode;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\WorkingTime\Mode\WorkingTimeModeDay;
|
||||
use App\WorkingTime\Mode\WorkingTimeModeFactory;
|
||||
use App\WorkingTime\Mode\WorkingTimeModeNone;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\WorkingTime\Mode\WorkingTimeModeFactory
|
||||
*/
|
||||
class WorkingTimeModeFactoryTest extends TestCase
|
||||
{
|
||||
public function testDefaults(): void
|
||||
{
|
||||
$none = new WorkingTimeModeNone();
|
||||
$day = new WorkingTimeModeDay();
|
||||
$modes = [$none, $day];
|
||||
$sut = new WorkingTimeModeFactory($modes);
|
||||
$this->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');
|
||||
}
|
||||
}
|
||||
30
tests/WorkingTime/Mode/WorkingTimeModeNoneTest.php
Normal file
30
tests/WorkingTime/Mode/WorkingTimeModeNoneTest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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\Tests\WorkingTime\Mode;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\WorkingTime\Calculator\WorkingTimeCalculatorNone;
|
||||
use App\WorkingTime\Mode\WorkingTimeModeNone;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\WorkingTime\Mode\WorkingTimeModeNone
|
||||
*/
|
||||
class WorkingTimeModeNoneTest extends TestCase
|
||||
{
|
||||
public function testDefaults(): void
|
||||
{
|
||||
$sut = new WorkingTimeModeNone();
|
||||
$this->assertEquals('none', $sut->getId());
|
||||
$this->assertEquals(0, $sut->getOrder());
|
||||
$this->assertEquals('', $sut->getName());
|
||||
$this->assertInstanceOf(WorkingTimeCalculatorNone::class, $sut->getCalculator(new User()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user