Release 2.0.30 (#4225)
This commit is contained in:
@@ -17,11 +17,11 @@ class Constants
|
||||
/**
|
||||
* The current release version
|
||||
*/
|
||||
public const VERSION = '2.0.29';
|
||||
public const VERSION = '2.0.30';
|
||||
/**
|
||||
* The current release: major * 10000 + minor * 100 + patch
|
||||
*/
|
||||
public const VERSION_ID = 20029;
|
||||
public const VERSION_ID = 20030;
|
||||
/**
|
||||
* The software name
|
||||
*/
|
||||
|
||||
@@ -34,10 +34,16 @@ use InvalidArgumentException;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
final class TimesheetService
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
private array $doNotValidateCodes = [];
|
||||
|
||||
public function __construct(
|
||||
private SystemConfiguration $configuration,
|
||||
private TimesheetRepository $repository,
|
||||
@@ -236,10 +242,25 @@ final class TimesheetService
|
||||
$errors = $this->validator->validate($timesheet, null, $groups);
|
||||
|
||||
if ($errors->count() > 0) {
|
||||
throw new ValidationFailedException($errors, 'Validation Failed');
|
||||
/** @var ConstraintViolation $error */
|
||||
foreach ($errors as $error) {
|
||||
if (\in_array($error->getCode(), $this->doNotValidateCodes, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new ValidationFailedException($errors, 'Validation Failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $validationCodes
|
||||
*/
|
||||
public function setIgnoreValidationCodes(array $validationCodes): void
|
||||
{
|
||||
$this->doNotValidateCodes = $validationCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure, that the timesheet record has the timezone of the user.
|
||||
*
|
||||
|
||||
@@ -254,7 +254,7 @@ final class TimesheetStatisticService
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DateTime($result, new \DateTimeZone($user->getTimezone()));
|
||||
return new DateTime((string) $result, new \DateTimeZone($user->getTimezone()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,10 +9,8 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class TimesheetDeactivated extends Constraint
|
||||
final class TimesheetDeactivated extends TimesheetConstraint
|
||||
{
|
||||
public const DISABLED_ACTIVITY_ERROR = 'kimai-timesheet-deactivated-activity';
|
||||
public const DISABLED_PROJECT_ERROR = 'kimai-timesheet-deactivated-project';
|
||||
|
||||
@@ -9,15 +9,18 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class TimesheetFutureTimes extends TimesheetConstraint
|
||||
{
|
||||
public const BEGIN_IN_FUTURE_ERROR = 'kimai-timesheet-future-times-01';
|
||||
public const END_IN_FUTURE_ERROR = 'kimai-timesheet-future-times-02';
|
||||
|
||||
protected const ERROR_NAMES = [
|
||||
self::BEGIN_IN_FUTURE_ERROR => 'The begin date cannot be in the future.',
|
||||
self::END_IN_FUTURE_ERROR => 'The end date cannot be in the future.',
|
||||
];
|
||||
|
||||
public string $message = 'The begin date cannot be in the future.';
|
||||
public string $message = 'The date cannot be in the future.';
|
||||
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
|
||||
@@ -22,33 +22,44 @@ final class TimesheetFutureTimesValidator extends ConstraintValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param TimesheetEntity $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate(mixed $timesheet, Constraint $constraint): void
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetFutureTimes)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetFutureTimes::class);
|
||||
}
|
||||
|
||||
if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) {
|
||||
throw new UnexpectedTypeException($timesheet, TimesheetEntity::class);
|
||||
if (!\is_object($value) || !($value instanceof TimesheetEntity)) {
|
||||
throw new UnexpectedTypeException($value, TimesheetEntity::class);
|
||||
}
|
||||
|
||||
if ($this->configuration->isTimesheetAllowFutureTimes()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$now = new \DateTime('now', $timesheet->getBegin()->getTimezone());
|
||||
$now = new \DateTime('now', $value->getBegin()->getTimezone());
|
||||
|
||||
// allow configured default rounding time + 1 minute - see #1295
|
||||
$allowedDiff = ($this->configuration->getTimesheetDefaultRoundingBegin() * 60) + 60;
|
||||
if (($now->getTimestamp() + $allowedDiff) < $timesheet->getBegin()->getTimestamp()) {
|
||||
$this->context->buildViolation('The begin date cannot be in the future.')
|
||||
$nowTs = $now->getTimestamp() + $allowedDiff;
|
||||
if ($value->getBegin() !== null && $nowTs < $value->getBegin()->getTimestamp()) {
|
||||
$this->context->buildViolation(TimesheetFutureTimes::getErrorName(TimesheetFutureTimes::BEGIN_IN_FUTURE_ERROR))
|
||||
->atPath('begin_date')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetFutureTimes::BEGIN_IN_FUTURE_ERROR)
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
$allowedDiff = ($this->configuration->getTimesheetDefaultRoundingEnd() * 60) + 60;
|
||||
$nowTs = $now->getTimestamp() + $allowedDiff;
|
||||
if ($value->getEnd() !== null && $nowTs < $value->getEnd()->getTimestamp()) {
|
||||
$this->context->buildViolation(TimesheetFutureTimes::getErrorName(TimesheetFutureTimes::END_IN_FUTURE_ERROR))
|
||||
->atPath('end_time')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetFutureTimes::END_IN_FUTURE_ERROR)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user