feat: all-season assignments

This commit is contained in:
Björn Fromme authored and Björn Fromme committed 2026-09-26 12:13:16 +02:00
1 parent 675cec1b69
commit cf876ed224
119 files changed
+6194 -294

No files matched your search

@@ -0,0 +1,88 @@
<?php
namespace App\Repository;
use App\Entity\SeasonAssignmentTask;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<SeasonAssignmentTask>
*
* @method SeasonAssignmentTask|null find($id, $lockMode = null, $lockVersion = null)
* @method SeasonAssignmentTask|null findOneBy(array $criteria, array $orderBy = null)
* @method SeasonAssignmentTask[] findAll()
* @method SeasonAssignmentTask[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class SeasonAssignmentTaskRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, SeasonAssignmentTask::class);
}
/**
* Every open entry that could still be reminded about: not ticked off, on a live assignment
* that has a teamer on it.
*
* The deadlines themselves are not part of this query. They are computed from the
* assignment's first day by a relative offset, and month arithmetic is not invertible
* day by day - "minus one month" from the 31st and back again lands somewhere else - so a
* query built by inverting the offset would silently skip entries. The reminder resolves
* every row through SeasonTaskDeadlineResolver instead; the set is small, one assignment per
* all-season teamer per season.
*
* @return SeasonAssignmentTask[]
*/
public function findOpen(?\DateTimeImmutable $windowStart = null, ?\DateTimeImmutable $windowEnd = null): array
{
return $this
->createOpenQueryBuilder($windowStart, $windowEnd)
->getQuery()
->getResult()
;
}
/**
* Extracted so the conditions can be pinned by a test compiling the DQL - the suite has no
* fixtures, so what is asserted is what the query says, not what it returns.
*/
public function createOpenQueryBuilder(
?\DateTimeImmutable $windowStart = null,
?\DateTimeImmutable $windowEnd = null,
): QueryBuilder {
$qb = $this->createQueryBuilder('task');
$qb
->select('task', 'season_assignment', 'teamer')
->innerJoin('task.seasonAssignment', 'season_assignment')
// no teamer means nobody to remind and nobody to be late
->innerJoin('season_assignment.teamer', 'teamer')
->where($qb->expr()->andX(
$qb->expr()->eq('task.done', ':done'),
$qb->expr()->isNull('season_assignment.deletedAt'),
$qb->expr()->isNull('teamer.deletedAt')
))
->setParameter('done', false)
->addOrderBy('teamer.lastName', \SortDirection::Ascending)
->addOrderBy('teamer.firstName', \SortDirection::Ascending)
;
if (null !== $windowStart) {
$qb
->andWhere($qb->expr()->gte('season_assignment.dateFrom', ':windowStart'))
->setParameter('windowStart', $windowStart)
;
}
if (null !== $windowEnd) {
$qb
->andWhere($qb->expr()->lte('season_assignment.dateFrom', ':windowEnd'))
->setParameter('windowEnd', $windowEnd)
;
}
return $qb;
}
}