Browse code

Check-In notifications WIP

Benjamin Roth authored on04/08/2025 10:45:29
Showing4 changed files
... ...
@@ -31,6 +31,13 @@ services:
31 31
             - "@database_connection"
32 32
             - "@logger"
33 33
 
34
+    vonrotenberg.cron.wa_checkin_notifications:
35
+        class: vonRotenberg\WeinanlieferungBundle\Cron\SendCheckInNotificationJob
36
+        public: true
37
+        arguments:
38
+            - "@database_connection"
39
+            - "@logger"
40
+
34 41
     vonrotenberg.wa.slot_checker:
35 42
         class: vonRotenberg\WeinanlieferungBundle\SlotChecker
36 43
         public: true
... ...
@@ -215,6 +215,10 @@ $GLOBALS['TL_DCA']['tl_vr_wa_reservation'] = array
215 215
             'eval'                    => array('rgxp' => 'datim', 'datepicker' => true, 'tl_class' => 'clr w50 wizard'),
216 216
             'sql'                     => "int(10) unsigned NOT NULL default '0'"
217 217
         ),
218
+        'checked_in_nc_sent'      => array
219
+        (
220
+            'sql' => "int(10) unsigned NOT NULL default '0'"
221
+        ),
218 222
         'behaelter_numbers' => array
219 223
         (
220 224
             'exclude'                 => true,
221 225
new file mode 100644
... ...
@@ -0,0 +1,50 @@
1
+<?php
2
+
3
+declare(strict_types=1);
4
+
5
+/*
6
+ * This file is part of eSales Media SinglereisenBundle
7
+ *
8
+ * (c) Benjamin Roth
9
+ *
10
+ * @license proprietary
11
+ */
12
+
13
+namespace vonRotenberg\WeinanlieferungBundle\Command;
14
+
15
+use Contao\CoreBundle\Framework\ContaoFramework;
16
+use Contao\System;
17
+use Symfony\Component\Console\Command\Command;
18
+use Symfony\Component\Console\Input\InputInterface;
19
+use Symfony\Component\Console\Output\OutputInterface;
20
+use vonRotenberg\WeinanlieferungBundle\Cron\SendCheckInNotificationJob;
21
+
22
+class SendCheckInNotificationCommand extends Command
23
+{
24
+    public function __construct(ContaoFramework $framework)
25
+    {
26
+        $this->framework = $framework;
27
+
28
+        parent::__construct();
29
+    }
30
+
31
+    protected function configure(): void
32
+    {
33
+        $this
34
+            ->setName('luumicore:sendCheckInNotifications')
35
+            ->setDescription('Sends notifications about completed check-ins.')
36
+        ;
37
+    }
38
+
39
+    protected function execute(InputInterface $input, OutputInterface $output): int
40
+    {
41
+        $this->framework->initialize();
42
+
43
+        /** @var SendCheckInNotificationJob $wa_service */
44
+        $wa_service = System::getContainer()->get("vonrotenberg.cron.wa_checkin_notifications");
45
+
46
+        $wa_service('cli');
47
+
48
+        return 0;
49
+    }
50
+}
0 51
new file mode 100644
... ...
@@ -0,0 +1,217 @@
1
+<?php
2
+
3
+declare(strict_types=1);
4
+
5
+/*
6
+* This file is part of contao-weinanlieferung-bundle.
7
+*
8
+* (c) vonRotenberg
9
+*
10
+* @license commercial
11
+*/
12
+
13
+namespace vonRotenberg\WeinanlieferungBundle\Cron;
14
+
15
+use Contao\Config;
16
+use Contao\CoreBundle\Monolog\ContaoContext;
17
+use Contao\CoreBundle\ServiceAnnotation\CronJob;
18
+use Contao\Date;
19
+use Contao\MemberModel;
20
+use Contao\StringUtil;
21
+use Contao\System;
22
+use Doctrine\DBAL\Connection;
23
+use NotificationCenter\Model\Notification;
24
+use Psr\Log\LoggerInterface;
25
+use Psr\Log\LogLevel;
26
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLageModel;
27
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel;
28
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel;
29
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungStandortModel;
30
+
31
+/**
32
+ * @CronJob("*\/10 * * * *")
33
+ */
34
+class SendCheckInNotificationJob
35
+{
36
+    /** @var LoggerInterface */
37
+    private $logger;
38
+
39
+    /** @var Connection */
40
+    private $db;
41
+
42
+    public function __construct(Connection $db, LoggerInterface $logger)
43
+    {
44
+        $this->logger = $logger;
45
+        $this->db = $db;
46
+    }
47
+
48
+    public function __invoke(string $scope)
49
+    {
50
+        System::loadLanguageFile('default', 'de');
51
+        list($admin_name, $admin_email) = StringUtil::splitFriendlyEmail(Config::get('adminEmail'));
52
+        $intNotificationsCount = 0;
53
+
54
+        // Get locations
55
+        $Locations = WeinanlieferungStandortModel::findBy('nc_enable', '1');
56
+
57
+        if ($Locations !== null) {
58
+            while ($Locations->next()) {
59
+                $Location = $Locations->current();
60
+                $time = Date::floorToMinute();
61
+
62
+                // Do we have check-ins that need notifications
63
+                // The key difference from SendBookingChangeNotificationJob is the WHERE clause:
64
+                // We look for r.checked_in = '1' AND r.checked_in_on > r.checked_in_nc_sent
65
+                $Bookings = $this->db->executeQuery(
66
+                    "SELECT r.id, r.pid, r.uid, r.behaelter, r.sorten, r.lage, r.ernteart, r.upload, r.checked_in_nc_sent,
67
+                    s.date as slot_date, s.time as slot_time, s.behaelter as slot_behaelter,
68
+                    s.sorten as slot_sorten, s.ernteart as slot_ernteart, s.lage as slot_lage,
69
+                    s.anmerkungen as slot_anmerkungen, r.approved, r.approved_on, r.checked_in,
70
+                    r.checked_in_on, r.behaelter_numbers
71
+                    FROM tl_vr_wa_reservation r
72
+                    INNER JOIN tl_vr_wa_slot s ON s.id = r.pid
73
+                    WHERE s.pid = ? AND r.checked_in = '1' AND r.checked_in_on > r.checked_in_nc_sent AND s.published='1'",
74
+                    [$Location->id]
75
+                );
76
+
77
+                // Load notification model if we have check-ins to notify
78
+                if ($Bookings->rowCount() && ($Notification = Notification::findByPk($Location->nc_notification)) !== null) {
79
+                    foreach ($Bookings->iterateAssociative() as $Booking) {
80
+                        // Get member
81
+                        $Member = MemberModel::findOneBy(
82
+                            ["id = ?", "login='1' AND (start='' OR start<='$time') AND (stop='' OR stop>'" . ($time + 60) . "') AND disable=''"],
83
+                            [$Booking['uid']]
84
+                        );
85
+
86
+                        // Send notification to member
87
+                        if ($Member !== null) {
88
+                            $arrSortenAvailable = [];
89
+                            $arrErnteartAvailable = [];
90
+                            $arrLageAvailable = [];
91
+                            $arrSortenBooked = [];
92
+                            $arrErnteartBooked = [];
93
+                            $arrLageBooked = [];
94
+
95
+                            $SortenLeseart = StringUtil::deserialize($Booking['slot_sorten'], true);
96
+                            foreach ($SortenLeseart as $sorteLeseart) {
97
+                                $objSorte = WeinanlieferungRebsorteModel::findByPk($sorteLeseart['sorte']);
98
+                                $objLeseart = WeinanlieferungLeseartModel::findByPk($sorteLeseart['leseart']);
99
+                                if ($objSorte !== null && $objLeseart !== null) {
100
+                                    $arrSortenAvailable[$objSorte->id . ',' . $objLeseart->id] =
101
+                                        $objSorte->title . ' ' . $objLeseart->title;
102
+                                }
103
+                            }
104
+
105
+                            if (!empty($Booking['sorten'])) {
106
+                                $SortenLeseart = explode(';', $Booking['sorten']);
107
+                                foreach ($SortenLeseart as $sorteLeseart) {
108
+                                    if (empty($sorteLeseart)) {
109
+                                        continue;
110
+                                    }
111
+                                    $parts = explode(',', $sorteLeseart);
112
+                                    if (count($parts) >= 2) {
113
+                                        $sorte = $parts[0];
114
+                                        $leseart = $parts[1];
115
+                                        $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte);
116
+                                        $objLeseart = WeinanlieferungLeseartModel::findByPk($leseart);
117
+                                        if ($objSorte !== null && $objLeseart !== null) {
118
+                                            $arrSortenBooked[$objSorte->id . ',' . $objLeseart->id] =
119
+                                                $objSorte->title . ' ' . $objLeseart->title;
120
+                                        }
121
+                                    }
122
+                                }
123
+                            }
124
+
125
+                            if (!empty($Booking['ernteart'])) {
126
+                                foreach (explode(',', $Booking['ernteart']) as $ernteart) {
127
+                                    if (empty($ernteart)) {
128
+                                        continue;
129
+                                    }
130
+                                    $arrErnteartBooked[$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
131
+                                }
132
+                            }
133
+
134
+                            if (!empty($Booking['slot_ernteart'])) {
135
+                                foreach (explode(',', $Booking['slot_ernteart']) as $ernteart) {
136
+                                    if (empty($ernteart)) {
137
+                                        continue;
138
+                                    }
139
+                                    $arrErnteartAvailable[$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
140
+                                }
141
+                            }
142
+
143
+                            if (!empty($Booking['slot_lage'])) {
144
+                                foreach (explode(',', $Booking['slot_lage']) as $lage) {
145
+                                    if (empty($lage)) {
146
+                                        continue;
147
+                                    }
148
+                                    if (($Lage = WeinanlieferungLageModel::findByPk($lage)) !== null) {
149
+                                        $arrLageAvailable[$Lage->id] = $Lage->title;
150
+                                    }
151
+                                }
152
+                            }
153
+
154
+                            if (!empty($Booking['lage'])) {
155
+                                foreach (explode(',', $Booking['lage']) as $lage) {
156
+                                    if (empty($lage)) {
157
+                                        continue;
158
+                                    }
159
+                                    if (($Lage = WeinanlieferungLageModel::findByPk($lage)) !== null) {
160
+                                        $arrLageBooked[$Lage->id] = $Lage->title;
161
+                                    }
162
+                                }
163
+                            }
164
+
165
+                            // Send notification with all booking data
166
+                            // We include the check-in specific fields: checked_in, checked_in_on, behaelter_numbers
167
+                            $Notification->send(
168
+                                [
169
+                                    'member_email'       => $Member->email . ($Member->nc_news_additionalEmail ? ',' . $Member->nc_news_additionalEmail : ''),
170
+                                    'member_firstname'   => $Member->firstname,
171
+                                    'member_lastname'    => $Member->lastname,
172
+                                    'member_memberno'    => $Member->memberno,
173
+                                    'slot_date'          => Date::parse(Date::getNumericDateFormat(), $Booking['slot_date']),
174
+                                    'slot_time'          => Date::parse(Date::getNumericTimeFormat(), $Booking['slot_time']),
175
+                                    'slot_standort'      => $Location->title,
176
+                                    'slot_behaelter'     => $Booking['slot_behaelter'],
177
+                                    'slot_sorten'        => implode(', ', $arrSortenAvailable),
178
+                                    'slot_ernteart'      => implode(', ', $arrErnteartAvailable),
179
+                                    'slot_lage'          => implode(', ', $arrLageAvailable),
180
+                                    'slot_anmerkungen'   => $Booking['slot_anmerkungen'],
181
+                                    'booking_checked_in_ncsent'     => $Booking['checked_in_nc_sent'],
182
+                                    'booking_behaelter'  => $Booking['behaelter'],
183
+                                    'booking_sorten'     => implode(', ', $arrSortenBooked),
184
+                                    'booking_ernteart'   => implode(', ', $arrErnteartBooked),
185
+                                    'booking_lage'       => implode(', ', $arrLageBooked),
186
+                                    'booking_approved'   => $Booking['approved'],
187
+                                    'booking_approved_on'=> $Booking['approved_on'],
188
+                                    'booking_checked_in' => $Booking['checked_in'],
189
+                                    'booking_checked_in_on' => $Booking['checked_in_on'],
190
+                                    'booking_behaelter_numbers' => $Booking['behaelter_numbers'],
191
+                                    'admin_email'        => $admin_email,
192
+                                ],
193
+                                $GLOBALS['TL_LANGUAGE']
194
+                            );
195
+
196
+                            // Update the checked_in_nc_sent timestamp to mark this notification as sent
197
+                            $intNotificationsCount++;
198
+                            $this->db->executeStatement(
199
+                                "UPDATE tl_vr_wa_reservation SET checked_in_nc_sent = ? WHERE id = ?",
200
+                                [$time, $Booking['id']]
201
+                            );
202
+                        }
203
+                    }
204
+                }
205
+            }
206
+
207
+            // Log notifications sent
208
+            if ($intNotificationsCount) {
209
+                $this->logger->log(
210
+                    LogLevel::INFO,
211
+                    sprintf('WA check-in notifications(%s) has been sent', $intNotificationsCount),
212
+                    ['contao' => new ContaoContext(__METHOD__, 'CRON')]
213
+                );
214
+            }
215
+        }
216
+    }
217
+}