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

@@ -24,6 +24,7 @@ use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Pagerfanta\Pagerfanta;
@@ -160,16 +161,26 @@ class ActivityRepository extends EntityRepository
return $stats;
}
private function addPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = [], bool $globalsOnly = false)
private function addPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = [], bool $globalsOnly = false): void
{
$permissions = $this->getPermissionCriteria($qb, $user, $teams, $globalsOnly);
if ($permissions->count() > 0) {
$qb->andWhere($permissions);
}
}
private function getPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = [], bool $globalsOnly = false): Andx
{
$andX = $qb->expr()->andX();
// make sure that all queries without a user see all projects
if (null === $user && empty($teams)) {
return;
return $andX;
}
// make sure that admins see all activities
if (null !== $user && $user->canSeeAllData()) {
return;
return $andX;
}
if (null !== $user) {
@@ -177,33 +188,33 @@ class ActivityRepository extends EntityRepository
}
if (empty($teams)) {
$qb->andWhere('SIZE(a.teams) = 0');
$andX->add('SIZE(a.teams) = 0');
if (!$globalsOnly) {
$qb->andWhere('SIZE(p.teams) = 0');
$qb->andWhere('SIZE(c.teams) = 0');
$andX->add('SIZE(p.teams) = 0');
$andX->add('SIZE(c.teams) = 0');
}
return;
return $andX;
}
$orActivity = $qb->expr()->orX(
'SIZE(a.teams) = 0',
$qb->expr()->isMemberOf(':teams', 'a.teams')
);
$qb->andWhere($orActivity);
$andX->add($orActivity);
if (!$globalsOnly) {
$orProject = $qb->expr()->orX(
'SIZE(p.teams) = 0',
$qb->expr()->isMemberOf(':teams', 'p.teams')
);
$qb->andWhere($orProject);
$andX->add($orProject);
$orCustomer = $qb->expr()->orX(
'SIZE(c.teams) = 0',
$qb->expr()->isMemberOf(':teams', 'c.teams')
);
$qb->andWhere($orCustomer);
$andX->add($orCustomer);
}
$ids = array_values(array_unique(array_map(function (Team $team) {
@@ -211,6 +222,8 @@ class ActivityRepository extends EntityRepository
}, $teams)));
$qb->setParameter('teams', $ids);
return $andX;
}
/**
@@ -242,9 +255,9 @@ class ActivityRepository extends EntityRepository
->addOrderBy('a.name', 'ASC')
;
$where = $qb->expr()->andX();
$mainQuery = $qb->expr()->andX();
$where->add($qb->expr()->eq('a.visible', ':visible'));
$mainQuery->add($qb->expr()->eq('a.visible', ':visible'));
$qb->setParameter('visible', true, \PDO::PARAM_BOOL);
if (!$query->isGlobalsOnly()) {
@@ -254,7 +267,7 @@ class ActivityRepository extends EntityRepository
->leftJoin('a.project', 'p')
->leftJoin('p.customer', 'c');
$where->add(
$mainQuery->add(
$qb->expr()->orX(
$qb->expr()->isNull('a.project'),
$qb->expr()->andX(
@@ -268,9 +281,9 @@ class ActivityRepository extends EntityRepository
}
if ($query->isGlobalsOnly()) {
$where->add($qb->expr()->isNull('a.project'));
$mainQuery->add($qb->expr()->isNull('a.project'));
} elseif ($query->hasProjects()) {
$where->add(
$mainQuery->add(
$qb->expr()->orX(
$qb->expr()->isNull('a.project'),
$qb->expr()->in('a.project', ':project')
@@ -279,28 +292,30 @@ class ActivityRepository extends EntityRepository
$qb->setParameter('project', $query->getProjects());
}
if (null !== $query->getActivityToIgnore()) {
$qb->andWhere($qb->expr()->neq('a.id', ':ignored'));
$qb->setParameter('ignored', $query->getActivityToIgnore());
$permissions = $this->getPermissionCriteria($qb, $query->getUser(), $query->getTeams(), $query->isGlobalsOnly());
if ($permissions->count() > 0) {
$mainQuery->add($permissions);
}
$this->addPermissionCriteria($qb, $query->getUser(), $query->getTeams(), $query->isGlobalsOnly());
$outerQuery = $qb->expr()->orX();
$or = $qb->expr()->orX();
// this must always be the last part before the or
$or->add($where);
// this must always be the last part of the query
if ($query->hasActivities()) {
$or->add($qb->expr()->in('a.id', ':activity'));
$outerQuery->add($qb->expr()->in('a.id', ':activity'));
$qb->setParameter('activity', $query->getActivities());
}
if ($or->count() > 0) {
$qb->andWhere($or);
if (null !== $query->getActivityToIgnore()) {
$mainQuery = $qb->expr()->andX(
$mainQuery,
$qb->expr()->neq('a.id', ':ignored')
);
$qb->setParameter('ignored', $query->getActivityToIgnore());
}
$outerQuery->add($mainQuery);
$qb->andWhere($outerQuery);
return $qb;
}

View File

@@ -25,6 +25,7 @@ use App\Repository\Query\CustomerQuery;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Pagerfanta\Pagerfanta;
@@ -157,14 +158,24 @@ class CustomerRepository extends EntityRepository
private function addPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = [])
{
$permissions = $this->getPermissionCriteria($qb, $user, $teams);
if ($permissions->count() > 0) {
$qb->andWhere($permissions);
}
}
private function getPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = []): Andx
{
$andX = $qb->expr()->andX();
// make sure that all queries without a user see all customers
if (null === $user && empty($teams)) {
return;
return $andX;
}
// make sure that admins see all customers
if (null !== $user && $user->canSeeAllData()) {
return;
return $andX;
}
if (null !== $user) {
@@ -172,22 +183,24 @@ class CustomerRepository extends EntityRepository
}
if (empty($teams)) {
$qb->andWhere('SIZE(c.teams) = 0');
$andX->add('SIZE(c.teams) = 0');
return;
return $andX;
}
$or = $qb->expr()->orX(
'SIZE(c.teams) = 0',
$qb->expr()->isMemberOf(':teams', 'c.teams')
);
$qb->andWhere($or);
$andX->add($or);
$ids = array_values(array_unique(array_map(function (Team $team) {
return $team->getId();
}, $teams)));
$qb->setParameter('teams', $ids);
return $andX;
}
/**
@@ -216,21 +229,33 @@ class CustomerRepository extends EntityRepository
->from(Customer::class, 'c')
->orderBy('c.name', 'ASC');
// TODO this where and the next if($query->hasCustomers()) should go into their own $qb->expr()->orX()
$qb->andWhere($qb->expr()->eq('c.visible', ':visible'));
$mainQuery = $qb->expr()->andX();
$mainQuery->add($qb->expr()->eq('c.visible', ':visible'));
$qb->setParameter('visible', true, \PDO::PARAM_BOOL);
$permissions = $this->getPermissionCriteria($qb, $query->getUser(), $query->getTeams());
if ($permissions->count() > 0) {
$mainQuery->add($permissions);
}
$outerQuery = $qb->expr()->orX();
if ($query->hasCustomers()) {
$qb->orWhere($qb->expr()->in('c.id', ':customer'))
->setParameter('customer', $query->getCustomers());
$outerQuery->add($qb->expr()->in('c.id', ':customer'));
$qb->setParameter('customer', $query->getCustomers());
}
if (null !== $query->getCustomerToIgnore()) {
$qb->andWhere($qb->expr()->neq('c.id', ':ignored'));
$mainQuery = $qb->expr()->andX(
$mainQuery,
$qb->expr()->neq('c.id', ':ignored')
);
$qb->setParameter('ignored', $query->getCustomerToIgnore());
}
$this->addPermissionCriteria($qb, $query->getUser(), $query->getTeams());
$outerQuery->add($mainQuery);
$qb->andWhere($outerQuery);
return $qb;
}

View File

@@ -26,6 +26,7 @@ use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Pagerfanta\Pagerfanta;
@@ -182,16 +183,26 @@ class ProjectRepository extends EntityRepository
return $stats;
}
public function addPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = [])
public function addPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = []): void
{
$permissions = $this->getPermissionCriteria($qb, $user, $teams);
if ($permissions->count() > 0) {
$qb->andWhere($permissions);
}
}
public function getPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = []): Andx
{
$andX = $qb->expr()->andX();
// make sure that all queries without a user see all projects
if (null === $user && empty($teams)) {
return;
return $andX;
}
// make sure that admins see all projects
if (null !== $user && $user->canSeeAllData()) {
return;
return $andX;
}
if (null !== $user) {
@@ -199,29 +210,31 @@ class ProjectRepository extends EntityRepository
}
if (empty($teams)) {
$qb->andWhere('SIZE(c.teams) = 0');
$qb->andWhere('SIZE(p.teams) = 0');
$andX->add('SIZE(c.teams) = 0');
$andX->add('SIZE(p.teams) = 0');
return;
return $andX;
}
$orProject = $qb->expr()->orX(
'SIZE(p.teams) = 0',
$qb->expr()->isMemberOf(':teams', 'p.teams')
);
$qb->andWhere($orProject);
$andX->add($orProject);
$orCustomer = $qb->expr()->orX(
'SIZE(c.teams) = 0',
$qb->expr()->isMemberOf(':teams', 'c.teams')
);
$qb->andWhere($orCustomer);
$andX->add($orCustomer);
$ids = array_values(array_unique(array_map(function (Team $team) {
return $team->getId();
}, $teams)));
$qb->setParameter('teams', $ids);
return $andX;
}
/**
@@ -259,57 +272,46 @@ class ProjectRepository extends EntityRepository
$qb->addSelect('c');
}
$qb->andWhere($qb->expr()->eq('p.visible', ':visible'));
$qb->andWhere($qb->expr()->eq('c.visible', ':customer_visible'));
if (!$query->isIgnoreDate()) {
$now = new DateTime();
$qb->andWhere(
$qb->expr()->andX(
$qb->expr()->orX(
$qb->expr()->lte('p.start', ':start'),
$qb->expr()->isNull('p.start')
),
$qb->expr()->orX(
$qb->expr()->gte('p.end', ':start'),
$qb->expr()->isNull('p.end')
)
)
)->setParameter('start', $now);
$qb->andWhere(
$qb->expr()->andX(
$qb->expr()->orX(
$qb->expr()->gte('p.end', ':end'),
$qb->expr()->isNull('p.end')
),
$qb->expr()->orX(
$qb->expr()->lte('p.start', ':end'),
$qb->expr()->isNull('p.start')
)
)
)->setParameter('end', $now);
}
$mainQuery = $qb->expr()->andX();
$mainQuery->add($qb->expr()->eq('p.visible', ':visible'));
$qb->setParameter('visible', true, \PDO::PARAM_BOOL);
$mainQuery->add($qb->expr()->eq('c.visible', ':customer_visible'));
$qb->setParameter('customer_visible', true, \PDO::PARAM_BOOL);
if ($query->hasProjects()) {
$qb->orWhere($qb->expr()->in('p.id', ':project'))
->setParameter('project', $query->getProjects());
if (!$query->isIgnoreDate()) {
$andx = $this->addProjectStartAndEndDate($qb, $query->getProjectStart(), $query->getProjectEnd());
$mainQuery->add($andx);
}
if ($query->hasCustomers()) {
$qb->andWhere($qb->expr()->in('p.customer', ':customer'))
->setParameter('customer', $query->getCustomers());
$mainQuery->add($qb->expr()->in('p.customer', ':customer'));
$qb->setParameter('customer', $query->getCustomers());
}
$permissions = $this->getPermissionCriteria($qb, $query->getUser(), $query->getTeams());
if ($permissions->count() > 0) {
$mainQuery->add($permissions);
}
$outerQuery = $qb->expr()->orX();
if ($query->hasProjects()) {
$outerQuery->add($qb->expr()->in('p.id', ':project'));
$qb->setParameter('project', $query->getProjects());
}
if (null !== $query->getProjectToIgnore()) {
$qb->andWhere($qb->expr()->neq('p.id', ':ignored'));
$mainQuery = $qb->expr()->andX(
$mainQuery,
$qb->expr()->neq('p.id', ':ignored')
);
$qb->setParameter('ignored', $query->getProjectToIgnore());
}
$this->addPermissionCriteria($qb, $query->getUser(), $query->getTeams());
$outerQuery->add($mainQuery);
$qb->andWhere($outerQuery);
return $qb;
}
@@ -369,39 +371,8 @@ class ProjectRepository extends EntityRepository
// begin < to and end = null
// begin > from and end < to
// ... and more ...
$begin = $query->getProjectStart();
$end = $query->getProjectEnd();
if (null !== $begin) {
$qb->andWhere(
$qb->expr()->andX(
$qb->expr()->orX(
$qb->expr()->lte('p.start', ':start'),
$qb->expr()->isNull('p.start')
),
$qb->expr()->orX(
$qb->expr()->gte('p.end', ':start'),
$qb->expr()->isNull('p.end')
)
)
)->setParameter('start', $query->getProjectStart());
}
if (null !== $end) {
$qb->andWhere(
$qb->expr()->andX(
$qb->expr()->orX(
$qb->expr()->gte('p.end', ':end'),
$qb->expr()->isNull('p.end')
),
$qb->expr()->orX(
$qb->expr()->lte('p.start', ':end'),
$qb->expr()->isNull('p.start')
)
)
)->setParameter('end', $query->getProjectEnd());
}
$times = $this->addProjectStartAndEndDate($qb, $query->getProjectStart(), $query->getProjectEnd());
$qb->andWhere($times);
$this->addPermissionCriteria($qb, $query->getCurrentUser());
@@ -440,6 +411,45 @@ class ProjectRepository extends EntityRepository
return $qb;
}
private function addProjectStartAndEndDate(QueryBuilder $qb, ?DateTime $begin, ?DateTime $end): Andx
{
$and = $qb->expr()->andX();
if (null !== $begin) {
$and->add(
$qb->expr()->andX(
$qb->expr()->orX(
$qb->expr()->lte('p.start', ':start'),
$qb->expr()->isNull('p.start')
),
$qb->expr()->orX(
$qb->expr()->gte('p.end', ':start'),
$qb->expr()->isNull('p.end')
)
)
);
$qb->setParameter('start', $begin);
}
if (null !== $end) {
$and->add(
$qb->expr()->andX(
$qb->expr()->orX(
$qb->expr()->gte('p.end', ':end'),
$qb->expr()->isNull('p.end')
),
$qb->expr()->orX(
$qb->expr()->lte('p.start', ':end'),
$qb->expr()->isNull('p.start')
)
)
);
$qb->setParameter('end', $end);
}
return $and;
}
public function countProjectsForQuery(ProjectQuery $query): int
{
$qb = $this->getQueryBuilderForQuery($query);

View File

@@ -14,6 +14,14 @@ use App\Entity\Project;
final class ProjectFormTypeQuery extends BaseFormTypeQuery
{
/**
* @var \DateTime|null
*/
private $projectStart;
/**
* @var \DateTime|null
*/
private $projectEnd;
/**
* @var Project|null
*/
@@ -22,8 +30,8 @@ final class ProjectFormTypeQuery extends BaseFormTypeQuery
private $withCustomer = false;
/**
* @param Project|int|null $project
* @param Customer|int|null $customer
* @param Project|int|null|array<int>|array<Project> $project
* @param Customer|int|null|array<int>|array<Customer> $customer
*/
public function __construct($project = null, $customer = null)
{
@@ -40,13 +48,25 @@ final class ProjectFormTypeQuery extends BaseFormTypeQuery
}
$this->setCustomers($customer);
}
$this->projectStart = $this->projectEnd = new \DateTime();
}
/**
* Whether customers should be joined
*
* @return bool
*/
public function withCustomer(): bool
{
return $this->withCustomer;
}
/**
* Directly join the customer
*
* @param bool $withCustomer
*/
public function setWithCustomer(bool $withCustomer): void
{
$this->withCustomer = $withCustomer;
@@ -60,11 +80,9 @@ final class ProjectFormTypeQuery extends BaseFormTypeQuery
return $this->projectToIgnore;
}
public function setProjectToIgnore(Project $projectToIgnore): ProjectFormTypeQuery
public function setProjectToIgnore(Project $projectToIgnore): void
{
$this->projectToIgnore = $projectToIgnore;
return $this;
}
public function isIgnoreDate(): bool
@@ -72,10 +90,28 @@ final class ProjectFormTypeQuery extends BaseFormTypeQuery
return $this->ignoreDate;
}
public function setIgnoreDate(bool $ignoreDate): ProjectFormTypeQuery
public function setIgnoreDate(bool $ignoreDate): void
{
$this->ignoreDate = $ignoreDate;
}
return $this;
public function getProjectStart(): ?\DateTime
{
return $this->projectStart;
}
public function setProjectStart(?\DateTime $projectStart): void
{
$this->projectStart = $projectStart;
}
public function getProjectEnd(): ?\DateTime
{
return $this->projectEnd;
}
public function setProjectEnd(?\DateTime $projectEnd): void
{
$this->projectEnd = $projectEnd;
}
}

View File

@@ -1052,7 +1052,7 @@ class TimesheetRepository extends EntityRepository
* @param User|null $user
* @param DateTime|null $startFrom
* @param int $limit
* @return array|mixed
* @return Timesheet[]
* @throws \Doctrine\ORM\Query\QueryException
*/
public function getRecentActivities(User $user = null, DateTime $startFrom = null, int $limit = 10)