added drag and drop for new records via calendar (#1962)
This commit is contained in:
@@ -17,6 +17,8 @@ use App\Event\TimesheetCreatePreEvent;
|
||||
use App\Event\TimesheetDeleteMultiplePreEvent;
|
||||
use App\Event\TimesheetDeletePreEvent;
|
||||
use App\Event\TimesheetMetaDefinitionEvent;
|
||||
use App\Event\TimesheetRestartPostEvent;
|
||||
use App\Event\TimesheetRestartPreEvent;
|
||||
use App\Event\TimesheetStopPostEvent;
|
||||
use App\Event\TimesheetStopPreEvent;
|
||||
use App\Event\TimesheetUpdateMultiplePostEvent;
|
||||
@@ -24,10 +26,14 @@ use App\Event\TimesheetUpdateMultiplePreEvent;
|
||||
use App\Event\TimesheetUpdatePostEvent;
|
||||
use App\Event\TimesheetUpdatePreEvent;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Validator\ValidationException;
|
||||
use App\Validator\ValidationFailedException;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
final class TimesheetService
|
||||
{
|
||||
@@ -51,19 +57,25 @@ final class TimesheetService
|
||||
* @var AuthorizationCheckerInterface
|
||||
*/
|
||||
private $auth;
|
||||
/**
|
||||
* @var ValidatorInterface
|
||||
*/
|
||||
private $validator;
|
||||
|
||||
public function __construct(
|
||||
TimesheetConfiguration $configuration,
|
||||
TimesheetRepository $repository,
|
||||
TrackingModeService $service,
|
||||
EventDispatcherInterface $dispatcher,
|
||||
AuthorizationCheckerInterface $security
|
||||
AuthorizationCheckerInterface $security,
|
||||
ValidatorInterface $validator
|
||||
) {
|
||||
$this->configuration = $configuration;
|
||||
$this->repository = $repository;
|
||||
$this->trackingModeService = $service;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->auth = $security;
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +100,7 @@ final class TimesheetService
|
||||
public function prepareNewTimesheet(Timesheet $timesheet, ?Request $request = null): Timesheet
|
||||
{
|
||||
if (null !== $timesheet->getId()) {
|
||||
throw new \InvalidArgumentException('Cannot prepare timesheet, already persisted');
|
||||
throw new InvalidArgumentException('Cannot prepare timesheet, already persisted');
|
||||
}
|
||||
|
||||
$event = new TimesheetMetaDefinitionEvent($timesheet);
|
||||
@@ -100,25 +112,73 @@ final class TimesheetService
|
||||
return $timesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Timesheet $timesheet
|
||||
* @param Timesheet $copyFrom
|
||||
* @throws ValidationFailedException for invalid timesheets or running timesheets that should be stopped
|
||||
* @throws InvalidArgumentException for already persisted timesheets
|
||||
* @throws AccessDeniedHttpException if user is not allowed to start timesheet
|
||||
*/
|
||||
public function restartTimesheet(Timesheet $timesheet, Timesheet $copyFrom): Timesheet
|
||||
{
|
||||
$this->dispatcher->dispatch(new TimesheetRestartPreEvent($timesheet, $copyFrom));
|
||||
$this->saveNewTimesheet($timesheet);
|
||||
$this->dispatcher->dispatch(new TimesheetRestartPostEvent($timesheet, $copyFrom));
|
||||
|
||||
return $timesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Timesheet $timesheet
|
||||
* @return Timesheet
|
||||
* @throws ValidationFailedException for invalid timesheets or running timesheets that should be stopped
|
||||
* @throws InvalidArgumentException for already persisted timesheets
|
||||
* @throws AccessDeniedHttpException if user is not allowed to start timesheet
|
||||
*/
|
||||
public function saveNewTimesheet(Timesheet $timesheet): Timesheet
|
||||
{
|
||||
if (null !== $timesheet->getId()) {
|
||||
throw new \InvalidArgumentException('Cannot create timesheet, already persisted');
|
||||
throw new InvalidArgumentException('Cannot create timesheet, already persisted');
|
||||
}
|
||||
|
||||
if (null === $timesheet->getEnd() && !$this->auth->isGranted('start', $timesheet)) {
|
||||
throw new AccessDeniedHttpException('You are not allowed to start this timesheet record');
|
||||
}
|
||||
|
||||
$this->validateTimesheet($timesheet);
|
||||
|
||||
try {
|
||||
$this->stopActiveEntries($timesheet);
|
||||
} catch (ValidationFailedException $vex) {
|
||||
// could happen for timesheets that were started in the future (end before begin)
|
||||
throw new ValidationFailedException($vex->getViolations(), 'Cannot stop running timesheet');
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(new TimesheetCreatePreEvent($timesheet));
|
||||
$this->repository->add($timesheet, $this->configuration->getActiveEntriesHardLimit());
|
||||
$this->repository->save($timesheet);
|
||||
$this->dispatcher->dispatch(new TimesheetCreatePostEvent($timesheet));
|
||||
|
||||
return $timesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does NOT validate the given timesheet!
|
||||
*
|
||||
* @param Timesheet $timesheet
|
||||
* @return Timesheet
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function updateTimesheet(Timesheet $timesheet): Timesheet
|
||||
{
|
||||
// there is at least one edge case which leads to a problem:
|
||||
// if you do not allow overlapping entries, you cannot restart a timesheet by removing the
|
||||
// end date if another timesheet is running, because the check for existing timesheets will always trigger
|
||||
/*
|
||||
if ($timesheet->getEnd() === null) {
|
||||
$this->stopActiveEntries($timesheet);
|
||||
}
|
||||
*/
|
||||
|
||||
$this->dispatcher->dispatch(new TimesheetUpdatePreEvent($timesheet));
|
||||
$this->repository->save($timesheet);
|
||||
$this->dispatcher->dispatch(new TimesheetUpdatePostEvent($timesheet));
|
||||
@@ -126,6 +186,13 @@ final class TimesheetService
|
||||
return $timesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does NOT validate the given timesheet!
|
||||
*
|
||||
* @param array $timesheets
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function updateMultipleTimesheets(array $timesheets): array
|
||||
{
|
||||
$this->dispatcher->dispatch(new TimesheetUpdateMultiplePreEvent($timesheets));
|
||||
@@ -135,10 +202,30 @@ final class TimesheetService
|
||||
return $timesheets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given timesheet, especially important for not setting a wrong end date.
|
||||
* But also to check that all required data is set.
|
||||
*
|
||||
* @param Timesheet $timesheet
|
||||
* @throws ValidationException for already stopped timesheets
|
||||
* @throws ValidationFailedException
|
||||
*/
|
||||
public function stopTimesheet(Timesheet $timesheet): void
|
||||
{
|
||||
if (null !== $timesheet->getEnd()) {
|
||||
throw new ValidationException('Timesheet entry already stopped');
|
||||
}
|
||||
|
||||
$begin = clone $timesheet->getBegin();
|
||||
$now = new \DateTime('now', $begin->getTimezone());
|
||||
|
||||
$timesheet->setBegin($begin);
|
||||
$timesheet->setEnd($now);
|
||||
|
||||
$this->validateTimesheet($timesheet);
|
||||
|
||||
$this->dispatcher->dispatch(new TimesheetStopPreEvent($timesheet));
|
||||
$this->repository->stopRecording($timesheet);
|
||||
$this->repository->save($timesheet);
|
||||
$this->dispatcher->dispatch(new TimesheetStopPostEvent($timesheet));
|
||||
}
|
||||
|
||||
@@ -153,4 +240,56 @@ final class TimesheetService
|
||||
$this->dispatcher->dispatch(new TimesheetDeleteMultiplePreEvent($timesheets));
|
||||
$this->repository->deleteMultiple($timesheets);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Timesheet $timesheet
|
||||
* @param string[] $groups
|
||||
* @throws ValidationFailedException
|
||||
*/
|
||||
private function validateTimesheet(Timesheet $timesheet, array $groups = []): void
|
||||
{
|
||||
$errors = $this->validator->validate($timesheet, null, $groups);
|
||||
|
||||
if ($errors->count() > 0) {
|
||||
throw new ValidationFailedException($errors, 'Validation Failed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all active records for the current user, besides the given $timesheet.
|
||||
*
|
||||
* @param Timesheet $timesheet
|
||||
* @return int
|
||||
* @throws ValidationException
|
||||
* @throws ValidationFailedException
|
||||
*/
|
||||
private function stopActiveEntries(Timesheet $timesheet): int
|
||||
{
|
||||
$user = $timesheet->getUser();
|
||||
$hardLimit = $this->configuration->getActiveEntriesHardLimit();
|
||||
$activeEntries = $this->repository->getActiveEntries($user);
|
||||
$counter = 0;
|
||||
|
||||
// reduce limit by one:
|
||||
// this method is only called when a new entry is started
|
||||
// -> all entries, including the new one must not exceed the $limit
|
||||
$limit = $hardLimit - 1;
|
||||
|
||||
if (\count($activeEntries) > $limit) {
|
||||
$i = 1;
|
||||
foreach ($activeEntries as $activeEntry) {
|
||||
if ($i > $limit && $timesheet->getId() !== $activeEntry->getId()) {
|
||||
if ($hardLimit > 1) {
|
||||
throw new ValidationException('timesheet.start.exceeded_limit');
|
||||
}
|
||||
|
||||
$this->stopTimesheet($activeEntry);
|
||||
$counter++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
return $counter;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user