From f8207b6731044f40664b9233d0b6dfd994aacaf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Wed, 8 Nov 2023 12:54:16 +0100 Subject: [PATCH] Feat: Implement email reminders in cli command to be executed by cron --- src/Command/CronCommand.php | 179 ++++++++++++++++++ src/Command/FlushLogsCommand.php | 41 ---- .../email/reminder_contract_upload.html.twig | 25 +++ .../email/reminder_invoice_upload.html.twig | 24 +++ 4 files changed, 228 insertions(+), 41 deletions(-) create mode 100644 src/Command/CronCommand.php delete mode 100644 src/Command/FlushLogsCommand.php create mode 100644 templates/email/reminder_contract_upload.html.twig create mode 100644 templates/email/reminder_invoice_upload.html.twig diff --git a/src/Command/CronCommand.php b/src/Command/CronCommand.php new file mode 100644 index 0000000..be61a34 --- /dev/null +++ b/src/Command/CronCommand.php @@ -0,0 +1,179 @@ +flushLogs($io); + $this->sendContractUploadReminders($io); + $this->sendInvoiceUploadReminders($io); + + return Command::SUCCESS; + } + + private function flushLogs(SymfonyStyle $io): void + { + // Deletes all log entries older than 30 days + $dueDate = (new \DateTimeImmutable())->modify('-30 days'); + try { + $result = $this + ->entityManager + ->getConnection() + ->executeQuery('DELETE FROM log WHERE log.timestamp < ?', [$dueDate->format('Y-m-d H:i:s')]); + $message = 'Deleted '.$result->rowCount().' log entries'; + $io->info($message); + $this->logger->info($message); + } catch (Exception $e) { + $message = 'Unable to delete log entries: '.$e->getMessage(); + $io->error($message); + $this->logger->error($message); + } + } + + private function sendContractUploadReminders(SymfonyStyle $io): void + { + // Find dispositions of assignments with due contract upload + $repository = $this->entityManager->getRepository(Disposition::class); + + // Contracts have to be uploaded before assignment's begin date. Reminder is + // sent three days before end of this period thus due date is today in three days. + $contractDueDate = (new \DateTimeImmutable())->modify('+3 days'); + + $qb = $repository->createQueryBuilder('disposition'); + + $dispositions = $qb + ->select('disposition', 'assignment', 'destination') + ->innerJoin('disposition.assignment', 'assignment') + ->innerJoin('assignment.destination', 'destination') + ->leftJoin('disposition.documents', 'document', Join::WITH, 'document.type = :documentType') + ->where($qb->expr()->andX( + $qb->expr()->isNull('document'), + $qb->expr()->orX( + // Due date can be determined by assignment's date which potentially overrides destination date + $qb->expr()->andX( + $qb->expr()->isNotNull('assignment.dateFrom'), + $qb->expr()->eq('assignment.dateFrom', ':dateFrom') + ), + $qb->expr()->eq('destination.dateFrom', ':dateFrom'), + ) + )) + ->setParameters([ + 'documentType' => Upload::TYPE_CONTRACT, + 'dateFrom' => $contractDueDate, + ]) + ->getQuery() + ->getResult() + ; + + if (0 === $count = count($dispositions)) { + $message = 'No reminders regarding due contract uploads to be sent'; + $io->info($message); + $this->logger->info($message); + return; + } + + foreach ($dispositions as $disposition) { + $teamer = $disposition->getTeamer(); + + $this->mailer->createAndSendEmail([ + 'disposition' => $disposition, + ], [ + 'to' => $teamer->getCommunication()->getEmail(), + 'subject' => 'Reminder: Fehlende Dokumente', + 'template' => 'email/reminder_contract_upload.html.twig', + ]); + } + + $message = 'Sent '.$count.' reminders about due contract uploads'; + $io->info($message); + $this->logger->info($message); + } + + private function sendInvoiceUploadReminders(SymfonyStyle $io): void + { + // Find dispositions of assignments with due invoice upload + $repository = $this->entityManager->getRepository(Disposition::class); + + // Invoices have to be uploaded until 14 days after end of assignment. Reminder + // is sent three days before end of this period thus on the 11th day after end of assignment. + $invoiceDueDate = (new \DateTimeImmutable())->modify('-11 days'); + + $qb = $repository->createQueryBuilder('disposition'); + + $dispositions = $qb + ->select('disposition', 'assignment', 'destination') + ->innerJoin('disposition.assignment', 'assignment') + ->innerJoin('assignment.destination', 'destination') + ->leftJoin('disposition.documents', 'document', Join::WITH, 'document.type = :documentType') + ->where($qb->expr()->andX( + $qb->expr()->isNull('document'), + $qb->expr()->orX( + // Due date can be determined by assignment's date which potentially overrides destination date + $qb->expr()->andX( + $qb->expr()->isNotNull('assignment.dateTo'), + $qb->expr()->eq('assignment.dateTo', ':dateTo') + ), + $qb->expr()->eq('destination.dateTo', ':dateTo'), + ) + )) + ->setParameters([ + 'documentType' => Upload::TYPE_INVOICE, + 'dateTo' => $invoiceDueDate, + ]) + ->getQuery() + ->getResult() + ; + + if (0 === $count = count($dispositions)) { + $message = 'No reminders regarding due invoice uploads to be sent'; + $io->info($message); + $this->logger->info($message); + return; + } + + foreach ($dispositions as $disposition) { + $teamer = $disposition->getTeamer(); + + $this->mailer->createAndSendEmail([ + 'disposition' => $disposition, + ], [ + 'to' => $teamer->getCommunication()->getEmail(), + 'subject' => 'Reminder: Fehlende Dokumente', + 'template' => 'email/reminder_invoice_upload.html.twig', + ]); + } + + $message = 'Sent '.$count.' reminders about due invoice uploads'; + $io->info($message); + $this->logger->info($message); + } +} diff --git a/src/Command/FlushLogsCommand.php b/src/Command/FlushLogsCommand.php deleted file mode 100644 index 44cba33..0000000 --- a/src/Command/FlushLogsCommand.php +++ /dev/null @@ -1,41 +0,0 @@ -modify('-30 days'); - try { - $result = $this - ->connection - ->executeQuery('DELETE FROM log WHERE log.timestamp < ?', [$dueDate->format('Y-m-d H:i:s')]); - $io->info('Deleted '.$result->rowCount().' log entries'); - } catch (Exception $e) { - $io->error('Unable to delete log entries: '.$e->getMessage()); - } - - return Command::SUCCESS; - } -} diff --git a/templates/email/reminder_contract_upload.html.twig b/templates/email/reminder_contract_upload.html.twig new file mode 100644 index 0000000..3dd2267 --- /dev/null +++ b/templates/email/reminder_contract_upload.html.twig @@ -0,0 +1,25 @@ +{% extends 'email/layout.html.twig' %} + +{% block body %} +

+ Reminder zu fehlenden Dokumenten +

+

+ + Honorarvertrag zu deinem Einsatz {{ disposition.assignment.destination}}. + +

+

+ Dein Honorarvertrag wurde noch nicht ausgefüllt. Du hast noch 3 Tage Zeit diesen ausgefüllt hochzuladen. Hältst + du diese Frist nicht ein, verfällt das Einsatzangebot und deine Teamkolleg:innen erhalten die Chance, diesen + Einsatz zu besetzen. +

+

+ Setz dich am besten direkt dran, damit du es nicht vergisst! 😊 +

+

+ + Zum Portal + +

+{% endblock %} \ No newline at end of file diff --git a/templates/email/reminder_invoice_upload.html.twig b/templates/email/reminder_invoice_upload.html.twig new file mode 100644 index 0000000..298adcb --- /dev/null +++ b/templates/email/reminder_invoice_upload.html.twig @@ -0,0 +1,24 @@ +{% extends 'email/layout.html.twig' %} + +{% block body %} +

+ Reminder zu fehlenden Dokumenten +

+

+ + Honorarnote zu deinem Einsatz {{ disposition.assignment.destination}}. + +

+

+ Deine Honorarnote wurde noch nicht ausgefüllt. Du hast noch 3 Tage Zeit diese ausgefüllt hochzuladen. Hältst du + diese Frist nicht ein, verfällt die Honorarnote und du erhältst kein Geld mehr! +

+

+ Setz dich am besten direkt dran, damit du es nicht vergisst! 😊 +

+

+ + Zum Portal + +

+{% endblock %} \ No newline at end of file