Browse code

Add slot notifications

Benjamin Roth authored on23/08/2024 13:33:02
Showing8 changed files
... ...
@@ -19,6 +19,13 @@ services:
19 19
             - "@database_connection"
20 20
             - "@logger"
21 21
 
22
+    vonrotenberg.cron.wa_slot_notifications:
23
+        class: vonRotenberg\WeinanlieferungBundle\Cron\SendSlotChangeNotificationJob
24
+        public: true
25
+        arguments:
26
+            - "@database_connection"
27
+            - "@logger"
28
+
22 29
     vonrotenberg.wa.slot_checker:
23 30
         class: vonRotenberg\WeinanlieferungBundle\SlotChecker
24 31
         public: true
... ...
@@ -109,3 +109,66 @@ $GLOBALS['NOTIFICATION_CENTER']['NOTIFICATION_TYPE']['weinanlieferung']['wa_book
109 109
         'booking_lage',
110 110
     )
111 111
 );
112
+
113
+$GLOBALS['NOTIFICATION_CENTER']['NOTIFICATION_TYPE']['weinanlieferung']['wa_slot_change'] = array
114
+(
115
+    'recipients'    => array
116
+    (
117
+        'member_email', // The email address of the recipient
118
+        'admin_email' // The email address of the recipient
119
+    ),
120
+    'email_sender_address'    => array
121
+    (
122
+        'member_email', // The email address of the recipient
123
+        'admin_email' // The email address of the recipient
124
+    ),
125
+    'email_sender_name'    => array
126
+    (
127
+        'member_email', // The email address of the recipient
128
+        'admin_email' // The email address of the recipient
129
+    ),
130
+    'email_subject'        => array
131
+    (
132
+        'domain',
133
+        'member_email',
134
+        'admin_email',
135
+        'slot_date',
136
+        'slot_time',
137
+        'slot_standort',
138
+        'slot_ncsent',
139
+        'slot_behaelter',
140
+        'slot_behaelterBuchbar',
141
+    ),
142
+    'email_text'    => array
143
+    (
144
+        'member_firstname', // The firstname of the recipient
145
+        'member_lastname', // The lastname of the recipient
146
+        'member_memberno', // The member account number of the recipient
147
+        'slot_date',
148
+        'slot_time',
149
+        'slot_standort',
150
+        'slot_behaelter',
151
+        'slot_behaelterBuchbar',
152
+        'slot_sorten',
153
+        'slot_ernteart',
154
+        'slot_lage',
155
+        'slot_anmerkungen',
156
+        'slot_ncsent',
157
+    ),
158
+    'email_html'    => array
159
+    (
160
+        'member_firstname', // The firstname of the recipient
161
+        'member_lastname', // The lastname of the recipient
162
+        'member_memberno', // The member account number of the recipient
163
+        'slot_date',
164
+        'slot_time',
165
+        'slot_standort',
166
+        'slot_behaelter',
167
+        'slot_behaelterBuchbar',
168
+        'slot_sorten',
169
+        'slot_ernteart',
170
+        'slot_lage',
171
+        'slot_anmerkungen',
172
+        'slot_ncsent',
173
+    )
174
+);
... ...
@@ -119,6 +119,11 @@ $GLOBALS['TL_DCA']['tl_vr_wa_slot'] = array
119 119
         (
120 120
             'sql' => "int(10) unsigned NOT NULL default '0'"
121 121
         ),
122
+        'nc_sent'      => array
123
+        (
124
+            'sql' => "int(10) unsigned NOT NULL default '0'"
125
+        ),
126
+
122 127
         'date'        => array
123 128
         (
124 129
             'exclude'   => true,
... ...
@@ -116,7 +116,7 @@ $GLOBALS['TL_DCA']['tl_vr_wa_standort'] = array
116 116
     // Subpalettes
117 117
     'subpalettes' => array
118 118
     (
119
-        'nc_enable' => 'nc_notification'
119
+        'nc_enable' => 'nc_notification,nc_notification_slots,nc_notification_groups'
120 120
     ),
121 121
 
122 122
     // Fields
... ...
@@ -174,6 +174,22 @@ $GLOBALS['TL_DCA']['tl_vr_wa_standort'] = array
174 174
             'inputType'                 => 'select',
175 175
             'eval'                      => array('mandatory'=>true,'includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'),
176 176
             'sql'                       => "int(10) unsigned NOT NULL default '0'"
177
+        ),
178
+        'nc_notification_slots' => array
179
+        (
180
+            'exclude'                   => true,
181
+            'inputType'                 => 'select',
182
+            'eval'                      => array('mandatory'=>true,'includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'),
183
+            'sql'                       => "int(10) unsigned NOT NULL default '0'"
184
+        ),
185
+        'nc_notification_groups' => array
186
+        (
187
+            'exclude'                 => true,
188
+            'inputType'               => 'checkbox',
189
+            'foreignKey'              => 'tl_member_group.name',
190
+            'eval'                    => array('mandatory'=>true, 'multiple'=>true,'tl_class'=>'clr'),
191
+            'sql'                     => "blob NULL",
192
+            'relation'                => array('type'=>'hasMany', 'load'=>'lazy')
177 193
         )
178 194
     )
179 195
 );
... ...
@@ -11,3 +11,5 @@
11 11
 $GLOBALS['TL_LANG']['tl_nc_notification']['type']['weinanlieferung'] = 'Weinanlieferung';
12 12
 $GLOBALS['TL_LANG']['tl_nc_notification']['type']['wa_booking_change'][0] = 'Neue Buchung / Buchungsupdate';
13 13
 $GLOBALS['TL_LANG']['tl_nc_notification']['type']['wa_booking_change'][1] = 'Wenn eine neue Buchung eingeht, oder eine vorhandene Buchung geändert wird.';
14
+$GLOBALS['TL_LANG']['tl_nc_notification']['type']['wa_slot_change'][0] = 'Neuer buchbarer Slot / Slot-Aktualisierung';
15
+$GLOBALS['TL_LANG']['tl_nc_notification']['type']['wa_slot_change'][1] = 'Wenn eine neuer buchbarer Slot zur Verfügung steht, oder ein vorhandener Slot geändert wird.';
14 16
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\SendSlotChangeNotificationJob;
21
+
22
+class SendSlotChangeNotificationCommand 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:sendSlotNotifications')
35
+            ->setDescription('Sends notifications about newly added or modified slots once they\'re bookable.')
36
+        ;
37
+    }
38
+
39
+    protected function execute(InputInterface $input, OutputInterface $output): int
40
+    {
41
+        $this->framework->initialize();
42
+
43
+        /** @var SendSlotChangeNotificationJob $wa_service */
44
+        $wa_service = System::getContainer()->get("vonrotenberg.cron.wa_slot_notifications");
45
+
46
+        $wa_service('cli');
47
+
48
+        return 0;
49
+    }
50
+}
0 51
new file mode 100644
... ...
@@ -0,0 +1,161 @@
1
+<?php
2
+
3
+declare(strict_types=1);
4
+
5
+/*
6
+* This file is part of newsmailer bundle for Contao.
7
+*
8
+* (c) Benjamin Roth
9
+*
10
+* @license LGPL-3.0-or-later
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\WeinanlieferungSlotsModel;
30
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungStandortModel;
31
+
32
+/**
33
+ * @CronJob("*\/10 * * * *")
34
+ */
35
+class SendSlotChangeNotificationJob
36
+{
37
+    /** @var LoggerInterface  */
38
+    private $logger;
39
+
40
+    /** @var Connection */
41
+    private $db;
42
+
43
+    public function __construct(Connection $db, LoggerInterface $logger)
44
+    {
45
+        $this->logger = $logger;
46
+        $this->db = $db;
47
+    }
48
+
49
+    public function __invoke(string $scope)
50
+    {
51
+        System::loadLanguageFile('default','de');
52
+        list($admin_name,$admin_email) = StringUtil::splitFriendlyEmail(Config::get('adminEmail'));
53
+        $intNotificationsCount = 0;
54
+
55
+        // Get locations
56
+        $Locations = WeinanlieferungStandortModel::findBy('nc_enable','1');
57
+
58
+        if ($Locations !== null)
59
+        {
60
+            while ($Locations->next())
61
+            {
62
+                $Location = $Locations->current();
63
+                $time = Date::floorToMinute();
64
+
65
+                // Get members
66
+                $memberGroupIds = StringUtil::deserialize($Location->nc_notification_groups,true);
67
+                $memberGroupSqlFragment = [];
68
+                if (!count($memberGroupIds))
69
+                {
70
+                    continue;
71
+                }
72
+                foreach ($memberGroupIds as $memberGroupId)
73
+                {
74
+                    $memberGroupSqlFragment[] = "`groups` LIKE '%\"$memberGroupId\"%'";
75
+                }
76
+                $Members = MemberModel::findBy(array("(".implode(' OR ',$memberGroupSqlFragment).")","(start='' OR start<='$time') AND (stop='' OR stop>'" . ($time + 60) . "') AND disable=''"),null);
77
+
78
+                // Do we have updateable items
79
+                $Slots = $this->db->executeQuery("SELECT s.id, s.date, s.time, s.duration, s.behaelter, s.sorten, s.ernteart, s.lage, s.anmerkungen, s.nc_sent FROM tl_vr_wa_slot s WHERE s.pid = ? AND s.nc_sent < s.tstamp AND s.published='1' AND (buchbar_ab='' OR buchbar_ab<='$time') AND (buchbar_bis='' OR buchbar_bis>'" . ($time + 60) . "')",[$Location->id]);
80
+
81
+                // Load groups and notification models if we have news to share
82
+                if ($Members !== null && $Slots->rowCount() && ($Notification = Notification::findByPk($Location->nc_notification_slots)) !== null)
83
+                {
84
+                    foreach ($Slots->iterateAssociative() as $Slot)
85
+                    {
86
+                        $arrSortenAvailable = [];
87
+                        $arrErnteartAvailable = [];
88
+                        $arrLageAvailable = [];
89
+                        $arrSortenBooked = [];
90
+                        $arrErnteartBooked = [];
91
+                        $arrLageBooked = [];
92
+
93
+                        $SlotModel = WeinanlieferungSlotsModel::findByPk($Slot['id']);
94
+
95
+                        if (isset($Slot['slot_sorten']))
96
+                        {
97
+                            $SortenLeseart = StringUtil::deserialize($Slot['sorten'],true);
98
+                            foreach($SortenLeseart as $sorteLeseart)
99
+                            {
100
+                                $objSorte = WeinanlieferungRebsorteModel::findByPk($sorteLeseart['sorte']);
101
+                                $objLeseart = WeinanlieferungLeseartModel::findByPk($sorteLeseart['leseart']);
102
+                                $arrSortenAvailable[$objSorte->id.','.$objLeseart->id] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
103
+                            }
104
+                        }
105
+                        if (isset($Slot['ernteart']))
106
+                        {
107
+                            foreach (explode(',', $Slot['ernteart']) as $ernteart)
108
+                            {
109
+                                $arrErnteartAvailable[$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
110
+                            }
111
+                        }
112
+                        if (isset($Slot['lage']))
113
+                        {
114
+                            foreach (explode(',', $Slot['lage']) as $lage)
115
+                            {
116
+                                if (($Lage = WeinanlieferungLageModel::findByPk($lage)) !== null)
117
+                                {
118
+                                    $arrLageAvailable[$Lage->id] = $Lage->title;
119
+                                }
120
+                            }
121
+                        }
122
+
123
+                        // Send notification to member
124
+                        foreach ($Members as $Member)
125
+                        {
126
+                            $Notification->send(array
127
+                            (
128
+                                'member_email'          => $Member->email . ($Member->nc_news_additionalEmail ? ',' . $Member->nc_news_additionalEmail : ''),
129
+                                'member_firstname'      => $Member->firstname,
130
+                                'member_lastname'       => $Member->lastname,
131
+                                'member_memberno'       => $Member->memberno,
132
+                                'slot_date'             => Date::parse(Date::getNumericDateFormat(),$Slot['date']),
133
+                                'slot_time'             => Date::parse(Date::getNumericTimeFormat(),$Slot['time']),
134
+                                'slot_standort'         => $Location->title,
135
+                                'slot_behaelter'        => $Slot['behaelter'],
136
+                                'slot_behaelterBuchbar' => $SlotModel !== null ? $SlotModel->getAvailableBehaelter() : '',
137
+                                'slot_sorten'           => implode(', ',$arrSortenAvailable),
138
+                                'slot_ernteart'         => implode(', ',$arrErnteartAvailable),
139
+                                'slot_lage'             => implode(', ',$arrLageAvailable),
140
+                                'slot_anmerkungen'      => $Slot['anmerkungen'],
141
+                                'slot_ncsent'           => $Slot['nc_sent'],
142
+                                'admin_email'           => $admin_email,
143
+                            ),
144
+                                $GLOBALS['TL_LANGUAGE']);
145
+
146
+                            $intNotificationsCount++;
147
+                        }
148
+
149
+                        $this->db->executeStatement("UPDATE tl_vr_wa_slot SET nc_sent = ? WHERE id = ?",[$time,$Slot['id']]);
150
+                    }
151
+                }
152
+            }
153
+
154
+            // Flag news as sent
155
+            if ($intNotificationsCount)
156
+            {
157
+                $this->logger->log(LogLevel::INFO, sprintf('WA notifications(%s) has been sent',$intNotificationsCount), array('contao' => new ContaoContext(__METHOD__, 'CRON')));
158
+            }
159
+        }
160
+    }
161
+}
... ...
@@ -46,4 +46,25 @@ class WeinanlieferungStandortContainerListener
46 46
 
47 47
         return $arrOptions;
48 48
     }
49
+
50
+    /**
51
+     * @Callback(table="tl_vr_wa_standort", target="fields.nc_notification_slots.options")
52
+     */
53
+    public function onNcNotificationSlotsOptionsCallback(DataContainer $dc)
54
+    {
55
+        $arrOptions = [];
56
+        $objNotifications = $this->db->executeQuery("SELECT id,title FROM tl_nc_notification WHERE type='wa_slot_change' ORDER BY title");
57
+
58
+        if (!$objNotifications->rowCount())
59
+        {
60
+            return $arrOptions;
61
+        }
62
+
63
+        foreach ($objNotifications->fetchAllAssociative() as $notification)
64
+        {
65
+            $arrOptions[$notification['id']] = $notification['title'];
66
+        }
67
+
68
+        return $arrOptions;
69
+    }
49 70
 }