diff --git a/src/Controller/Auth/SamlController.php b/src/Controller/Auth/SamlController.php index efceb7e4..682796ce 100644 --- a/src/Controller/Auth/SamlController.php +++ b/src/Controller/Auth/SamlController.php @@ -43,17 +43,20 @@ final class SamlController extends AbstractController $session = $request->getSession(); $authErrorKey = Security::AUTHENTICATION_ERROR; + $error = null; + if ($request->attributes->has($authErrorKey)) { $error = $request->attributes->get($authErrorKey); } elseif (null !== $session && $session->has($authErrorKey)) { $error = $session->get($authErrorKey); $session->remove($authErrorKey); - } else { - $error = null; } if ($error) { - throw new \RuntimeException($error->getMessage()); + if (\is_object($error) && method_exists($error, 'getMessage')) { + $error = $error->getMessage(); + } + throw new \RuntimeException($error); } $this->oneLoginAuth->login($session->get('_security.main.target_path')); diff --git a/src/Controller/Reporting/ReportByUserController.php b/src/Controller/Reporting/ReportByUserController.php new file mode 100644 index 00000000..dd545969 --- /dev/null +++ b/src/Controller/Reporting/ReportByUserController.php @@ -0,0 +1,230 @@ +timesheetRepository = $timesheetRepository; + } + + private function canSelectUser(): bool + { + // also found in App\EventSubscriber\Actions\UserSubscriber + if (!$this->isGranted('view_other_timesheet')) { + return false; + } + + return true; + } + + /** + * @Route(path="/month_by_user", name="report_user_month", methods={"GET","POST"}) + * + * @param Request $request + * @return Response + * @throws Exception + */ + public function monthByUser(Request $request): Response + { + $currentUser = $this->getUser(); + $dateTimeFactory = $this->getDateTimeFactory($currentUser); + $localeFormats = $this->getLocaleFormats($request->getLocale()); + $canChangeUser = $this->canSelectUser(); + + $values = new MonthByUser(); + $values->setUser($currentUser); + $values->setDate($dateTimeFactory->getStartOfMonth()); + + $form = $this->createForm(MonthByUserForm::class, $values, [ + 'include_user' => $canChangeUser, + 'timezone' => $dateTimeFactory->getTimezone()->getName(), + 'start_date' => $values->getDate(), + 'format' => $localeFormats->getDateTypeFormat(), + ]); + + $form->submit($request->query->all(), false); + + if ($values->getUser() === null) { + $values->setUser($currentUser); + } + + if ($currentUser !== $values->getUser() && !$canChangeUser) { + throw new AccessDeniedException('User is not allowed to see other users timesheet'); + } + + if ($values->getDate() === null) { + $values->setDate($dateTimeFactory->getStartOfMonth()); + } + + $start = $values->getDate(); + $start->modify('first day of 00:00:00'); + + $end = clone $start; + $end->modify('last day of 23:59:59'); + + $selectedUser = $values->getUser(); + + $previousMonth = clone $start; + $previousMonth->modify('-1 month'); + + $nextMonth = clone $start; + $nextMonth->modify('+1 month'); + + $data = $this->timesheetRepository->getDailyStats($selectedUser, $start, $end); + $rows = $this->prepareReportData($data); + + return $this->render('reporting/report_by_user.html.twig', [ + 'report_title' => 'report_user_month', + 'box_id' => 'user-month-reporting-box', + 'form' => $form->createView(), + 'days' => $data, + 'rows' => $rows, + 'user' => $selectedUser, + 'current' => $start, + 'next' => $nextMonth, + 'previous' => $previousMonth, + ]); + } + + /** + * @Route(path="/week_by_user", name="report_user_week", methods={"GET","POST"}) + * + * @param Request $request + * @return Response + * @throws Exception + */ + public function weekByUser(Request $request): Response + { + $currentUser = $this->getUser(); + $dateTimeFactory = $this->getDateTimeFactory($currentUser); + $localeFormats = $this->getLocaleFormats($request->getLocale()); + $canChangeUser = $this->canSelectUser(); + + $values = new WeekByUser(); + $values->setUser($currentUser); + $values->setDate($dateTimeFactory->getStartOfWeek()); + + $form = $this->createForm(WeekByUserForm::class, $values, [ + 'include_user' => $canChangeUser, + 'timezone' => $dateTimeFactory->getTimezone()->getName(), + 'start_date' => $values->getDate(), + 'format' => $localeFormats->getDateTypeFormat(), + ]); + + $form->submit($request->query->all(), false); + + if ($values->getUser() === null) { + $values->setUser($currentUser); + } + + if ($currentUser !== $values->getUser() && !$canChangeUser) { + throw new AccessDeniedException('User is not allowed to see other users timesheet'); + } + + if ($values->getDate() === null) { + $values->setDate($dateTimeFactory->getStartOfWeek()); + } + + $start = $dateTimeFactory->getStartOfWeek($values->getDate()); + $end = $dateTimeFactory->getEndOfWeek($values->getDate()); + + $selectedUser = $values->getUser(); + + $previous = clone $start; + $previous->modify('-1 week'); + + $next = clone $start; + $next->modify('+1 week'); + + $data = $this->timesheetRepository->getDailyStats($selectedUser, $start, $end); + $rows = $this->prepareReportData($data); + + return $this->render('reporting/report_by_user.html.twig', [ + 'report_title' => 'report_user_week', + 'box_id' => 'user-week-reporting-box', + 'form' => $form->createView(), + 'days' => $data, + 'rows' => $rows, + 'user' => $selectedUser, + 'current' => $start, + 'next' => $next, + 'previous' => $previous, + ]); + } + + private function prepareReportData(array $data): array + { + $days = []; + + foreach ($data as $day) { + $days[$day->getDay()->format('Ymd')] = ['date' => $day->getDay(), 'duration' => 0]; + } + + $rows = []; + + /** @var Day $day */ + foreach ($data as $day) { + $dayId = $day->getDay()->format('Ymd'); + foreach ($day->getDetails() as $id => $detail) { + $projectId = $detail['project']->getId(); + if (!\array_key_exists($projectId, $rows)) { + $rows[$projectId] = [ + 'project' => $detail['project'], + 'duration' => 0, + 'days' => $days, + 'activities' => [], + ]; + } + + $rows[$projectId]['duration'] += $detail['duration']; + $rows[$projectId]['days'][$dayId]['duration'] += $detail['duration']; + + $activityId = $detail['activity']->getId(); + if (!\array_key_exists($activityId, $rows[$projectId]['activities'])) { + $rows[$projectId]['activities'][$activityId] = [ + 'activity' => $detail['activity'], + 'duration' => 0, + 'days' => $days, + ]; + } + + $rows[$projectId]['activities'][$activityId]['duration'] += $detail['duration']; + $rows[$projectId]['activities'][$activityId]['days'][$dayId]['duration'] += $detail['duration']; + } + } + + return $rows; + } +} diff --git a/src/Controller/Reporting/ReportUsersListController.php b/src/Controller/Reporting/ReportUsersListController.php new file mode 100644 index 00000000..9acb92d9 --- /dev/null +++ b/src/Controller/Reporting/ReportUsersListController.php @@ -0,0 +1,200 @@ +timesheetRepository = $timesheetRepository; + $this->userRepository = $userRepository; + } + + /** + * @Route(path="/monthly_users_list", name="report_monthly_users", methods={"GET","POST"}) + * + * @param Request $request + * @return Response + * @throws Exception + */ + public function monthlyUsersList(Request $request): Response + { + $currentUser = $this->getUser(); + $dateTimeFactory = $this->getDateTimeFactory(); + $localeFormats = $this->getLocaleFormats($request->getLocale()); + + $query = new UserQuery(); + $query->setCurrentUser($currentUser); + $allUsers = $this->userRepository->getUsersForQuery($query); + + $rows = []; + + $values = new MonthlyUserList(); + $values->setDate($dateTimeFactory->getStartOfMonth()); + + $form = $this->createForm(MonthlyUserListForm::class, $values, [ + 'timezone' => $dateTimeFactory->getTimezone()->getName(), + 'start_date' => $values->getDate(), + 'format' => $localeFormats->getDateTypeFormat(), + ]); + + $form->submit($request->query->all(), false); + + if ($form->isSubmitted() && !$form->isValid()) { + $values->setDate($dateTimeFactory->getStartOfMonth()); + } + + if ($values->getDate() === null) { + $values->setDate($dateTimeFactory->getStartOfMonth()); + } + + $start = $values->getDate(); + $start->modify('first day of 00:00:00'); + + $end = clone $start; + $end->modify('last day of 23:59:59'); + + $previousMonth = clone $start; + $previousMonth->modify('-1 month'); + + $nextMonth = clone $start; + $nextMonth->modify('+1 month'); + + foreach ($allUsers as $user) { + $rows[] = [ + 'days' => $this->timesheetRepository->getDailyStats($user, $start, $end), + 'user' => $user + ]; + } + + $days = []; + + if (isset($rows[0])) { + /** @var Day $day */ + foreach ($rows[0]['days'] as $day) { + $days[$day->getDay()->format('Ymd')] = $day->getDay(); + } + } + + return $this->render('reporting/report_user_list.html.twig', [ + 'report_title' => 'report_monthly_users', + 'box_id' => 'monthly-user-list-reporting-box', + 'form' => $form->createView(), + 'rows' => $rows, + 'days' => $days, + 'current' => $start, + 'next' => $nextMonth, + 'previous' => $previousMonth, + ]); + } + + /** + * @Route(path="/weekly_users_list", name="report_weekly_users", methods={"GET","POST"}) + * + * @param Request $request + * @return Response + * @throws Exception + */ + public function weeklyUsersList(Request $request): Response + { + $currentUser = $this->getUser(); + $dateTimeFactory = $this->getDateTimeFactory(); + $localeFormats = $this->getLocaleFormats($request->getLocale()); + + $query = new UserQuery(); + $query->setCurrentUser($currentUser); + $allUsers = $this->userRepository->getUsersForQuery($query); + + $rows = []; + + $values = new WeeklyUserList(); + $values->setDate($dateTimeFactory->getStartOfWeek()); + + $form = $this->createForm(WeeklyUserListForm::class, $values, [ + 'timezone' => $dateTimeFactory->getTimezone()->getName(), + 'start_date' => $values->getDate(), + 'format' => $localeFormats->getDateTypeFormat(), + ]); + + $form->submit($request->query->all(), false); + + if ($form->isSubmitted() && !$form->isValid()) { + $values->setDate($dateTimeFactory->getStartOfWeek()); + } + + if ($values->getDate() === null) { + $values->setDate($dateTimeFactory->getStartOfWeek()); + } + + $start = $dateTimeFactory->getStartOfWeek($values->getDate()); + $end = $dateTimeFactory->getEndOfWeek($values->getDate()); + + $previousWeek = clone $start; + $previousWeek->modify('-1 week'); + + $nextWeek = clone $start; + $nextWeek->modify('+1 week'); + + foreach ($allUsers as $user) { + $rows[] = [ + 'days' => $this->timesheetRepository->getDailyStats($user, $start, $end), + 'user' => $user + ]; + } + + $days = []; + + if (isset($rows[0])) { + /** @var Day $day */ + foreach ($rows[0]['days'] as $day) { + $days[$day->getDay()->format('Ymd')] = $day->getDay(); + } + } + + return $this->render('reporting/report_user_list.html.twig', [ + 'report_title' => 'report_weekly_users', + 'box_id' => 'weekly-user-list-reporting-box', + 'form' => $form->createView(), + 'rows' => $rows, + 'days' => $days, + 'current' => $start, + 'next' => $nextWeek, + 'previous' => $previousWeek, + ]); + } +} diff --git a/src/Controller/ReportingController.php b/src/Controller/ReportingController.php index 97c7fb4e..a1c73e61 100644 --- a/src/Controller/ReportingController.php +++ b/src/Controller/ReportingController.php @@ -9,23 +9,10 @@ namespace App\Controller; -use App\Model\Statistic\Day; -use App\Reporting\MonthByUser; -use App\Reporting\MonthByUserForm; -use App\Reporting\MonthlyUserList; -use App\Reporting\MonthlyUserListForm; use App\Reporting\ReportingService; -use App\Reporting\WeekByUser; -use App\Reporting\WeekByUserForm; -use App\Repository\Query\UserQuery; -use App\Repository\TimesheetRepository; -use App\Repository\UserRepository; -use Exception; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; -use Symfony\Component\Security\Core\Exception\AccessDeniedException; /** * Controller used to render reports. @@ -35,21 +22,6 @@ use Symfony\Component\Security\Core\Exception\AccessDeniedException; */ final class ReportingController extends AbstractController { - /** - * @var TimesheetRepository - */ - private $timesheetRepository; - /** - * @var UserRepository - */ - private $userRepository; - - public function __construct(TimesheetRepository $timesheetRepository, UserRepository $userRepository) - { - $this->timesheetRepository = $timesheetRepository; - $this->userRepository = $userRepository; - } - /** * @Route(path="/", name="reporting", methods={"GET"}) * @@ -83,266 +55,4 @@ final class ReportingController extends AbstractController return $this->redirectToRoute($route); } - - private function canSelectUser(): bool - { - // also found in App\EventSubscriber\Actions\UserSubscriber - if (!$this->isGranted('view_other_timesheet')) { - return false; - } - - return true; - } - - /** - * @Route(path="/month_by_user", name="report_user_month", methods={"GET","POST"}) - * - * @param Request $request - * @return Response - * @throws Exception - */ - public function monthByUser(Request $request): Response - { - $currentUser = $this->getUser(); - $dateTimeFactory = $this->getDateTimeFactory($currentUser); - $localeFormats = $this->getLocaleFormats($request->getLocale()); - $canChangeUser = $this->canSelectUser(); - - $values = new MonthByUser(); - $values->setUser($currentUser); - $values->setDate($dateTimeFactory->getStartOfMonth()); - - $form = $this->createForm(MonthByUserForm::class, $values, [ - 'include_user' => $canChangeUser, - 'timezone' => $dateTimeFactory->getTimezone()->getName(), - 'start_date' => $values->getDate(), - 'format' => $localeFormats->getDateTypeFormat(), - ]); - - $form->submit($request->query->all(), false); - - if ($values->getUser() === null) { - $values->setUser($currentUser); - } - - if ($currentUser !== $values->getUser() && !$canChangeUser) { - throw new AccessDeniedException('User is not allowed to see other users timesheet'); - } - - if ($values->getDate() === null) { - $values->setDate($dateTimeFactory->getStartOfMonth()); - } - - $start = $values->getDate(); - $start->modify('first day of 00:00:00'); - - $end = clone $start; - $end->modify('last day of 23:59:59'); - - $selectedUser = $values->getUser(); - - $previousMonth = clone $start; - $previousMonth->modify('-1 month'); - - $nextMonth = clone $start; - $nextMonth->modify('+1 month'); - - $data = $this->timesheetRepository->getDailyStats($selectedUser, $start, $end); - $rows = $this->prepareMonthlyData($data); - - return $this->render('reporting/month_by_user.html.twig', [ - 'form' => $form->createView(), - 'days' => $data, - 'rows' => $rows, - 'user' => $selectedUser, - 'current' => $start, - 'next' => $nextMonth, - 'previous' => $previousMonth, - ]); - } - - /** - * @Route(path="/week_by_user", name="report_user_week", methods={"GET","POST"}) - * - * @param Request $request - * @return Response - * @throws Exception - */ - public function weekByUser(Request $request): Response - { - $currentUser = $this->getUser(); - $dateTimeFactory = $this->getDateTimeFactory($currentUser); - $localeFormats = $this->getLocaleFormats($request->getLocale()); - $canChangeUser = $this->canSelectUser(); - - $values = new WeekByUser(); - $values->setUser($currentUser); - $values->setDate($dateTimeFactory->getStartOfWeek()); - - $form = $this->createForm(WeekByUserForm::class, $values, [ - 'include_user' => $canChangeUser, - 'timezone' => $dateTimeFactory->getTimezone()->getName(), - 'start_date' => $values->getDate(), - 'format' => $localeFormats->getDateTypeFormat(), - ]); - - $form->submit($request->query->all(), false); - - if ($values->getUser() === null) { - $values->setUser($currentUser); - } - - if ($currentUser !== $values->getUser() && !$canChangeUser) { - throw new AccessDeniedException('User is not allowed to see other users timesheet'); - } - - if ($values->getDate() === null) { - $values->setDate($dateTimeFactory->getStartOfWeek()); - } - - $start = $dateTimeFactory->getStartOfWeek($values->getDate()); - $end = $dateTimeFactory->getEndOfWeek($values->getDate()); - - $selectedUser = $values->getUser(); - - $previous = clone $start; - $previous->modify('-1 week'); - - $next = clone $start; - $next->modify('+1 week'); - - $data = $this->timesheetRepository->getDailyStats($selectedUser, $start, $end); - $rows = $this->prepareMonthlyData($data); - - return $this->render('reporting/week_by_user.html.twig', [ - 'form' => $form->createView(), - 'days' => $data, - 'rows' => $rows, - 'user' => $selectedUser, - 'current' => $start, - 'next' => $next, - 'previous' => $previous, - ]); - } - - /** - * @Route(path="/monthly_users_list", name="report_monthly_users", methods={"GET","POST"}) - * @Security("is_granted('view_other_timesheet')") - * - * @param Request $request - * @return Response - * @throws Exception - */ - public function monthlyUsersList(Request $request): Response - { - $currentUser = $this->getUser(); - $dateTimeFactory = $this->getDateTimeFactory(); - $localeFormats = $this->getLocaleFormats($request->getLocale()); - - $query = new UserQuery(); - $query->setCurrentUser($currentUser); - $allUsers = $this->userRepository->getUsersForQuery($query); - - $rows = []; - - $values = new MonthlyUserList(); - $values->setDate($dateTimeFactory->getStartOfMonth()); - - $form = $this->createForm(MonthlyUserListForm::class, $values, [ - 'timezone' => $dateTimeFactory->getTimezone()->getName(), - 'start_date' => $values->getDate(), - 'format' => $localeFormats->getDateTypeFormat(), - ]); - - $form->submit($request->query->all(), false); - - if ($form->isSubmitted() && !$form->isValid()) { - $values->setDate($dateTimeFactory->getStartOfMonth()); - } - - if ($values->getDate() === null) { - $values->setDate($dateTimeFactory->getStartOfMonth()); - } - - $start = $values->getDate(); - $start->modify('first day of 00:00:00'); - - $end = clone $start; - $end->modify('last day of 23:59:59'); - - $previousMonth = clone $start; - $previousMonth->modify('-1 month'); - - $nextMonth = clone $start; - $nextMonth->modify('+1 month'); - - foreach ($allUsers as $user) { - $rows[] = [ - 'days' => $this->timesheetRepository->getDailyStats($user, $start, $end), - 'user' => $user - ]; - } - - $days = []; - - if (isset($rows[0])) { - /** @var Day $day */ - foreach ($rows[0]['days'] as $day) { - $days[$day->getDay()->format('Ymd')] = $day->getDay(); - } - } - - return $this->render('reporting/monthly_user_list.html.twig', [ - 'form' => $form->createView(), - 'rows' => $rows, - 'days' => $days, - 'current' => $start, - 'next' => $nextMonth, - 'previous' => $previousMonth, - ]); - } - - private function prepareMonthlyData(array $data): array - { - $days = []; - - foreach ($data as $day) { - $days[$day->getDay()->format('Ymd')] = ['date' => $day->getDay(), 'duration' => 0]; - } - - $rows = []; - - /** @var Day $day */ - foreach ($data as $day) { - $dayId = $day->getDay()->format('Ymd'); - foreach ($day->getDetails() as $id => $detail) { - $projectId = $detail['project']->getId(); - if (!\array_key_exists($projectId, $rows)) { - $rows[$projectId] = [ - 'project' => $detail['project'], - 'duration' => 0, - 'days' => $days, - 'activities' => [], - ]; - } - - $rows[$projectId]['duration'] += $detail['duration']; - $rows[$projectId]['days'][$dayId]['duration'] += $detail['duration']; - - $activityId = $detail['activity']->getId(); - if (!\array_key_exists($activityId, $rows[$projectId]['activities'])) { - $rows[$projectId]['activities'][$activityId] = [ - 'activity' => $detail['activity'], - 'duration' => 0, - 'days' => $days, - ]; - } - - $rows[$projectId]['activities'][$activityId]['duration'] += $detail['duration']; - $rows[$projectId]['activities'][$activityId]['days'][$dayId]['duration'] += $detail['duration']; - } - } - - return $rows; - } } diff --git a/src/EventSubscriber/Actions/ReportingSubscriber.php b/src/EventSubscriber/Actions/ReportingSubscriber.php index 43458c9e..d50e1bee 100644 --- a/src/EventSubscriber/Actions/ReportingSubscriber.php +++ b/src/EventSubscriber/Actions/ReportingSubscriber.php @@ -10,6 +10,7 @@ namespace App\EventSubscriber\Actions; use App\Event\PageActionsEvent; +use App\Reporting\Report; use App\Reporting\ReportingService; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; @@ -34,7 +35,11 @@ class ReportingSubscriber extends AbstractActionsSubscriber $reports = $this->reportingService->getAvailableReports($event->getUser()); foreach ($reports as $report) { - $event->addActionToSubmenu('reporting', $report->getId(), ['title' => $report->getLabel(), 'translation_domain' => 'reporting', 'url' => $this->path($report->getRoute()), 'class' => 'toolbar-action report-' . $report->getId()]); + $subMenu = 'reporting'; + if ($report instanceof Report) { + $subMenu = $report->getReportIcon(); + } + $event->addActionToSubmenu($subMenu, $report->getId(), ['title' => $report->getLabel(), 'translation_domain' => 'reporting', 'url' => $this->path($report->getRoute()), 'class' => 'toolbar-action report-' . $report->getId()]); } $event->addHelp($this->documentationLink('reporting.html')); diff --git a/src/EventSubscriber/MenuSubscriber.php b/src/EventSubscriber/MenuSubscriber.php index 8223b902..63d0c25f 100644 --- a/src/EventSubscriber/MenuSubscriber.php +++ b/src/EventSubscriber/MenuSubscriber.php @@ -95,7 +95,7 @@ final class MenuSubscriber implements EventSubscriberInterface if ($auth->isGranted('view_reporting')) { $reporting = new MenuItemModel('reporting', 'menu.reporting', 'reporting', [], $this->getIcon('reporting')); - $reporting->setChildRoutes(['report_user_week', 'report_user_month', 'report_monthly_users']); + $reporting->setChildRoutes(['report_user_week', 'report_user_month', 'report_weekly_users', 'report_monthly_users', 'report_project_view']); $menu->addChild($reporting); } diff --git a/src/Form/Toolbar/AbstractToolbarForm.php b/src/Form/Toolbar/AbstractToolbarForm.php index b958d492..ef094fe4 100644 --- a/src/Form/Toolbar/AbstractToolbarForm.php +++ b/src/Form/Toolbar/AbstractToolbarForm.php @@ -316,7 +316,8 @@ abstract class AbstractToolbarForm extends AbstractType 'choices' => [ 'label.asc' => BaseQuery::ORDER_ASC, 'label.desc' => BaseQuery::ORDER_DESC - ] + ], + 'search' => false, ]); } @@ -340,7 +341,8 @@ abstract class AbstractToolbarForm extends AbstractType } $builder->add('orderBy', ChoiceType::class, [ 'label' => 'label.orderBy', - 'choices' => $all + 'choices' => $all, + 'search' => false, ]); } diff --git a/src/Reporting/AbstractUserList.php b/src/Reporting/AbstractUserList.php new file mode 100644 index 00000000..45294889 --- /dev/null +++ b/src/Reporting/AbstractUserList.php @@ -0,0 +1,28 @@ +date; + } + + public function setDate(\DateTime $date): void + { + $this->date = $date; + } +} diff --git a/src/Reporting/MonthlyUserList.php b/src/Reporting/MonthlyUserList.php index e4f329b5..e20e2ab3 100644 --- a/src/Reporting/MonthlyUserList.php +++ b/src/Reporting/MonthlyUserList.php @@ -9,22 +9,6 @@ namespace App\Reporting; -final class MonthlyUserList +final class MonthlyUserList extends AbstractUserList { - /** - * @var \DateTime - */ - private $date; - - public function getDate(): ?\DateTime - { - return $this->date; - } - - public function setDate(\DateTime $date): MonthlyUserList - { - $this->date = $date; - - return $this; - } } diff --git a/src/Reporting/Report.php b/src/Reporting/Report.php index 517d0c96..7bcc1cf6 100644 --- a/src/Reporting/Report.php +++ b/src/Reporting/Report.php @@ -14,12 +14,16 @@ final class Report implements ReportInterface private $id; private $label; private $route; + private $reportIcon = 'reporting'; - public function __construct(string $id, string $route, string $label) + public function __construct(string $id, string $route, string $label, ?string $reportIcon = null) { $this->id = $id; $this->route = $route; $this->label = $label; + if (null !== $reportIcon) { + $this->reportIcon = $reportIcon; + } } public function getRoute(): string @@ -36,4 +40,9 @@ final class Report implements ReportInterface { return $this->label; } + + public function getReportIcon(): string + { + return $this->reportIcon; + } } diff --git a/src/Reporting/ReportingService.php b/src/Reporting/ReportingService.php index 5bab3e52..0abc88ae 100644 --- a/src/Reporting/ReportingService.php +++ b/src/Reporting/ReportingService.php @@ -42,13 +42,14 @@ final class ReportingService $event = new ReportingEvent($user); if ($this->security->isGranted('view_reporting')) { - $event->addReport(new Report(self::DEFAULT_VIEW, 'report_user_week', 'report_user_week')); - $event->addReport(new Report('month_by_user', 'report_user_month', 'report_user_month')); - if ($this->security->isGranted('budget_project')) { - $event->addReport(new Report('project_view', 'report_project_view', 'report_project_view')); - } + $event->addReport(new Report('week_by_user', 'report_user_week', 'report_user_week', 'user')); + $event->addReport(new Report('month_by_user', 'report_user_month', 'report_user_month', 'user')); if ($this->security->isGranted('view_other_timesheet')) { - $event->addReport(new Report('monthly_users_list', 'report_monthly_users', 'report_monthly_users')); + $event->addReport(new Report('weekly_users_list', 'report_weekly_users', 'report_weekly_users', 'user')); + $event->addReport(new Report('monthly_users_list', 'report_monthly_users', 'report_monthly_users', 'user')); + } + if ($this->security->isGranted('budget_project')) { + $event->addReport(new Report('project_view', 'report_project_view', 'report_project_view', 'project')); } $this->dispatcher->dispatch($event); diff --git a/src/Reporting/WeeklyUserList.php b/src/Reporting/WeeklyUserList.php new file mode 100644 index 00000000..9ce6f5ac --- /dev/null +++ b/src/Reporting/WeeklyUserList.php @@ -0,0 +1,14 @@ +add('date', WeekPickerType::class, [ + 'model_timezone' => $options['timezone'], + 'view_timezone' => $options['timezone'], + 'start_date' => $options['start_date'], + 'format' => $options['format'], + ]); + } + + /** + * {@inheritdoc} + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => WeeklyUserList::class, + 'timezone' => date_default_timezone_get(), + 'start_date' => new \DateTime(), + 'format' => DateType::HTML5_FORMAT, + 'csrf_protection' => false, + 'method' => 'GET', + ]); + } +} diff --git a/templates/macros/search.html.twig b/templates/macros/search.html.twig index f0b785af..ceab96b8 100644 --- a/templates/macros/search.html.twig +++ b/templates/macros/search.html.twig @@ -51,7 +51,7 @@ {% else %} diff --git a/templates/macros/widgets.html.twig b/templates/macros/widgets.html.twig index 3edb7233..d8a1f2f3 100644 --- a/templates/macros/widgets.html.twig +++ b/templates/macros/widgets.html.twig @@ -147,7 +147,7 @@ {% endmacro %} {% macro badge(title, color) %} - {{ title }} + {{ title }} {% endmacro %} {% macro alert(type, description, title, icon) %} diff --git a/templates/reporting/month_by_user.html.twig b/templates/reporting/month_by_user.html.twig deleted file mode 100644 index 09f5b369..00000000 --- a/templates/reporting/month_by_user.html.twig +++ /dev/null @@ -1,112 +0,0 @@ -{% extends 'reporting/layout.html.twig' %} - -{% block report_title %}{{ 'report_user_month'|trans({}, 'reporting') }}{% endblock %} - -{% block report %} - - {% set hasData = false %} - {% for day in days %} - {% if day.details is not empty %} - {% set hasData = true %} - {% endif %} - {% endfor %} - - {% embed '@AdminLTE/Widgets/box-widget.html.twig' %} - {% import "macros/widgets.html.twig" as widgets %} - {% block box_before %} - {{ form_start(form, {'action': path('report_user_month'), 'attr': {'class': 'form-inline'}}) }} - {% endblock %} - {% block box_after %} - {{ form_end(form) }} - {% endblock %} - {% block box_title %} - {% if form.user is defined %} - {{ form_row(form.user, {'label': false}) }} - {% else %} - {{ widgets.username(user) }} - {% endif %} - {{ form_widget(form.date) }} - {% endblock %} - {% block box_body_class %}user-month-reporting-box table-responsive{% if hasData %} no-padding{% endif %}{% endblock %} - {% block box_body %} - {% if not hasData %} - {{ widgets.nothing_found() }} - {% else %} - - - - - {% for day in days %} - - {% endfor %} - - {% for project in rows %} - - - - {% for day in project.days %} - - {% endfor %} - - {% for activity in project.activities %} - - - - {% for day in activity.days %} - - {% endfor %} - - {% endfor %} - {% endfor %} - {% set total = 0 %} - - {% for day in days %} - {% set total = total + day.totalDuration %} - {% endfor %} - - - {% for day in days %} - - {% endfor %} - -
   - {{ day.day|day_name(true) }}
- {{ day.day|date_format('d.m') }} -
- {{ widgets.label_project(project.project) }} - {{ project.duration|duration }} - {% if day.duration > 0 %} - {{ day.duration|duration }} - {% endif %} -
- {{ widgets.label_activity(activity.activity) }} - {{ activity.duration|duration }} - {% if day.duration > 0 %} - {{ day.duration|duration }} - {% endif %} -
{{ total|duration }} - {% if day.totalDuration > 0 %} - {{ day.totalDuration|duration }} - {% endif %} -
- {% endif %} - {% endblock %} - {% endembed %} - -{% endblock %} - -{% block javascripts %} - {{ parent() }} - -{% endblock %} diff --git a/templates/reporting/week_by_user.html.twig b/templates/reporting/report_by_user.html.twig similarity index 93% rename from templates/reporting/week_by_user.html.twig rename to templates/reporting/report_by_user.html.twig index 12a85934..c04460f6 100644 --- a/templates/reporting/week_by_user.html.twig +++ b/templates/reporting/report_by_user.html.twig @@ -1,6 +1,6 @@ {% extends 'reporting/layout.html.twig' %} -{% block report_title %}{{ 'report_user_week'|trans({}, 'reporting') }}{% endblock %} +{% block report_title %}{{ report_title|trans({}, 'reporting') }}{% endblock %} {% block report %} @@ -14,7 +14,7 @@ {% embed '@AdminLTE/Widgets/box-widget.html.twig' %} {% import "macros/widgets.html.twig" as widgets %} {% block box_before %} - {{ form_start(form, {'action': path('report_user_week'), 'attr': {'class': 'form-inline'}}) }} + {{ form_start(form, {'attr': {'class': 'form-inline'}}) }} {% endblock %} {% block box_after %} {{ form_end(form) }} @@ -27,7 +27,7 @@ {% endif %} {{ form_widget(form.date) }} {% endblock %} - {% block box_body_class %}user-week-reporting-box table-responsive{% if hasData %} no-padding{% endif %}{% endblock %} + {% block box_body_class %}{{ box_id }} table-responsive{% if hasData %} no-padding{% endif %}{% endblock %} {% block box_body %} {% if not hasData %} {{ widgets.nothing_found() }} diff --git a/templates/reporting/monthly_user_list.html.twig b/templates/reporting/report_user_list.html.twig similarity index 80% rename from templates/reporting/monthly_user_list.html.twig rename to templates/reporting/report_user_list.html.twig index 5d745375..e42cbf7f 100644 --- a/templates/reporting/monthly_user_list.html.twig +++ b/templates/reporting/report_user_list.html.twig @@ -1,13 +1,13 @@ {% extends 'reporting/layout.html.twig' %} -{% block report_title %}{{ 'report_monthly_users'|trans({}, 'reporting') }}{% endblock %} +{% block report_title %}{{ report_title|trans({}, 'reporting') }}{% endblock %} {% block report %} {% embed '@AdminLTE/Widgets/box-widget.html.twig' %} {% import "macros/widgets.html.twig" as widgets %} {% block box_before %} - {{ form_start(form, {'action': path('report_monthly_users'), 'attr': {'class': 'form-inline'}}) }} + {{ form_start(form, {'attr': {'class': 'form-inline'}}) }} {% endblock %} {% block box_after %} {{ form_end(form) }} @@ -15,7 +15,7 @@ {% block box_title %} {{ form_widget(form.date) }} {% endblock %} - {% block box_body_class %}monthly-user-list-reporting-box table-responsive no-padding{% endblock %} + {% block box_body_class %}{{ box_id }} table-responsive no-padding{% endblock %} {% block box_body %} @@ -29,19 +29,19 @@ {% endfor %} {% for userDay in rows %} - {% set usersMonthDuration = 0 %} + {% set usersTotalDuration = 0 %} {% for day in userDay.days %} {% if day.totalDuration > 0 %} - {% set usersMonthDuration = usersMonthDuration + day.totalDuration %} + {% set usersTotalDuration = usersTotalDuration + day.totalDuration %} {% endif %} {% endfor %} {% for day in userDay.days %} diff --git a/tests/Controller/Reporting/ReportByUserControllerTest.php b/tests/Controller/Reporting/ReportByUserControllerTest.php new file mode 100644 index 00000000..fd796717 --- /dev/null +++ b/tests/Controller/Reporting/ReportByUserControllerTest.php @@ -0,0 +1,60 @@ +setAmount(50); + $fixture->setAmountRunning(10); + $fixture->setUser($this->getUserByRole($role)); + $fixture->setStartDate(new \DateTime()); + $this->importFixture($fixture); + } + + public function testWeekByUserIsSecure() + { + $this->assertUrlIsSecured('/reporting/week_by_user'); + } + + public function testMonthByUserIsSecure() + { + $this->assertUrlIsSecured('/reporting/month_by_user'); + } + + public function testUserWeekReport() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); + $this->importReportingFixture(User::ROLE_SUPER_ADMIN); + $this->assertAccessIsGranted($client, '/reporting/week_by_user?user=4&date=12999119191'); + self::assertStringContainsString('
attr('value')); + } + + public function testUserMonthReport() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); + $this->importReportingFixture(User::ROLE_USER); + $this->assertAccessIsGranted($client, '/reporting/month_by_user?user=4&date=12999119191'); + self::assertStringContainsString('
count()); + } +} diff --git a/tests/Controller/Reporting/ReportUsersListControllerTest.php b/tests/Controller/Reporting/ReportUsersListControllerTest.php new file mode 100644 index 00000000..bd607973 --- /dev/null +++ b/tests/Controller/Reporting/ReportUsersListControllerTest.php @@ -0,0 +1,70 @@ +setAmount(50); + $fixture->setAmountRunning(10); + $fixture->setUser($this->getUserByRole($role)); + $fixture->setStartDate(new \DateTime()); + $this->importFixture($fixture); + } + + public function testWeeklyListIsSecure() + { + $this->assertUrlIsSecured('/reporting/weekly_users_list'); + } + + public function testMonthlyListIsSecure() + { + $this->assertUrlIsSecured('/reporting/monthly_users_list'); + } + + public function testWeeklyUsersListIsSecureForUserRole() + { + $this->assertUrlIsSecuredForRole(User::ROLE_USER, '/reporting/weekly_users_list'); + } + + public function testMonthlyUsersListIsSecureForUserRole() + { + $this->assertUrlIsSecuredForRole(User::ROLE_USER, '/reporting/monthly_users_list'); + } + + public function testWeeklyUsersReport() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD); + $this->importReportingFixture(User::ROLE_TEAMLEAD); + $this->assertAccessIsGranted($client, '/reporting/weekly_users_list'); + self::assertStringContainsString('
count()); + } + + public function testMonthlyUsersReport() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD); + $this->importReportingFixture(User::ROLE_TEAMLEAD); + $this->assertAccessIsGranted($client, '/reporting/monthly_users_list'); + self::assertStringContainsString('
count()); + } +} diff --git a/tests/Controller/ReportingControllerTest.php b/tests/Controller/ReportingControllerTest.php index d064744c..934856fc 100644 --- a/tests/Controller/ReportingControllerTest.php +++ b/tests/Controller/ReportingControllerTest.php @@ -10,7 +10,6 @@ namespace App\Tests\Controller; use App\Entity\User; -use App\Tests\DataFixtures\TimesheetFixtures; /** * @group integration @@ -22,73 +21,12 @@ class ReportingControllerTest extends ControllerBaseTest $this->assertUrlIsSecured('/reporting'); } - public function testWeekByUserIsSecure() - { - $this->assertUrlIsSecured('/reporting/week_by_user'); - } - - public function testMonthByUserIsSecure() - { - $this->assertUrlIsSecured('/reporting/month_by_user'); - } - - public function testMonthlyListIsSecure() - { - $this->assertUrlIsSecured('/reporting/monthly_users_list'); - } - - public function testMonthlyUsersListIsSecureForUserRole() - { - $this->assertUrlIsSecuredForRole(User::ROLE_USER, '/reporting/monthly_users_list'); - } - - protected function importReportingFixture(string $role) - { - $fixture = new TimesheetFixtures(); - $fixture->setAmount(50); - $fixture->setAmountRunning(10); - $fixture->setUser($this->getUserByRole($role)); - $fixture->setStartDate(new \DateTime()); - $this->importFixture($fixture); - } - public function testRedirectForDefaultReportUrl() { $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); - $this->importReportingFixture(User::ROLE_USER); $this->request($client, '/reporting/'); $this->assertIsRedirect($client, $this->createUrl('/reporting/week_by_user')); $client->followRedirect(); self::assertStringContainsString('
getResponse()->getContent()); - $option = $client->getCrawler()->filterXPath("//select[@id='user']/option[@selected]"); - self::assertEquals(4, $option->attr('value')); - } - - public function testUserMonthReport() - { - $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); - $this->importReportingFixture(User::ROLE_USER); - $this->assertAccessIsGranted($client, '/reporting/month_by_user?user=4&date=12999119191'); - self::assertStringContainsString('
count()); - } - - public function testMonthlyUsersReport() - { - $client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD); - $this->importReportingFixture(User::ROLE_TEAMLEAD); - $this->assertAccessIsGranted($client, '/reporting/monthly_users_list'); - self::assertStringContainsString('
count()); - } } diff --git a/tests/Reporting/ReportTest.php b/tests/Reporting/ReportTest.php index 70ca9ca0..6a43b02b 100644 --- a/tests/Reporting/ReportTest.php +++ b/tests/Reporting/ReportTest.php @@ -25,5 +25,9 @@ class ReportTest extends TestCase self::assertEquals('id', $report->getId()); self::assertEquals('route', $report->getRoute()); self::assertEquals('label', $report->getLabel()); + self::assertEquals('reporting', $report->getReportIcon()); + + $report = new Report('id', 'route', 'label', 'foo'); + self::assertEquals('foo', $report->getReportIcon()); } } diff --git a/tests/Reporting/ReportingServiceTest.php b/tests/Reporting/ReportingServiceTest.php index db17d313..a2971c40 100644 --- a/tests/Reporting/ReportingServiceTest.php +++ b/tests/Reporting/ReportingServiceTest.php @@ -47,6 +47,6 @@ class ReportingServiceTest extends TestCase $sut = $this->getSut(true); $reports = $sut->getAvailableReports(new User()); self::assertIsArray($reports); - self::assertCount(4, $reports); + self::assertCount(5, $reports); } } diff --git a/translations/messages.de.xlf b/translations/messages.de.xlf index a2630682..20f83cbf 100644 --- a/translations/messages.de.xlf +++ b/translations/messages.de.xlf @@ -66,7 +66,11 @@ label.set_as_default - Als Standard-Einstellung speichern + Einstellung als Suchfavorit speichern + + + label.remove_default + Suchfavorit löschen label.asc diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index b1382a6e..3d6130dd 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -66,7 +66,11 @@ label.set_as_default - Save as default setting + Save setting as search favourite + + + label.remove_default + Delete search favourite label.asc diff --git a/translations/reporting.de.xlf b/translations/reporting.de.xlf index 32fec252..de576c5d 100644 --- a/translations/reporting.de.xlf +++ b/translations/reporting.de.xlf @@ -10,6 +10,10 @@ report_user_month Monatsansicht für einen Benutzer + + report_weekly_users + Wochenansicht für alle Benutzer + report_monthly_users Monatsansicht für alle Benutzer diff --git a/translations/reporting.en.xlf b/translations/reporting.en.xlf index f6812427..31f73e76 100644 --- a/translations/reporting.en.xlf +++ b/translations/reporting.en.xlf @@ -10,6 +10,10 @@ report_user_month Monthly view for one user + + report_weekly_users + Weekly view for all users + report_monthly_users Monthly view for all users
{{ widgets.username(userDay.user) }} - {% if usersMonthDuration > 0 %} - {{ usersMonthDuration|duration }} + {% if usersTotalDuration > 0 %} + {{ usersTotalDuration|duration }} {% endif %}