* * @method TeamerWorkMonth|null find($id, $lockMode = null, $lockVersion = null) * @method TeamerWorkMonth|null findOneBy(array $criteria, array $orderBy = null) * @method TeamerWorkMonth[] findAll() * @method TeamerWorkMonth[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class TeamerWorkMonthRepository extends ServiceEntityRepository { use QueryHelperTrait; public function __construct(ManagerRegistry $registry) { parent::__construct($registry, TeamerWorkMonth::class); } public function findOneForTeamerAndMonth(Teamer $teamer, int $year, int $month): ?TeamerWorkMonth { return $this->findOneBy(['teamer' => $teamer, 'year' => $year, 'month' => $month]); } /** * @return TeamerWorkMonth[] */ public function findForTeamer(Teamer $teamer): array { return $this->findBy( ['teamer' => $teamer], ['year' => \SortDirection::Descending, 'month' => \SortDirection::Descending], ); } /** * The admin list. * * Deliberately over TeamerWorkMonth rather than over Upload: UploadRepository's list * queries inner-join a disposition, which structurally excludes every document hanging * off anything else, and it is the month that carries the status anyway. */ public function getListQuery(WorkMonthFilterDto $filterDto): Query { $qb = $this->createQueryBuilder('work_month'); $qb ->select('work_month', 'teamer') ->innerJoin('work_month.teamer', 'teamer') ; $this->applyFilterSettings($filterDto, $qb); return $qb ->addOrderBy('work_month.year', \SortDirection::Descending) ->addOrderBy('work_month.month', \SortDirection::Descending) ->addOrderBy('teamer.lastName', \SortDirection::Ascending) ->getQuery() ; } /** * Months still waiting for a signed copy, for the reminder. * * @return TeamerWorkMonth[] */ public function findAwaitingDocument(\DateTimeImmutable $importedBefore): array { return $this ->createQueryBuilder('work_month') ->select('work_month', 'teamer') ->innerJoin('work_month.teamer', 'teamer') ->andWhere('work_month.status IN (:awaiting)') ->andWhere('work_month.syncedAt <= :importedBefore') ->andWhere('teamer.deletedAt IS NULL') ->setParameter('awaiting', [WorkMonthStatus::OPEN, WorkMonthStatus::REJECTED]) ->setParameter('importedBefore', $importedBefore) ->addOrderBy('teamer.lastName', \SortDirection::Ascending) ->getQuery() ->getResult() ; } private function applyFilterSettings(WorkMonthFilterDto $filterDto, QueryBuilder $qb): void { if (null !== $filterDto->getTeamerName()) { $qb ->andWhere($qb->expr()->orX( $qb->expr()->like('teamer.firstName', ':teamerName'), $qb->expr()->like('teamer.lastName', ':teamerName') )) ->setParameter('teamerName', '%'.$this->escapeLikeWildcards($filterDto->getTeamerName()).'%') ; } if (null !== $period = $filterDto->getPeriod()) { $qb ->andWhere('work_month.year = :year') ->andWhere('work_month.month = :month') ->setParameter('year', (int) $period->format('Y')) ->setParameter('month', (int) $period->format('n')) ; } if (0 < count($filterDto->getStatuses())) { $qb ->andWhere('work_month.status IN (:statuses)') ->setParameter('statuses', $filterDto->getStatuses()) ; } elseif (!$filterDto->isIncludeApproved()) { // Only a default, so an explicit status choice still wins. $qb ->andWhere('work_month.status != :approved') ->setParameter('approved', WorkMonthStatus::APPROVED) ; } if ($filterDto->isIncludeMismatchedOnly()) { $qb->andWhere('work_month.mismatchDetectedAt IS NOT NULL'); } } }