Release 2.39 (#5604)
* prepare audit via annotation * default calendar slot label distance of 1h + replace freestyle config with dropdown * added missing return definition in callbacks * refactor view name handling * dispatch calendar view changes and push them into the URL to be able to reload the poage * bump packages * fix timezone issue in calendar sum calculation * fixes #5618 resetRates() * show expected daily hours in working-contract screen
This commit is contained in:
21
src/Audit/Loggable.php
Normal file
21
src/Audit/Loggable.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\Audit;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class Loggable
|
||||
{
|
||||
/**
|
||||
* @param class-string|null $customFieldClass
|
||||
*/
|
||||
public function __construct(public ?string $customFieldClass = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
18
src/Audit/Versioned.php
Normal file
18
src/Audit/Versioned.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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\Audit;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||
final class Versioned
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
55
src/Calendar/CalendarQuery.php
Normal file
55
src/Calendar/CalendarQuery.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\Calendar;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Form\Type\CalendarViewType;
|
||||
use DateTimeInterface;
|
||||
|
||||
class CalendarQuery
|
||||
{
|
||||
private ?DateTimeInterface $date = null;
|
||||
private string $view = CalendarViewType::DEFAULT_VIEW;
|
||||
private ?User $user = null;
|
||||
|
||||
public function getDate(): ?DateTimeInterface
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
public function setDate(?DateTimeInterface $date): void
|
||||
{
|
||||
$this->date = $date;
|
||||
}
|
||||
|
||||
public function getView(): string
|
||||
{
|
||||
return $this->view;
|
||||
}
|
||||
|
||||
public function setView(string $view): void
|
||||
{
|
||||
$this->view = match($view){
|
||||
'agendaDay', 'day' => 'day',
|
||||
'agendaWeek', 'week' => 'week',
|
||||
default => 'month',
|
||||
};
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,11 @@ final class Constants
|
||||
/**
|
||||
* The current release version
|
||||
*/
|
||||
public const VERSION = '2.38.0';
|
||||
public const VERSION = '2.39.0';
|
||||
/**
|
||||
* The current release: major * 10000 + minor * 100 + patch
|
||||
*/
|
||||
public const VERSION_ID = 23800;
|
||||
public const VERSION_ID = 23900;
|
||||
/**
|
||||
* The software name
|
||||
*/
|
||||
|
||||
@@ -9,15 +9,18 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Calendar\CalendarQuery;
|
||||
use App\Calendar\CalendarService;
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\User;
|
||||
use App\Form\CalendarForm;
|
||||
use App\Form\Toolbar\CalendarToolbarForm;
|
||||
use App\Form\Type\CalendarViewType;
|
||||
use App\Timesheet\TrackingModeService;
|
||||
use App\Utils\PageSetup;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
/**
|
||||
@@ -35,34 +38,41 @@ final class CalendarController extends AbstractController
|
||||
#[Route(path: '/{profile}', name: 'calendar_user', methods: ['GET'])]
|
||||
public function userCalendar(Request $request): Response
|
||||
{
|
||||
$form = null;
|
||||
$profile = $this->getUser();
|
||||
$currentUser = $this->getUser();
|
||||
$profile = $currentUser;
|
||||
$canChangeUser = $this->isGranted('view_other_timesheet');
|
||||
|
||||
if ($this->isGranted('view_other_timesheet')) {
|
||||
$form = $this->createFormForGetRequest(CalendarForm::class, ['user' => $profile], [
|
||||
'action' => $this->generateUrl('calendar'),
|
||||
]);
|
||||
$query = new CalendarQuery();
|
||||
$query->setUser($profile);
|
||||
$query->setDate($this->getDateTimeFactory($profile)->create());
|
||||
|
||||
$form->handleRequest($request);
|
||||
$defaultView = CalendarViewType::DEFAULT_VIEW;
|
||||
$userView = $profile->getPreference('calendar_initial_view')?->getValue();
|
||||
if ($userView !== null) {
|
||||
$defaultView = (string) $userView;
|
||||
}
|
||||
$query->setView($defaultView);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$values = $form->getData();
|
||||
if ($values['user'] instanceof User) {
|
||||
$profile = $values['user'];
|
||||
}
|
||||
}
|
||||
$form = $this->createFormForGetRequest(CalendarToolbarForm::class, $query, [
|
||||
'action' => $this->generateUrl('calendar'),
|
||||
'change_user' => $canChangeUser,
|
||||
]);
|
||||
|
||||
$form = $form->createView();
|
||||
$form->submit($request->query->all(), false);
|
||||
|
||||
// hide if the current user is the only available one
|
||||
if (\count($form->offsetGet('user')->vars['choices']) < 2) {
|
||||
$form = null;
|
||||
$profile = $this->getUser();
|
||||
}
|
||||
if ($query->getUser() === null) {
|
||||
$query->setUser($currentUser);
|
||||
}
|
||||
|
||||
/** @var User $profile */
|
||||
$profile = $query->getUser();
|
||||
|
||||
if ($currentUser !== $profile && !$canChangeUser) {
|
||||
throw new AccessDeniedException('User is not allowed to see other users calendar');
|
||||
}
|
||||
|
||||
$mode = $this->service->getActiveMode();
|
||||
$factory = $this->getDateTimeFactory();
|
||||
$factory = $this->getDateTimeFactory($profile);
|
||||
|
||||
// if now is default time, we do not pass it on, so it can be re-calculated for each new entry
|
||||
$defaultStart = null;
|
||||
@@ -89,7 +99,9 @@ final class CalendarController extends AbstractController
|
||||
|
||||
return $this->render('calendar/user.html.twig', [
|
||||
'page_setup' => $page,
|
||||
'form' => $form,
|
||||
'initial_view' => $query->getView(),
|
||||
'initial_date' => $query->getDate(),
|
||||
'form' => $form->createView(),
|
||||
'user' => $profile,
|
||||
'config' => $config,
|
||||
'dragAndDrop' => $dragAndDrop,
|
||||
|
||||
@@ -83,8 +83,25 @@ final class ContractController extends AbstractController
|
||||
$boxConfiguration->setDecimal(false);
|
||||
$boxConfiguration->setCollapsed($summary->count() > 0);
|
||||
|
||||
$hasConfiguration = $profile->hasWorkHourConfiguration();
|
||||
$days = [];
|
||||
if ($hasConfiguration) {
|
||||
$calculator = $workingTimeService->getContractMode($profile)->getCalculator($profile);
|
||||
$start = $dateTimeFactory->getStartOfWeek();
|
||||
$end = $dateTimeFactory->getEndOfWeek();
|
||||
while ($start < $end) {
|
||||
$tmp = clone $start;
|
||||
$days[] = [
|
||||
'date' => $tmp,
|
||||
'duration' => $calculator->isWorkDay($tmp) ? $calculator->getWorkHoursForDay($tmp) : null
|
||||
];
|
||||
$start = $start->add(new \DateInterval('P1D'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('contract/status.html.twig', [
|
||||
'withWorkHourConfiguration' => $profile->hasWorkHourConfiguration(),
|
||||
'days' => $days,
|
||||
'withWorkHourConfiguration' => $hasConfiguration,
|
||||
'box_configuration' => $boxConfiguration,
|
||||
'page_setup' => $page,
|
||||
'decimal' => $boxConfiguration->isDecimal(),
|
||||
|
||||
@@ -37,6 +37,7 @@ use App\Validator\Constraints\ColorChoices;
|
||||
use App\Validator\Constraints\DateTimeFormat;
|
||||
use App\Validator\Constraints\TimeFormat;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CountryType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||
@@ -587,7 +588,8 @@ final class SystemConfigurationController extends AbstractController
|
||||
->setConstraints([new NotBlank(), new TimeFormat()]),
|
||||
(new Configuration('calendar.slot_duration'))
|
||||
->setTranslationDomain('system-configuration')
|
||||
->setType(TextType::class)
|
||||
->setType(ChoiceType::class)
|
||||
->setOptions(['choices' => ['00:15' => '00:15:00', '00:30' => '00:30:00', '01:00' => '01:00:00']])
|
||||
->setConstraints([new Regex(['pattern' => '/[0-2]{1}[0-9]{1}:[0-9]{2}:[0-9]{2}/']), new NotNull()]),
|
||||
(new Configuration('calendar.dragdrop_amount'))
|
||||
->setTranslationDomain('system-configuration')
|
||||
|
||||
@@ -365,9 +365,7 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
}
|
||||
|
||||
if ($dto->isRecalculateRates()) {
|
||||
$timesheet->setFixedRate(null);
|
||||
$timesheet->setHourlyRate(null);
|
||||
$timesheet->setInternalRate(null);
|
||||
$timesheet->resetRates();
|
||||
$execute = true;
|
||||
} elseif (null !== $dto->getFixedRate()) {
|
||||
$timesheet->setFixedRate($dto->getFixedRate());
|
||||
|
||||
@@ -35,7 +35,7 @@ final class TimesheetApiEditForm extends TimesheetEditForm
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
$data = $event->getData();
|
||||
if (\array_key_exists('billable', $data)) {
|
||||
$data['billableMode'] = Timesheet::BILLABLE_AUTOMATIC;
|
||||
|
||||
@@ -27,7 +27,7 @@ trait ColorTrait
|
||||
// this code exists only for backward compatibility
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SET_DATA,
|
||||
function (FormEvent $event) use ($required) {
|
||||
function (FormEvent $event) use ($required): void {
|
||||
if (!$event->getForm()->getConfig()->hasOption('choices')) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ trait FormTrait
|
||||
// replaces the project select after submission, to make sure only projects for the selected customer are displayed
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($builder, $project, $customer, $isNew, $options) {
|
||||
function (FormEvent $event) use ($builder, $project, $customer, $isNew, $options): void {
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = $event->getData();
|
||||
$customer = \array_key_exists('customer', $data) && $data['customer'] !== '' ? $data['customer'] : null;
|
||||
@@ -114,7 +114,7 @@ trait FormTrait
|
||||
// replaces the activity select after submission, to make sure only activities for the selected project are displayed
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($options) {
|
||||
function (FormEvent $event) use ($options): void {
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = $event->getData();
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ final class TimesheetMultiUpdate extends AbstractType
|
||||
// TODO replace me with FormTrait
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($project, $customer) {
|
||||
function (FormEvent $event) use ($project, $customer): void {
|
||||
$data = $event->getData();
|
||||
$customer = isset($data['customer']) && !empty($data['customer']) ? $data['customer'] : null;
|
||||
$project = isset($data['project']) && !empty($data['project']) ? $data['project'] : $project;
|
||||
@@ -126,7 +126,7 @@ final class TimesheetMultiUpdate extends AbstractType
|
||||
// TODO replace me with FormTrait
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($activityOptions) {
|
||||
function (FormEvent $event) use ($activityOptions): void {
|
||||
$data = $event->getData();
|
||||
if (!isset($data['project']) || empty($data['project'])) {
|
||||
return;
|
||||
|
||||
@@ -188,7 +188,7 @@ class TimesheetEditForm extends AbstractType
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::POST_SET_DATA,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var Timesheet $timesheet */
|
||||
$timesheet = $event->getData();
|
||||
$begin = $timesheet->getBegin();
|
||||
@@ -203,7 +203,7 @@ class TimesheetEditForm extends AbstractType
|
||||
// map single fields to original datetime object
|
||||
$builder->addEventListener(
|
||||
FormEvents::SUBMIT,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var Timesheet $data */
|
||||
$data = $event->getData();
|
||||
|
||||
@@ -243,7 +243,7 @@ class TimesheetEditForm extends AbstractType
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::POST_SET_DATA,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var Timesheet|null $data */
|
||||
$data = $event->getData();
|
||||
if (null !== $data->getEnd()) {
|
||||
@@ -255,7 +255,7 @@ class TimesheetEditForm extends AbstractType
|
||||
// make sure that date & time fields are mapped back to begin & end fields
|
||||
$builder->addEventListener(
|
||||
FormEvents::SUBMIT,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var Timesheet $timesheet */
|
||||
$timesheet = $event->getData();
|
||||
$oldEnd = $timesheet->getEnd();
|
||||
@@ -334,7 +334,7 @@ class TimesheetEditForm extends AbstractType
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::POST_SET_DATA,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var Timesheet|null $timesheet */
|
||||
$timesheet = $event->getData();
|
||||
if (null === $timesheet || ($timesheet instanceof Timesheet && $timesheet->isRunning())) {
|
||||
@@ -346,7 +346,7 @@ class TimesheetEditForm extends AbstractType
|
||||
// make sure that duration is mapped back to end field
|
||||
$builder->addEventListener(
|
||||
FormEvents::SUBMIT,
|
||||
function (FormEvent $event) use ($forceApply) {
|
||||
function (FormEvent $event) use ($forceApply): void {
|
||||
/** @var Timesheet $timesheet */
|
||||
$timesheet = $event->getData();
|
||||
|
||||
|
||||
@@ -7,17 +7,24 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Form;
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Form\Type\CalendarViewType;
|
||||
use App\Form\Type\DayPickerType;
|
||||
use App\Form\Type\UserType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class CalendarForm extends AbstractType
|
||||
final class CalendarToolbarForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('date', DayPickerType::class, [
|
||||
'model_timezone' => $options['timezone'],
|
||||
'view_timezone' => $options['timezone'],
|
||||
]);
|
||||
$builder->add('view', CalendarViewType::class, []);
|
||||
$builder->add('user', UserType::class, [
|
||||
'required' => false,
|
||||
'attr' => ['onchange' => 'this.form.submit()']
|
||||
@@ -28,7 +35,9 @@ final class CalendarForm extends AbstractType
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'csrf_protection' => false,
|
||||
'timezone' => date_default_timezone_get(),
|
||||
'method' => 'GET',
|
||||
'change_user' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ trait ToolbarFormTrait
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($builder, $options, $name, $multiCustomer, $multiProject) {
|
||||
function (FormEvent $event) use ($builder, $options, $name, $multiCustomer, $multiProject): void {
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = $event->getData();
|
||||
$event->getForm()->add($name, CustomerType::class, array_merge([
|
||||
@@ -187,7 +187,7 @@ trait ToolbarFormTrait
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($builder, $options, $name, $multiCustomer, $multiProject, $multiActivity) {
|
||||
function (FormEvent $event) use ($builder, $options, $name, $multiCustomer, $multiProject, $multiActivity): void {
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = $event->getData();
|
||||
$event->getForm()->add($name, ProjectType::class, array_merge([
|
||||
@@ -266,7 +266,7 @@ trait ToolbarFormTrait
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($name, $multiProject, $activityOptions, $options) {
|
||||
function (FormEvent $event) use ($name, $multiProject, $activityOptions, $options): void {
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = $event->getData();
|
||||
$event->getForm()->add($name, ActivityType::class, array_merge($activityOptions, [
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
namespace App\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
@@ -20,12 +22,28 @@ final class CalendarViewType extends AbstractType
|
||||
{
|
||||
public const DEFAULT_VIEW = 'month';
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->addModelTransformer(new CallbackTransformer(
|
||||
function ($transform) {
|
||||
return match ($transform) {
|
||||
'agendaDay', 'day' => 'day',
|
||||
'agendaWeek', 'week' => 'week',
|
||||
default => self::DEFAULT_VIEW,
|
||||
};
|
||||
},
|
||||
function ($reverseTransform) {
|
||||
return $reverseTransform;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$choices = [
|
||||
'month' => 'month',
|
||||
'agendaWeek' => 'agendaWeek',
|
||||
'agendaDay' => 'agendaDay',
|
||||
'agendaWeek' => 'week',
|
||||
'agendaDay' => 'day',
|
||||
];
|
||||
|
||||
$resolver->setDefaults([
|
||||
|
||||
43
src/Form/Type/DayPickerType.php
Normal file
43
src/Form/Type/DayPickerType.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?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\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Form field type to enter a date in HTML5 format, mainly for GET forms.
|
||||
*/
|
||||
class DayPickerType extends AbstractType
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'label' => 'date',
|
||||
'widget' => 'single_text',
|
||||
'html5' => true,
|
||||
'format' => DateType::HTML5_FORMAT,
|
||||
'model_timezone' => date_default_timezone_get(),
|
||||
'view_timezone' => date_default_timezone_get(),
|
||||
'datepicker' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): string
|
||||
{
|
||||
return DateType::class;
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'day';
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ final class EntityMetaDefinitionType extends AbstractType
|
||||
{
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SET_DATA,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var MetaTableTypeInterface $definition */
|
||||
$definition = $event->getData();
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ final class ExportColumnsType extends AbstractType
|
||||
{
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
$data = $event->getData();
|
||||
if (\is_array($data)) {
|
||||
$this->ordered = $data; // @phpstan-ignore assign.propertyType
|
||||
@@ -156,7 +156,7 @@ final class ExportColumnsType extends AbstractType
|
||||
);
|
||||
$builder->addEventListener(
|
||||
FormEvents::SUBMIT,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
$event->setData($this->ordered);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -27,7 +27,7 @@ final class MetaFieldsCollectionType extends AbstractType
|
||||
{
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SET_DATA,
|
||||
function (FormEvent $event) use ($options) {
|
||||
function (FormEvent $event) use ($options): void {
|
||||
/** @var ArrayCollection<MetaTableTypeInterface> $collection */
|
||||
$collection = $event->getData();
|
||||
foreach ($collection as $collectionItem) {
|
||||
|
||||
@@ -53,7 +53,7 @@ final class QuickEntryTimesheetType extends AbstractType
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::POST_SET_DATA,
|
||||
function (FormEvent $event) use ($durationOptions) {
|
||||
function (FormEvent $event) use ($durationOptions): void {
|
||||
/** @var Timesheet|null $data */
|
||||
$data = $event->getData();
|
||||
if (null === $data || $data->isRunning()) {
|
||||
@@ -89,7 +89,7 @@ final class QuickEntryTimesheetType extends AbstractType
|
||||
// make sure that duration is mapped back to end field
|
||||
$builder->addEventListener(
|
||||
FormEvents::SUBMIT,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var Timesheet $data */
|
||||
$data = $event->getData();
|
||||
$duration = $data->getDuration(false);
|
||||
|
||||
@@ -39,7 +39,7 @@ final class QuickEntryWeekType extends AbstractType
|
||||
|
||||
$builder->add('project', ProjectType::class, $projectOptions);
|
||||
|
||||
$projectFunction = function (FormEvent $event) use ($projectOptions) {
|
||||
$projectFunction = function (FormEvent $event) use ($projectOptions): void {
|
||||
/** @var QuickEntryModel|null $data */
|
||||
$data = $event->getData();
|
||||
if ($data === null || $data->getProject() === null) {
|
||||
@@ -62,7 +62,7 @@ final class QuickEntryWeekType extends AbstractType
|
||||
|
||||
$builder->add('activity', ActivityType::class, $activityOptions);
|
||||
|
||||
$activityFunction = function (FormEvent $event) use ($activityOptions) {
|
||||
$activityFunction = function (FormEvent $event) use ($activityOptions): void {
|
||||
/** @var QuickEntryModel|null $data */
|
||||
$data = $event->getData();
|
||||
if ($data === null || $data->getActivity() === null) {
|
||||
@@ -77,7 +77,7 @@ final class QuickEntryWeekType extends AbstractType
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, $activityFunction);
|
||||
|
||||
// make sure to pre-fill the form, so non-global activities can be loaded for the select project
|
||||
$activityPreSubmitFunction = function (FormEvent $event) use ($activityOptions) {
|
||||
$activityPreSubmitFunction = function (FormEvent $event) use ($activityOptions): void {
|
||||
$data = $event->getData();
|
||||
|
||||
if (\is_array($data)) {
|
||||
@@ -113,7 +113,7 @@ final class QuickEntryWeekType extends AbstractType
|
||||
],
|
||||
]);
|
||||
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options) {
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options): void {
|
||||
if ($event->getData() === null && $options['prototype_data'] instanceof QuickEntryModel) {
|
||||
$event->setData(clone $options['prototype_data']);
|
||||
}
|
||||
@@ -151,7 +151,7 @@ final class QuickEntryWeekType extends AbstractType
|
||||
// make sure that duration is mapped back to end field
|
||||
$builder->addEventListener(
|
||||
FormEvents::SUBMIT,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var QuickEntryModel $data */
|
||||
$data = $event->getData();
|
||||
$newRecords = $data->getNewTimesheet();
|
||||
|
||||
@@ -31,7 +31,7 @@ final class SystemConfigurationType extends AbstractType
|
||||
{
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SET_DATA,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var Configuration $preference */
|
||||
$preference = $event->getData();
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ final class TagsSelectType extends AbstractType
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) {
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options): void {
|
||||
/** @var array<string> $tagIds */
|
||||
$tagIds = $event->getData();
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ final class UserPreferenceType extends AbstractType
|
||||
{
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SET_DATA,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var UserPreference $preference */
|
||||
$preference = $event->getData();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ final class UserPreferencesCollectionType extends AbstractType
|
||||
{
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SET_DATA,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event): void {
|
||||
/** @var ArrayCollection<UserPreference> $collection */
|
||||
$collection = $event->getData();
|
||||
foreach ($collection as $collectionItem) {
|
||||
|
||||
@@ -34,7 +34,7 @@ final class ProjectDetailsForm extends AbstractType
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($projectOptions) {
|
||||
function (FormEvent $event) use ($projectOptions): void {
|
||||
$data = $event->getData();
|
||||
if (isset($data['project']) && !empty($data['project'])) {
|
||||
$projectId = $data['project'];
|
||||
|
||||
@@ -18,7 +18,7 @@ final class WorkingTimeCalculatorNone implements WorkingTimeCalculator
|
||||
|
||||
public function isWorkDay(\DateTimeInterface $dateTime): bool
|
||||
{
|
||||
// we don't know it, so we must assume every day is a a working day
|
||||
// we don't know it, so we must assume every day is a working day
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user