weekly quick-entry form (#2793)

This commit is contained in:
Kevin Papst
2021-10-18 11:12:46 +02:00
committed by GitHub
parent 64893e0a95
commit 9ab098af86
105 changed files with 3356 additions and 778 deletions

View File

@@ -25,7 +25,7 @@ class ProjectValidator extends ConstraintValidator
public function validate($value, Constraint $constraint)
{
if (!($constraint instanceof ProjectConstraint)) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\Project');
throw new UnexpectedTypeException($constraint, ProjectConstraint::class);
}
if (!\is_object($value) || !($value instanceof Project)) {

View File

@@ -0,0 +1,25 @@
<?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\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"CLASS"})
*/
class QuickEntryModel extends Constraint
{
public const ACTIVITY_REQUIRED = 'quick-entry-model-01';
public const PROJECT_REQUIRED = 'quick-entry-model-02';
public $messageActivityRequired = 'An activity needs to be selected.';
public $messageProjectRequired = 'A project needs to be selected.';
}

View File

@@ -0,0 +1,56 @@
<?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\Validator\Constraints;
use App\Model\QuickEntryModel;
use App\Validator\Constraints\QuickEntryModel as QuickEntryModelConstraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class QuickEntryModelValidator extends ConstraintValidator
{
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof QuickEntryModelConstraint) {
throw new UnexpectedTypeException($constraint, QuickEntryModelConstraint::class);
}
if (!\is_object($value) || !($value instanceof QuickEntryModel)) {
throw new UnexpectedTypeException($value, QuickEntryModel::class);
}
/** @var QuickEntryModel $model */
$model = $value;
if ($model->isPrototype()) {
return;
}
if ($model->hasExistingTimesheet() || $model->hasNewTimesheet()) {
if ($model->getActivity() === null) {
$this->context->buildViolation($constraint->messageActivityRequired)
->atPath('activity')
->setCode(QuickEntryModelConstraint::ACTIVITY_REQUIRED)
->addViolation();
}
if ($model->getProject() === null) {
$this->context->buildViolation($constraint->messageProjectRequired)
->atPath('project')
->setCode(QuickEntryModelConstraint::PROJECT_REQUIRED)
->addViolation();
}
}
}
}

View 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\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"CLASS"})
*/
class QuickEntryTimesheet extends Constraint
{
}

View File

@@ -0,0 +1,61 @@
<?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\Validator\Constraints;
use App\Entity\Timesheet as TimesheetEntity;
use App\Validator\Constraints\QuickEntryTimesheet as QuickEntryTimesheetConstraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class QuickEntryTimesheetValidator extends ConstraintValidator
{
/**
* @var Constraint[]
*/
private $constraints;
/**
* @param Constraint[] $constraints
*/
public function __construct(iterable $constraints)
{
$this->constraints = $constraints;
}
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof QuickEntryTimesheetConstraint) {
throw new UnexpectedTypeException($constraint, QuickEntryTimesheetConstraint::class);
}
if (!\is_object($value) || !($value instanceof TimesheetEntity)) {
throw new UnexpectedTypeException($value, TimesheetEntity::class);
}
/** @var TimesheetEntity $timesheet */
$timesheet = $value;
if ($timesheet->getId() === null && $timesheet->getDuration(false) === null) {
return;
}
foreach ($this->constraints as $constraint) {
$this->context
->getValidator()
->inContext($this->context)
->atPath('duration')
->validate($timesheet, $constraint, [Constraint::DEFAULT_GROUP]);
}
}
}

View File

@@ -18,22 +18,32 @@ use Symfony\Component\Validator\Constraint;
*/
class Timesheet extends Constraint
{
public const MISSING_BEGIN_ERROR = 'kimai-timesheet-81';
public const END_BEFORE_BEGIN_ERROR = 'kimai-timesheet-82';
public const MISSING_ACTIVITY_ERROR = 'kimai-timesheet-84';
public const MISSING_PROJECT_ERROR = 'kimai-timesheet-85';
public const ACTIVITY_PROJECT_MISMATCH_ERROR = 'kimai-timesheet-86';
public const DISABLED_ACTIVITY_ERROR = 'kimai-timesheet-87';
public const DISABLED_PROJECT_ERROR = 'kimai-timesheet-88';
public const DISABLED_CUSTOMER_ERROR = 'kimai-timesheet-89';
public const PROJECT_NOT_STARTED = 'kimai-timesheet-91';
public const PROJECT_ALREADY_ENDED = 'kimai-timesheet-92';
/** @deprecated since 1.15.3 - use TimesheetBasic::MISSING_BEGIN_ERROR instead */
public const MISSING_BEGIN_ERROR = TimesheetBasic::MISSING_BEGIN_ERROR;
/** @deprecated since 1.15.3 - use TimesheetBasic::END_BEFORE_BEGIN_ERROR instead */
public const END_BEFORE_BEGIN_ERROR = TimesheetBasic::END_BEFORE_BEGIN_ERROR;
/** @deprecated since 1.15.3 - use TimesheetBasic::MISSING_ACTIVITY_ERROR instead */
public const MISSING_ACTIVITY_ERROR = TimesheetBasic::MISSING_ACTIVITY_ERROR;
/** @deprecated since 1.15.3 - use TimesheetBasic::MISSING_PROJECT_ERROR instead */
public const MISSING_PROJECT_ERROR = TimesheetBasic::MISSING_PROJECT_ERROR;
/** @deprecated since 1.15.3 - use TimesheetBasic::ACTIVITY_PROJECT_MISMATCH_ERROR instead */
public const ACTIVITY_PROJECT_MISMATCH_ERROR = TimesheetBasic::ACTIVITY_PROJECT_MISMATCH_ERROR;
/** @deprecated since 1.15.3 - use TimesheetBasic::DISABLED_ACTIVITY_ERROR instead */
public const DISABLED_ACTIVITY_ERROR = TimesheetBasic::DISABLED_ACTIVITY_ERROR;
/** @deprecated since 1.15.3 - use TimesheetBasic::DISABLED_PROJECT_ERROR instead */
public const DISABLED_PROJECT_ERROR = TimesheetBasic::DISABLED_PROJECT_ERROR;
/** @deprecated since 1.15.3 - use TimesheetBasic::DISABLED_CUSTOMER_ERROR instead */
public const DISABLED_CUSTOMER_ERROR = TimesheetBasic::DISABLED_CUSTOMER_ERROR;
/** @deprecated since 1.15.3 - use TimesheetBasic::PROJECT_NOT_STARTED instead */
public const PROJECT_NOT_STARTED = TimesheetBasic::PROJECT_NOT_STARTED;
/** @deprecated since 1.15.3 - use TimesheetBasic::PROJECT_ALREADY_ENDED instead */
public const PROJECT_ALREADY_ENDED = TimesheetBasic::PROJECT_ALREADY_ENDED;
protected static $errorNames = [
self::MISSING_BEGIN_ERROR => 'You must submit a begin date.',
self::END_BEFORE_BEGIN_ERROR => 'End date must not be earlier then start date.',
self::MISSING_ACTIVITY_ERROR => 'A timesheet must have an activity.',
self::MISSING_PROJECT_ERROR => 'A timesheet must have a project.',
self::MISSING_ACTIVITY_ERROR => 'An activity needs to be selected.',
self::MISSING_PROJECT_ERROR => 'A project needs to be selected.',
self::ACTIVITY_PROJECT_MISMATCH_ERROR => 'Project mismatch, project specific activity and timesheet project are different.',
self::DISABLED_ACTIVITY_ERROR => 'Cannot start a disabled activity.',
self::DISABLED_PROJECT_ERROR => 'Cannot start a disabled project.',

View 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\Validator\Constraints;
use Doctrine\Common\Annotations\Annotation\Target;
/**
* @Annotation
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
*/
class TimesheetBasic extends TimesheetConstraint
{
public const MISSING_BEGIN_ERROR = 'kimai-timesheet-81';
public const END_BEFORE_BEGIN_ERROR = 'kimai-timesheet-82';
public const MISSING_ACTIVITY_ERROR = 'kimai-timesheet-84';
public const MISSING_PROJECT_ERROR = 'kimai-timesheet-85';
public const ACTIVITY_PROJECT_MISMATCH_ERROR = 'kimai-timesheet-86';
public const DISABLED_ACTIVITY_ERROR = 'kimai-timesheet-87';
public const DISABLED_PROJECT_ERROR = 'kimai-timesheet-88';
public const DISABLED_CUSTOMER_ERROR = 'kimai-timesheet-89';
public const PROJECT_NOT_STARTED = 'kimai-timesheet-91';
public const PROJECT_ALREADY_ENDED = 'kimai-timesheet-92';
protected static $errorNames = [
self::MISSING_BEGIN_ERROR => 'You must submit a begin date.',
self::END_BEFORE_BEGIN_ERROR => 'End date must not be earlier then start date.',
self::MISSING_ACTIVITY_ERROR => 'An activity needs to be selected.',
self::MISSING_PROJECT_ERROR => 'A project needs to be selected.',
self::ACTIVITY_PROJECT_MISMATCH_ERROR => 'Project mismatch, project specific activity and timesheet project are different.',
self::DISABLED_ACTIVITY_ERROR => 'Cannot start a disabled activity.',
self::DISABLED_PROJECT_ERROR => 'Cannot start a disabled project.',
self::DISABLED_CUSTOMER_ERROR => 'Cannot start a disabled customer.',
self::PROJECT_NOT_STARTED => 'The project has not started at that time.',
self::PROJECT_ALREADY_ENDED => 'The project is finished at that time.',
];
public $message = 'This timesheet has invalid settings.';
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
}

View File

@@ -0,0 +1,172 @@
<?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\Validator\Constraints;
use App\Entity\Timesheet as TimesheetEntity;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
final class TimesheetBasicValidator extends ConstraintValidator
{
/**
* @param TimesheetEntity $timesheet
* @param Constraint $constraint
*/
public function validate($timesheet, Constraint $constraint)
{
if (!($constraint instanceof TimesheetBasic)) {
throw new UnexpectedTypeException($constraint, TimesheetBasic::class);
}
if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) {
throw new UnexpectedTypeException($timesheet, TimesheetEntity::class);
}
$this->validateBeginAndEnd($timesheet, $this->context);
$this->validateActivityAndProject($timesheet, $this->context);
}
/**
* @param TimesheetEntity $timesheet
* @param ExecutionContextInterface $context
*/
protected function validateBeginAndEnd(TimesheetEntity $timesheet, ExecutionContextInterface $context)
{
$begin = $timesheet->getBegin();
$end = $timesheet->getEnd();
if (null === $begin) {
$context->buildViolation('You must submit a begin date.')
->atPath('begin')
->setTranslationDomain('validators')
->setCode(TimesheetBasic::MISSING_BEGIN_ERROR)
->addViolation();
return;
}
if (null !== $end && $begin > $end) {
$context->buildViolation('End date must not be earlier then start date.')
->atPath('end')
->setTranslationDomain('validators')
->setCode(TimesheetBasic::END_BEFORE_BEGIN_ERROR)
->addViolation();
}
}
/**
* @param TimesheetEntity $timesheet
* @param ExecutionContextInterface $context
*/
protected function validateActivityAndProject(TimesheetEntity $timesheet, ExecutionContextInterface $context)
{
if (null === ($activity = $timesheet->getActivity())) {
$context->buildViolation('An activity needs to be selected.')
->atPath('activity')
->setTranslationDomain('validators')
->setCode(TimesheetBasic::MISSING_ACTIVITY_ERROR)
->addViolation();
}
if (null === ($project = $timesheet->getProject())) {
$context->buildViolation('A project needs to be selected.')
->atPath('project')
->setTranslationDomain('validators')
->setCode(TimesheetBasic::MISSING_PROJECT_ERROR)
->addViolation();
}
if (null === $activity || null === $project) {
return;
}
if (null !== $activity->getProject() && $activity->getProject() !== $project) {
$context->buildViolation('Project mismatch, project specific activity and timesheet project are different.')
->atPath('project')
->setTranslationDomain('validators')
->setCode(TimesheetBasic::ACTIVITY_PROJECT_MISMATCH_ERROR)
->addViolation();
}
$timesheetEnd = $timesheet->getEnd();
$newOrStarted = null === $timesheetEnd || $timesheet->getId() === null;
if ($newOrStarted && !$activity->isVisible()) {
$context->buildViolation('Cannot start a disabled activity.')
->atPath('activity')
->setTranslationDomain('validators')
->setCode(TimesheetBasic::DISABLED_ACTIVITY_ERROR)
->addViolation();
}
if ($newOrStarted && !$project->isVisible()) {
$context->buildViolation('Cannot start a disabled project.')
->atPath('project')
->setTranslationDomain('validators')
->setCode(TimesheetBasic::DISABLED_PROJECT_ERROR)
->addViolation();
}
if ($newOrStarted && !$project->getCustomer()->isVisible()) {
$context->buildViolation('Cannot start a disabled customer.')
->atPath('customer')
->setTranslationDomain('validators')
->setCode(TimesheetBasic::DISABLED_CUSTOMER_ERROR)
->addViolation();
}
$pathStart = 'begin';
$pathEnd = 'end';
$projectBegin = $project->getStart();
$projectEnd = $project->getEnd();
if (null === $projectBegin && null === $projectEnd) {
return;
}
$timesheetStart = $timesheet->getBegin();
$timesheetEnd = $timesheet->getEnd();
if (null !== $timesheetStart && $pathStart !== null) {
if (null !== $projectBegin && $timesheetStart->getTimestamp() < $projectBegin->getTimestamp()) {
$context->buildViolation('The project has not started at that time.')
->atPath($pathStart)
->setTranslationDomain('validators')
->setCode(TimesheetBasic::PROJECT_NOT_STARTED)
->addViolation();
} elseif (null !== $projectEnd && $timesheetStart->getTimestamp() > $projectEnd->getTimestamp()) {
$context->buildViolation('The project is finished at that time.')
->atPath($pathStart)
->setTranslationDomain('validators')
->setCode(TimesheetBasic::PROJECT_ALREADY_ENDED)
->addViolation();
}
}
if (null !== $timesheetEnd && $pathEnd !== null) {
if (null !== $projectEnd && $timesheetEnd->getTimestamp() > $projectEnd->getTimestamp()) {
$context->buildViolation('The project is finished at that time.')
->atPath($pathEnd)
->setTranslationDomain('validators')
->setCode(TimesheetBasic::PROJECT_ALREADY_ENDED)
->addViolation();
} elseif (null !== $projectBegin && $timesheetEnd->getTimestamp() < $projectBegin->getTimestamp()) {
$context->buildViolation('The project has not started at that time.')
->atPath($pathEnd)
->setTranslationDomain('validators')
->setCode(TimesheetBasic::PROJECT_NOT_STARTED)
->addViolation();
}
}
}
}

View File

@@ -76,6 +76,11 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
$duration = $timesheet->getEnd()->getTimestamp() - $timesheet->getBegin()->getTimestamp();
}
// this validator needs a project to calculate the rates
if ($timesheet->getProject() === null) {
return;
}
$timeRate = $this->rateService->calculate($timesheet);
$rate = $timeRate->getRate();

View 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\Validator\Constraints;
final class TimesheetExported extends TimesheetConstraint
{
public const TIMESHEET_EXPORTED = 'kimai-timesheet-exported-01';
protected static $errorNames = [
self::TIMESHEET_EXPORTED => 'This timesheet is already exported.',
];
public $message = 'This timesheet is already exported.';
/**
* @var \DateTime|string|null
*/
public $now;
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
}

View 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\Validator\Constraints;
use App\Entity\Timesheet as TimesheetEntity;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
final class TimesheetExportedValidator extends ConstraintValidator
{
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
/**
* @param TimesheetEntity $timesheet
* @param Constraint $constraint
*/
public function validate($timesheet, Constraint $constraint)
{
if (!($constraint instanceof TimesheetExported)) {
throw new UnexpectedTypeException($constraint, TimesheetExported::class);
}
if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) {
throw new UnexpectedTypeException($timesheet, TimesheetEntity::class);
}
if (!$timesheet->isExported()) {
return;
}
if (null !== $this->security->getUser() && $this->security->isGranted('edit_exported_timesheet')) {
return;
}
$this->context->buildViolation(TimesheetExported::getErrorName(TimesheetExported::TIMESHEET_EXPORTED))
->atPath('exported')
->setTranslationDomain('validators')
->setCode(TimesheetExported::TIMESHEET_EXPORTED)
->addViolation();
}
}

View File

@@ -12,12 +12,15 @@ namespace App\Validator\Constraints;
final class TimesheetLongRunning extends TimesheetConstraint
{
public const LONG_RUNNING = 'kimai-timesheet-long-running-01';
public const MAXIMUM = 'kimai-timesheet-long-running-02';
protected static $errorNames = [
self::LONG_RUNNING => 'TIMESHEET_LONG_RUNNING',
self::MAXIMUM => 'MAXIMUM',
];
public $message = 'Maximum duration of {{ value }} hours exceeded.';
public $maximumMessage = 'Maximum duration exceeded.';
public function getTargets()
{

View File

@@ -42,6 +42,18 @@ final class TimesheetLongRunningValidator extends ConstraintValidator
return;
}
// one year is currently the maximum that can be logged (which is already not logically)
// the database column could hold more data, but let's limit it here
if ($timesheet->getDuration() > 31536000) {
$this->context->buildViolation($constraint->maximumMessage)
->setTranslationDomain('validators')
->atPath('duration')
->setCode(TimesheetLongRunning::MAXIMUM)
->addViolation();
return;
}
$maxMinutes = $this->systemConfiguration->getTimesheetLongRunningDuration();
if ($maxMinutes <= 0) {

View File

@@ -28,7 +28,7 @@ class TimesheetMultiUpdate extends Constraint
protected static $errorNames = [
self::MISSING_ACTIVITY_ERROR => 'You need to choose an activity, if the project should be changed.',
self::MISSING_PROJECT_ERROR => 'A timesheet must have a project.',
self::MISSING_PROJECT_ERROR => 'A project needs to be selected.',
self::ACTIVITY_PROJECT_MISMATCH_ERROR => 'Project mismatch: chosen project does not match the activity project.',
self::DISABLED_ACTIVITY_ERROR => 'Cannot start a disabled activity.',
self::DISABLED_PROJECT_ERROR => 'Cannot start a disabled project.',

View File

@@ -13,7 +13,6 @@ use App\Entity\Timesheet as TimesheetEntity;
use App\Validator\Constraints\Timesheet as TimesheetConstraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
final class TimesheetValidator extends ConstraintValidator
@@ -45,10 +44,6 @@ final class TimesheetValidator extends ConstraintValidator
throw new UnexpectedTypeException($timesheet, TimesheetEntity::class);
}
$this->validateBeginAndEnd($timesheet, $this->context);
$this->validateActivityAndProject($timesheet, $this->context);
$this->validateActiveLimit($timesheet, $this->context);
foreach ($this->constraints as $constraint) {
$this->context
->getValidator()
@@ -56,142 +51,4 @@ final class TimesheetValidator extends ConstraintValidator
->validate($timesheet, $constraint, [Constraint::DEFAULT_GROUP]);
}
}
/**
* @param TimesheetEntity $timesheet
* @param ExecutionContextInterface $context
*/
protected function validateActiveLimit(TimesheetEntity $timesheet, ExecutionContextInterface $context)
{
// TODO check active entries against hard_limit
}
/**
* @param TimesheetEntity $timesheet
* @param ExecutionContextInterface $context
*/
protected function validateBeginAndEnd(TimesheetEntity $timesheet, ExecutionContextInterface $context)
{
$begin = $timesheet->getBegin();
$end = $timesheet->getEnd();
if (null === $begin) {
$context->buildViolation('You must submit a begin date.')
->atPath('begin')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::MISSING_BEGIN_ERROR)
->addViolation();
return;
}
if (null !== $end && $begin > $end) {
$context->buildViolation('End date must not be earlier then start date.')
->atPath('end')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::END_BEFORE_BEGIN_ERROR)
->addViolation();
}
}
/**
* @param TimesheetEntity $timesheet
* @param ExecutionContextInterface $context
*/
protected function validateActivityAndProject(TimesheetEntity $timesheet, ExecutionContextInterface $context)
{
if (null === ($activity = $timesheet->getActivity())) {
$context->buildViolation('A timesheet must have an activity.')
->atPath('activity')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::MISSING_ACTIVITY_ERROR)
->addViolation();
}
if (null === ($project = $timesheet->getProject())) {
$context->buildViolation('A timesheet must have a project.')
->atPath('project')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::MISSING_PROJECT_ERROR)
->addViolation();
}
if (null === $activity || null === $project) {
return;
}
if (null !== $activity->getProject() && $activity->getProject() !== $project) {
$context->buildViolation('Project mismatch, project specific activity and timesheet project are different.')
->atPath('project')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::ACTIVITY_PROJECT_MISMATCH_ERROR)
->addViolation();
}
$timesheetEnd = $timesheet->getEnd();
if (null === $timesheetEnd && !$activity->isVisible()) {
$context->buildViolation('Cannot start a disabled activity.')
->atPath('activity')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::DISABLED_ACTIVITY_ERROR)
->addViolation();
}
if (null === $timesheetEnd && !$project->isVisible()) {
$context->buildViolation('Cannot start a disabled project.')
->atPath('project')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::DISABLED_PROJECT_ERROR)
->addViolation();
}
if (null === $timesheetEnd && !$project->getCustomer()->isVisible()) {
$context->buildViolation('Cannot start a disabled customer.')
->atPath('customer')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::DISABLED_CUSTOMER_ERROR)
->addViolation();
}
$projectBegin = $project->getStart();
$projectEnd = $project->getEnd();
if (null !== $projectBegin || null !== $projectEnd) {
$timesheetStart = $timesheet->getBegin();
$timesheetEnd = $timesheet->getEnd();
if (null !== $timesheetStart) {
if (null !== $projectBegin && $timesheetStart->getTimestamp() < $projectBegin->getTimestamp()) {
$context->buildViolation('The project has not started at that time.')
->atPath('begin')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::PROJECT_NOT_STARTED)
->addViolation();
} elseif (null !== $projectEnd && $timesheetStart->getTimestamp() > $projectEnd->getTimestamp()) {
$context->buildViolation('The project is finished at that time.')
->atPath('begin')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::PROJECT_ALREADY_ENDED)
->addViolation();
}
}
if (null !== $timesheetEnd) {
if (null !== $projectEnd && $timesheetEnd->getTimestamp() > $projectEnd->getTimestamp()) {
$context->buildViolation('The project is finished at that time.')
->atPath('end')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::PROJECT_ALREADY_ENDED)
->addViolation();
} elseif (null !== $projectBegin && $timesheetEnd->getTimestamp() < $projectBegin->getTimestamp()) {
$context->buildViolation('The project has not started at that time.')
->atPath('end')
->setTranslationDomain('validators')
->setCode(TimesheetConstraint::PROJECT_NOT_STARTED)
->addViolation();
}
}
}
}
}