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
... ...
@@ -101,3 +101,66 @@ $GLOBALS['NOTIFICATION_CENTER']['NOTIFICATION_TYPE']['weinanlieferung']['wa_book
101 101
         'booking_lage',
102 102
     )
103 103
 );
104
+
105
+$GLOBALS['NOTIFICATION_CENTER']['NOTIFICATION_TYPE']['weinanlieferung']['wa_slot_change'] = array
106
+(
107
+    'recipients'    => array
108
+    (
109
+        'member_email', // The email address of the recipient
110
+        'admin_email' // The email address of the recipient
111
+    ),
112
+    'email_sender_address'    => array
113
+    (
114
+        'member_email', // The email address of the recipient
115
+        'admin_email' // The email address of the recipient
116
+    ),
117
+    'email_sender_name'    => array
118
+    (
119
+        'member_email', // The email address of the recipient
120
+        'admin_email' // The email address of the recipient
121
+    ),
122
+    'email_subject'        => array
123
+    (
124
+        'domain',
125
+        'member_email',
126
+        'admin_email',
127
+        'slot_date',
128
+        'slot_time',
129
+        'slot_standort',
130
+        'slot_ncsent',
131
+        'slot_behaelter',
132
+        'slot_behaelterBuchbar',
133
+    ),
134
+    'email_text'    => array
135
+    (
136
+        'member_firstname', // The firstname of the recipient
137
+        'member_lastname', // The lastname of the recipient
138
+        'member_memberno', // The member account number of the recipient
139
+        'slot_date',
140
+        'slot_time',
141
+        'slot_standort',
142
+        'slot_behaelter',
143
+        'slot_behaelterBuchbar',
144
+        'slot_sorten',
145
+        'slot_ernteart',
146
+        'slot_lage',
147
+        'slot_anmerkungen',
148
+        'slot_ncsent',
149
+    ),
150
+    'email_html'    => array
151
+    (
152
+        'member_firstname', // The firstname of the recipient
153
+        'member_lastname', // The lastname of the recipient
154
+        'member_memberno', // The member account number of the recipient
155
+        'slot_date',
156
+        'slot_time',
157
+        'slot_standort',
158
+        'slot_behaelter',
159
+        'slot_behaelterBuchbar',
160
+        'slot_sorten',
161
+        'slot_ernteart',
162
+        'slot_lage',
163
+        'slot_anmerkungen',
164
+        'slot_ncsent',
165
+    )
166
+);
... ...
@@ -117,6 +117,11 @@ $GLOBALS['TL_DCA']['tl_vr_wa_slot'] = array
117 117
         (
118 118
             'sql' => "int(10) unsigned NOT NULL default '0'"
119 119
         ),
120
+        'nc_sent'      => array
121
+        (
122
+            'sql' => "int(10) unsigned NOT NULL default '0'"
123
+        ),
124
+
120 125
         'date'        => array
121 126
         (
122 127
             'exclude'   => true,
... ...
@@ -111,7 +111,7 @@ $GLOBALS['TL_DCA']['tl_vr_wa_standort'] = array
111 111
     // Subpalettes
112 112
     'subpalettes' => array
113 113
     (
114
-        'nc_enable' => 'nc_notification'
114
+        'nc_enable' => 'nc_notification,nc_notification_slots,nc_notification_groups'
115 115
     ),
116 116
 
117 117
     // Fields
... ...
@@ -151,6 +151,22 @@ $GLOBALS['TL_DCA']['tl_vr_wa_standort'] = array
151 151
             'inputType'                 => 'select',
152 152
             'eval'                      => array('mandatory'=>true,'includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'),
153 153
             'sql'                       => "int(10) unsigned NOT NULL default '0'"
154
+        ),
155
+        'nc_notification_slots' => array
156
+        (
157
+            'exclude'                   => true,
158
+            'inputType'                 => 'select',
159
+            'eval'                      => array('mandatory'=>true,'includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'),
160
+            'sql'                       => "int(10) unsigned NOT NULL default '0'"
161
+        ),
162
+        'nc_notification_groups' => array
163
+        (
164
+            'exclude'                 => true,
165
+            'inputType'               => 'checkbox',
166
+            'foreignKey'              => 'tl_member_group.name',
167
+            'eval'                    => array('mandatory'=>true, 'multiple'=>true,'tl_class'=>'clr'),
168
+            'sql'                     => "blob NULL",
169
+            'relation'                => array('type'=>'hasMany', 'load'=>'lazy')
154 170
         )
155 171
     )
156 172
 );
... ...
@@ -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
 }