added project filter in user-list reports (#4615)

This commit is contained in:
Kevin Papst
2024-02-07 18:00:29 +01:00
committed by GitHub
parent e98732f44a
commit 7abe787778
13 changed files with 139 additions and 60 deletions

View File

@@ -21,6 +21,7 @@ use App\Form\UserPreferencesForm;
use App\Form\UserRolesType;
use App\Form\UserTeamsType;
use App\Form\UserTwoFactorType;
use App\Repository\Query\TimesheetStatisticQuery;
use App\Repository\TeamRepository;
use App\Repository\TimesheetRepository;
use App\Repository\UserRepository;
@@ -84,13 +85,15 @@ final class ProfileController extends AbstractController
// but we need a full year, because the chart needs always 12 month
$begin = $dateFactory->createStartOfYear($begin);
$query = new TimesheetStatisticQuery($begin, $end, [$profile]);
$viewVars = [
'tab' => 'charts',
'page_setup' => $this->getPageSetup($profile, 'charts'),
'user' => $profile,
'stats' => $userStats,
'workingSince' => $workStartingDay,
'workMonths' => $statisticService->getMonthlyStats($begin, $end, [$profile])[0]
'workMonths' => $statisticService->getMonthlyStats($query)[0]
];
return $this->render('user/stats.html.twig', $viewVars);

View File

@@ -15,6 +15,7 @@ use App\Export\Spreadsheet\Writer\XlsxWriter;
use App\Model\DailyStatistic;
use App\Reporting\MonthlyUserList\MonthlyUserList;
use App\Reporting\MonthlyUserList\MonthlyUserListForm;
use App\Repository\Query\TimesheetStatisticQuery;
use App\Repository\Query\UserQuery;
use App\Repository\Query\VisibilityInterface;
use App\Repository\UserRepository;
@@ -106,7 +107,9 @@ final class ReportUsersMonthController extends AbstractController
$hasData = true;
if (!empty($allUsers)) {
$dayStats = $statisticService->getDailyStatistics($start, $end, $allUsers);
$statsQuery = new TimesheetStatisticQuery($start, $end, $allUsers);
$statsQuery->setProject($values->getProject());
$dayStats = $statisticService->getDailyStatistics($statsQuery);
}
if (empty($dayStats)) {

View File

@@ -15,6 +15,7 @@ use App\Export\Spreadsheet\Writer\XlsxWriter;
use App\Model\DailyStatistic;
use App\Reporting\WeeklyUserList\WeeklyUserList;
use App\Reporting\WeeklyUserList\WeeklyUserListForm;
use App\Repository\Query\TimesheetStatisticQuery;
use App\Repository\Query\UserQuery;
use App\Repository\Query\VisibilityInterface;
use App\Repository\UserRepository;
@@ -102,7 +103,9 @@ final class ReportUsersWeekController extends AbstractController
$hasData = true;
if (!empty($allUsers)) {
$dayStats = $statisticService->getDailyStatistics($start, $end, $allUsers);
$statsQuery = new TimesheetStatisticQuery($start, $end, $allUsers);
$statsQuery->setProject($values->getProject());
$dayStats = $statisticService->getDailyStatistics($statsQuery);
}
if (empty($dayStats)) {

View File

@@ -16,6 +16,7 @@ use App\Export\Spreadsheet\Writer\XlsxWriter;
use App\Model\MonthlyStatistic;
use App\Reporting\YearlyUserList\YearlyUserList;
use App\Reporting\YearlyUserList\YearlyUserListForm;
use App\Repository\Query\TimesheetStatisticQuery;
use App\Repository\Query\UserQuery;
use App\Repository\Query\VisibilityInterface;
use App\Repository\UserRepository;
@@ -107,7 +108,9 @@ final class ReportUsersYearController extends AbstractController
$hasData = true;
if (!empty($allUsers)) {
$monthStats = $statisticService->getMonthlyStats($start, $end, $allUsers);
$statsQuery = new TimesheetStatisticQuery($start, $end, $allUsers);
$statsQuery->setProject($values->getProject());
$monthStats = $statisticService->getMonthlyStats($statsQuery);
}
if (empty($monthStats)) {

View File

@@ -9,6 +9,7 @@
namespace App\Reporting;
use App\Entity\Project;
use App\Entity\Team;
abstract class AbstractUserList
@@ -17,6 +18,7 @@ abstract class AbstractUserList
private bool $decimal = false;
private string $sumType = 'duration';
private ?Team $team = null;
private ?Project $project = null;
public function getDate(): ?\DateTimeInterface
{
@@ -61,4 +63,14 @@ abstract class AbstractUserList
{
$this->team = $team;
}
public function getProject(): ?Project
{
return $this->project;
}
public function setProject(?Project $project): void
{
$this->project = $project;
}
}

View File

@@ -10,6 +10,7 @@
namespace App\Reporting\MonthlyUserList;
use App\Form\Type\MonthPickerType;
use App\Form\Type\ProjectType;
use App\Form\Type\ReportSumType;
use App\Form\Type\TeamType;
use Symfony\Component\Form\AbstractType;
@@ -33,6 +34,11 @@ final class MonthlyUserListForm extends AbstractType
'required' => false,
'width' => false,
]);
$builder->add('project', ProjectType::class, [
'multiple' => false,
'required' => false,
'width' => false,
]);
$builder->add('sumType', ReportSumType::class);
}

View File

@@ -9,6 +9,7 @@
namespace App\Reporting\WeeklyUserList;
use App\Form\Type\ProjectType;
use App\Form\Type\ReportSumType;
use App\Form\Type\TeamType;
use App\Form\Type\WeekPickerType;
@@ -33,6 +34,11 @@ final class WeeklyUserListForm extends AbstractType
'required' => false,
'width' => false,
]);
$builder->add('project', ProjectType::class, [
'multiple' => false,
'required' => false,
'width' => false,
]);
$builder->add('sumType', ReportSumType::class);
}

View File

@@ -9,6 +9,7 @@
namespace App\Reporting\YearlyUserList;
use App\Form\Type\ProjectType;
use App\Form\Type\ReportSumType;
use App\Form\Type\TeamType;
use App\Form\Type\YearPickerType;
@@ -34,6 +35,11 @@ final class YearlyUserListForm extends AbstractType
'required' => false,
'width' => false,
]);
$builder->add('project', ProjectType::class, [
'multiple' => false,
'required' => false,
'width' => false,
]);
$builder->add('sumType', ReportSumType::class);
}

View File

@@ -0,0 +1,57 @@
<?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\Repository\Query;
use App\Entity\Project;
use App\Entity\User;
final class TimesheetStatisticQuery
{
private ?Project $project = null;
/**
* @param array<User> $users
*/
public function __construct(
private readonly \DateTimeInterface $begin,
private readonly \DateTimeInterface $end,
private array $users
)
{
}
public function getBegin(): \DateTimeInterface
{
return $this->begin;
}
public function getEnd(): \DateTimeInterface
{
return $this->end;
}
/**
* @return User[]
*/
public function getUsers(): array
{
return $this->users;
}
public function getProject(): ?Project
{
return $this->project;
}
public function setProject(?Project $project): void
{
$this->project = $project;
}
}

View File

@@ -53,7 +53,7 @@ class UserRepository extends EntityRepository implements UserLoaderInterface, Us
* @throws ORMException
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function saveUser(User $user)
public function saveUser(User $user): void
{
$entityManager = $this->getEntityManager();
$entityManager->persist($user);
@@ -397,7 +397,7 @@ class UserRepository extends EntityRepository implements UserLoaderInterface, Us
* @param UserQuery $query
* @return User[]
*/
public function getUsersForQuery(UserQuery $query): iterable
public function getUsersForQuery(UserQuery $query): array
{
$qb = $this->getQueryBuilderForQuery($query);
@@ -408,8 +408,9 @@ class UserRepository extends EntityRepository implements UserLoaderInterface, Us
* @param QueryBuilder $qb
* @return User[]
*/
protected function getHydratedResultsByQuery(QueryBuilder $qb): iterable
protected function getHydratedResultsByQuery(QueryBuilder $qb): array
{
/** @var array<User> $results */
$results = $qb->getQuery()->getResult();
$loader = new UserLoader($qb->getEntityManager());

View File

@@ -12,8 +12,8 @@ namespace App\Timesheet;
use App\Entity\User;
use App\Model\DailyStatistic;
use App\Model\MonthlyStatistic;
use App\Repository\Query\TimesheetStatisticQuery;
use App\Repository\TimesheetRepository;
use DateTime;
use DateTimeInterface;
final class TimesheetStatisticService
@@ -23,13 +23,15 @@ final class TimesheetStatisticService
}
/**
* @param DateTimeInterface $begin
* @param DateTimeInterface $end
* @param User[] $users
* @return DailyStatistic[]
*/
public function getDailyStatistics(DateTimeInterface $begin, DateTimeInterface $end, array $users): array
public function getDailyStatistics(TimesheetStatisticQuery $query): array
{
$begin = $query->getBegin();
$end = $query->getEnd();
$users = $query->getUsers();
$project = $query->getProject();
/** @var DailyStatistic[] $stats */
$stats = [];
@@ -63,6 +65,13 @@ final class TimesheetStatisticService
->addGroupBy('billable')
;
if ($project !== null) {
$qb
->andWhere($qb->expr()->eq('t.project', ':project'))
->setParameter('project', $project)
;
}
$results = $qb->getQuery()->getResult();
foreach ($results as $row) {
@@ -241,7 +250,7 @@ final class TimesheetStatisticService
return $stats;
}
public function findFirstRecordDate(User $user): ?DateTime
public function findFirstRecordDate(User $user): ?\DateTimeImmutable
{
$result = $this->repository->createQueryBuilder('t')
->select('MIN(t.begin)')
@@ -254,19 +263,19 @@ final class TimesheetStatisticService
return null;
}
return new DateTime((string) $result, new \DateTimeZone($user->getTimezone()));
return new \DateTimeImmutable((string) $result, new \DateTimeZone($user->getTimezone()));
}
/**
* Returns an array of Year statistics.
*
* @param DateTime $begin
* @param DateTime $end
* @param User[] $users
* @return MonthlyStatistic[]
*/
public function getMonthlyStats(DateTime $begin, DateTime $end, array $users): array
public function getMonthlyStats(TimesheetStatisticQuery $query): array
{
$begin = $query->getBegin();
$end = $query->getEnd();
$users = $query->getUsers();
$project = $query->getProject();
/** @var MonthlyStatistic[] $stats */
$stats = [];
@@ -297,6 +306,13 @@ final class TimesheetStatisticService
->addGroupBy('billable')
;
if ($project !== null) {
$qb
->andWhere($qb->expr()->eq('t.project', ':project'))
->setParameter('project', $project)
;
}
$results = $qb->getQuery()->getResult();
foreach ($results as $row) {