Browse code

Add booking notification

Benjamin Roth authored on04/09/2023 10:47:12
Showing11 changed files
... ...
@@ -11,3 +11,10 @@ services:
11 11
     vonRotenberg\WeinanlieferungBundle\Migration\RebsortenMigration:
12 12
         tags:
13 13
             - { name: contao.migration, priority: 0 }
14
+
15
+    vonrotenberg.cron.wa_booking_notifications:
16
+        class: vonRotenberg\WeinanlieferungBundle\Cron\SendBookingChangeNotificationJob
17
+        public: true
18
+        arguments:
19
+            - "@database_connection"
20
+            - "@logger"
... ...
@@ -32,3 +32,66 @@ $GLOBALS['TL_MODELS']['tl_vr_wa_rebsorte'] = WeinanlieferungRebsorteModel::class
32 32
 $GLOBALS['TL_MODELS']['tl_vr_wa_leseart'] = WeinanlieferungLeseartModel::class;
33 33
 $GLOBALS['TL_MODELS']['tl_vr_wa_lage'] = WeinanlieferungLageModel::class;
34 34
 $GLOBALS['TL_MODELS']['tl_vr_wa_reservation'] = WeinanlieferungReservationModel::class;
35
+
36
+
37
+// Notification
38
+$GLOBALS['NOTIFICATION_CENTER']['NOTIFICATION_TYPE']['weinanlieferung']['wa_booking_change'] = array
39
+(
40
+    'recipients'    => array
41
+    (
42
+        'member_email', // The email address of the recipient
43
+        'admin_email' // The email address of the recipient
44
+    ),
45
+    'email_sender_address'    => array
46
+    (
47
+        'member_email', // The email address of the recipient
48
+        'admin_email' // The email address of the recipient
49
+    ),
50
+    'email_sender_name'    => array
51
+    (
52
+        'member_email', // The email address of the recipient
53
+        'admin_email' // The email address of the recipient
54
+    ),
55
+    'email_subject'        => array
56
+    (
57
+        'domain',
58
+        'member_email',
59
+        'admin_email',
60
+        'slot_date',
61
+        'slot_time'
62
+    ),
63
+    'email_text'    => array
64
+    (
65
+        'member_firstname', // The firstname of the recipient
66
+        'member_lastname', // The lastname of the recipient
67
+        'member_memberno', // The member account number of the recipient
68
+        'slot_date',
69
+        'slot_time',
70
+        'slot_behaelter',
71
+        'slot_sorten',
72
+        'slot_ernteart',
73
+        'slot_lage',
74
+        'slot_anmerkungen',
75
+        'booking_behaelter',
76
+        'booking_sorten',
77
+        'booking_ernteart',
78
+        'booking_lage',
79
+    ),
80
+    'email_html'    => array
81
+    (
82
+        'member_firstname', // The firstname of the recipient
83
+        'member_lastname', // The lastname of the recipient
84
+        'member_memberno', // The member account number of the recipient
85
+        'slot_date',
86
+        'slot_time',
87
+        'slot_behaelter',
88
+        'slot_sorten',
89
+        'slot_ernteart',
90
+        'slot_lage',
91
+        'slot_anmerkungen',
92
+        'booking_behaelter',
93
+        'booking_sorten',
94
+        'booking_ernteart',
95
+        'booking_lage',
96
+    )
97
+);
35 98
new file mode 100644
... ...
@@ -0,0 +1,26 @@
1
+<?php
2
+
3
+/*
4
+ * This file is part of contao-weinanlieferung-bundle.
5
+ *
6
+ * (c) vonRotenberg
7
+ *
8
+ * @license commercial
9
+ */
10
+
11
+use Contao\CoreBundle\DataContainer\PaletteManipulator;
12
+
13
+PaletteManipulator::create()
14
+    ->addField('memberno','personal_legend',PaletteManipulator::POSITION_APPEND)
15
+    ->applyToPalette('default','tl_member');
16
+
17
+
18
+$GLOBALS['TL_DCA']['tl_member']['fields']['memberno'] = array
19
+(
20
+    'label'                 => &$GLOBALS['TL_LANG']['tl_member']['memberno'],
21
+    'exclude'               => true,
22
+    'search'                => true,
23
+    'inputType'             => 'text',
24
+    'eval'                  => array('maxlength'=>16, 'tl_class'=>'w50'),
25
+    'sql'                   => "varchar(16) NOT NULL default ''",
26
+);
... ...
@@ -125,6 +125,10 @@ $GLOBALS['TL_DCA']['tl_vr_wa_reservation'] = array
125 125
         (
126 126
             'sql' => "int(10) unsigned NOT NULL default '0'"
127 127
         ),
128
+        'nc_sent'      => array
129
+        (
130
+            'sql' => "int(10) unsigned NOT NULL default '0'"
131
+        ),
128 132
         'uid'         => array
129 133
         (
130 134
             'exclude'                 => true,
... ...
@@ -104,12 +104,14 @@ $GLOBALS['TL_DCA']['tl_vr_wa_standort'] = array
104 104
     // Palettes
105 105
     'palettes' => array
106 106
     (
107
-        'default' => '{title_legend},title'
107
+        '__selector__' => ['nc_enable'],
108
+        'default' => '{title_legend},title;{notification_legend},nc_enable'
108 109
     ),
109 110
 
110 111
     // Subpalettes
111 112
     'subpalettes' => array
112 113
     (
114
+        'nc_enable' => 'nc_notification'
113 115
     ),
114 116
 
115 117
     // Fields
... ...
@@ -135,6 +137,20 @@ $GLOBALS['TL_DCA']['tl_vr_wa_standort'] = array
135 137
                 'maxlength' => 255
136 138
             ),
137 139
             'sql'                     => "varchar(255) NOT NULL default ''"
140
+        ),
141
+        'nc_enable' => array
142
+        (
143
+            'exclude'                 => true,
144
+            'inputType'               => 'checkbox',
145
+            'eval'                    => array('tl_class'=>'w50 m12','submitOnChange'=>true),
146
+            'sql'                     => "char(1) NOT NULL default ''"
147
+        ),
148
+        'nc_notification' => array
149
+        (
150
+            'exclude'                   => true,
151
+            'inputType'                 => 'select',
152
+            'eval'                      => array('mandatory'=>true,'includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'),
153
+            'sql'                       => "int(10) unsigned NOT NULL default '0'"
138 154
         )
139 155
     )
140 156
 );
141 157
new file mode 100644
... ...
@@ -0,0 +1,12 @@
1
+<?php
2
+
3
+/**
4
+ * This file is part of contao-weinanlieferung-bundle.
5
+ *
6
+ * (c) vonRotenberg
7
+ *
8
+ * @license commercial
9
+ */
10
+
11
+$GLOBALS['TL_LANG']['tl_member']['memberno'][0] = 'Mitgliedsnummer';
12
+$GLOBALS['TL_LANG']['tl_member']['memberno'][1] = 'Die Mitgliedsnummer des Winzers.';
0 13
new file mode 100644
... ...
@@ -0,0 +1,13 @@
1
+<?php
2
+
3
+/**
4
+ * This file is part of contao-weinanlieferung-bundle.
5
+ *
6
+ * (c) vonRotenberg
7
+ *
8
+ * @license commercial
9
+ */
10
+
11
+$GLOBALS['TL_LANG']['tl_nc_notification']['type']['weinanlieferung'] = 'Weinanlieferung';
12
+$GLOBALS['TL_LANG']['tl_nc_notification']['type']['wa_booking_change'][0] = 'Neue Buchung / Buchungsupdate';
13
+$GLOBALS['TL_LANG']['tl_nc_notification']['type']['wa_booking_change'][1] = 'Wenn eine neue Buchung eingeht, oder eine vorhandene Buchung geändert wird.';
... ...
@@ -10,8 +10,13 @@
10 10
 
11 11
 $GLOBALS['TL_LANG']['tl_vr_wa_standort']['title'][0] = 'Titel';
12 12
 $GLOBALS['TL_LANG']['tl_vr_wa_standort']['title'][1] = 'Der Name des Anlieferungs-Standorts.';
13
+$GLOBALS['TL_LANG']['tl_vr_wa_standort']['nc_enable'][0] = 'Benachrichtigungen aktivieren';
14
+$GLOBALS['TL_LANG']['tl_vr_wa_standort']['nc_enable'][1] = 'Ermöglicht das Versenden von Benachrichtigungen beim Veröffentlichen neuer Nachrichten.';
15
+$GLOBALS['TL_LANG']['tl_vr_wa_standort']['nc_notification'][0] = 'Benachrichtigung';
16
+$GLOBALS['TL_LANG']['tl_vr_wa_standort']['nc_notification'][1] = 'Die zu versendende Benachrichtigung.';
13 17
 
14 18
 $GLOBALS['TL_LANG']['tl_vr_wa_standort']['title_legend'] = 'Standort';
19
+$GLOBALS['TL_LANG']['tl_vr_wa_standort']['notification_legend'] = 'Benachrichtigung-Einstellungen';
15 20
 
16 21
 $GLOBALS['TL_LANG']['tl_vr_wa_standort']['rebsorte'] = 'Rebsorten';
17 22
 $GLOBALS['TL_LANG']['tl_vr_wa_standort']['leseart'] = 'Leseart';
18 23
new file mode 100644
... ...
@@ -0,0 +1,48 @@
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
+
21
+class SendBookingChangeNotificationCommand extends Command
22
+{
23
+    public function __construct(ContaoFramework $framework)
24
+    {
25
+        $this->framework = $framework;
26
+
27
+        parent::__construct();
28
+    }
29
+
30
+    protected function configure(): void
31
+    {
32
+        $this
33
+            ->setName('weinanlieferung:sendBookingNotifications')
34
+            ->setDescription('Sends notifications about newly added or modified bookings.')
35
+        ;
36
+    }
37
+
38
+    protected function execute(InputInterface $input, OutputInterface $output): int
39
+    {
40
+        $this->framework->initialize();
41
+
42
+        $wa_service = System::getContainer()->get("vonrotenberg.cron.wa_booking_notifications");
43
+
44
+        $wa_service('cli');
45
+
46
+        return 0;
47
+    }
48
+}
0 49
new file mode 100644
... ...
@@ -0,0 +1,159 @@
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\WeinanlieferungStandortModel;
30
+
31
+/**
32
+ * @CronJob("*\/10 * * * *")
33
+ */
34
+class SendBookingChangeNotificationJob
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
+        $this->logger->log(LogLevel::INFO, 'WA notifications debug', array('contao' => new ContaoContext(__METHOD__, 'CRON')));
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
+                // Do we have updateable items
66
+                $Bookings = $this->db->executeQuery("SELECT r.id, r.pid, r.uid, r.behaelter, r.sorten, r.lage, r.ernteart, r.upload, s.date as 'slot_date', s.time as 'slot_time', s.behaelter as 'slot_behaelter', s.sorten as 'slot_sorten', s.ernteart as 'slot_ernteart', s.lage as 'slot_lage', s.anmerkungen as 'slot_anmerkungen' FROM tl_vr_wa_reservation r INNER JOIN tl_vr_wa_slot s ON s.id = r.pid WHERE s.pid = ? AND r.nc_sent < r.tstamp AND s.published='1'",[$Location->id]);
67
+
68
+                // Load groups and notification models if we have news to share
69
+                if ($Bookings->rowCount() && ($Notification = Notification::findByPk($Location->nc_notification)) !== null)
70
+                {
71
+                    foreach ($Bookings->iterateAssociative() as $Booking)
72
+                    {
73
+                        // Get group members
74
+                        $Member = MemberModel::findOneBy(array("id = ?", "login='1' AND (start='' OR start<='$time') AND (stop='' OR stop>'" . ($time + 60) . "') AND disable=''"), [$Booking['uid']]);
75
+
76
+                        // Send notification to member
77
+                        if ($Member !== null)
78
+                        {
79
+                            $arrSortenAvailable = [];
80
+                            $arrErnteartAvailable = [];
81
+                            $arrLageAvailable = [];
82
+                            $arrSortenBooked = [];
83
+                            $arrErnteartBooked = [];
84
+                            $arrLageBooked = [];
85
+
86
+                            $SortenLeseart = StringUtil::deserialize($Booking['slot_sorten'],true);
87
+                            foreach($SortenLeseart as $sorteLeseart)
88
+                            {
89
+                                $objSorte = WeinanlieferungRebsorteModel::findByPk($sorteLeseart['sorte']);
90
+                                $objLeseart = WeinanlieferungLeseartModel::findByPk($sorteLeseart['leseart']);
91
+                                $arrSortenAvailable[$objSorte->id.','.$objLeseart->id] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
92
+                            }
93
+                            $SortenLeseart = explode(';',$Booking['sorten']);
94
+                            foreach($SortenLeseart as $sorteLeseart)
95
+                            {
96
+                                list($sorte,$leseart) = explode(',',$sorteLeseart);
97
+                                $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte);
98
+                                $objLeseart = WeinanlieferungLeseartModel::findByPk($leseart);
99
+                                $arrSortenBooked[$objSorte->id.','.$objLeseart->id] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
100
+                            }
101
+                            foreach (explode(',', $Booking['ernteart']) as $ernteart)
102
+                            {
103
+                                $arrErnteartBooked[$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
104
+                            }
105
+                            foreach (explode(',', $Booking['slot_ernteart']) as $ernteart)
106
+                            {
107
+                                $arrErnteartAvailable[$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
108
+                            }
109
+                            foreach (explode(',', $Booking['slot_lage']) as $lage)
110
+                            {
111
+                                if (($Lage = WeinanlieferungLageModel::findByPk($lage)) !== null)
112
+                                {
113
+                                    $arrLageAvailable[$Lage->id] = $Lage->title;
114
+                                }
115
+                            }
116
+                            foreach (explode(',', $Booking['lage']) as $lage)
117
+                            {
118
+                                if (($Lage = WeinanlieferungLageModel::findByPk($lage)) !== null)
119
+                                {
120
+                                    $arrLageBooked[$Lage->id] = $Lage->title;
121
+                                }
122
+                            }
123
+
124
+                            $Notification->send(array
125
+                            (
126
+                                'member_email'       => $Member->email . ($Member->nc_news_additionalEmail ? ',' . $Member->nc_news_additionalEmail : ''),
127
+                                'member_firstname'   => $Member->firstname,
128
+                                'member_lastname'    => $Member->lastname,
129
+                                'member_memberno'    => $Member->memberno,
130
+                                'slot_date'          => Date::parse(Date::getNumericDateFormat(),$Booking['slot_date']),
131
+                                'slot_time'          => Date::parse(Date::getNumericTimeFormat(),$Booking['slot_time']),
132
+                                'slot_behaelter'     => $Booking['slot_behaelter'],
133
+                                'slot_sorten'        => implode(', ',$arrSortenAvailable),
134
+                                'slot_ernteart'      => implode(', ',$arrErnteartAvailable),
135
+                                'slot_lage'          => implode(', ',$arrLageAvailable),
136
+                                'slot_anmerkungen'   => $Booking['slot_anmerkungen'],
137
+                                'booking_behaelter'  => $Booking['behaelter'],
138
+                                'booking_sorten'     => implode(', ',$arrSortenBooked),
139
+                                'booking_ernteart'   => implode(', ',$arrErnteartBooked),
140
+                                'booking_lage'       => implode(', ',$arrLageBooked),
141
+                                'admin_email'        => $admin_email,
142
+                            ),
143
+                                $GLOBALS['TL_LANGUAGE']);
144
+
145
+                            $intNotificationsCount++;
146
+                            $this->db->executeStatement("UPDATE tl_vr_wa_reservation SET nc_sent = ? WHERE id = ?",[$time,$Booking['id']]);
147
+                        }
148
+                    }
149
+                }
150
+            }
151
+
152
+            // Flag news as sent
153
+            if ($intNotificationsCount)
154
+            {
155
+                $this->logger->log(LogLevel::INFO, sprintf('WA notifications(%s) has been sent',$intNotificationsCount), array('contao' => new ContaoContext(__METHOD__, 'CRON')));
156
+            }
157
+        }
158
+    }
159
+}
0 160
new file mode 100644
... ...
@@ -0,0 +1,49 @@
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\EventListener\DataContainer;
14
+
15
+use Contao\CoreBundle\ServiceAnnotation\Callback;
16
+use Contao\DataContainer;
17
+use Doctrine\DBAL\Connection;
18
+
19
+class WeinanlieferungStandortContainerListener
20
+{
21
+    /** @var Connection */
22
+    protected $db;
23
+
24
+    public function __construct(Connection $db)
25
+    {
26
+        $this->db = $db;
27
+    }
28
+
29
+    /**
30
+     * @Callback(table="tl_vr_wa_standort", target="fields.nc_notification.options")
31
+     */
32
+    public function onNcNotificationOptionsCallback(DataContainer $dc)
33
+    {
34
+        $arrOptions = [];
35
+        $objNotifications = $this->db->executeQuery("SELECT id,title FROM tl_nc_notification WHERE type='wa_booking_change' ORDER BY title");
36
+
37
+        if (!$objNotifications->rowCount())
38
+        {
39
+            return $arrOptions;
40
+        }
41
+
42
+        foreach ($objNotifications->fetchAllAssociative() as $notification)
43
+        {
44
+            $arrOptions[$notification['id']] = $notification['title'];
45
+        }
46
+
47
+        return $arrOptions;
48
+    }
49
+}