Browse code

Add check-in event and trigger csv file export

Benjamin Roth authored on30/07/2025 11:22:49
Showing4 changed files
... ...
@@ -8,6 +8,11 @@ services:
8 8
         resource: ../src
9 9
         exclude: ../src/{VonrotenbergWeinanlieferungBundle.php,ContaoManager,Entity,Migrations,Model,Resources,Tests,Widget}
10 10
 
11
+    vonRotenberg\WeinanlieferungBundle\EventListener\CheckInCompletedListener:
12
+        arguments:
13
+            $logger: '@logger'
14
+            $projectDir: '%kernel.project_dir%'
15
+
11 16
     vonRotenberg\WeinanlieferungBundle\Migration\RebsortenMigration:
12 17
         tags:
13 18
             - { name: contao.migration, priority: 0 }
... ...
@@ -27,10 +27,12 @@ use Contao\Input;
27 27
 use Contao\StringUtil;
28 28
 use Contao\System;
29 29
 use Doctrine\DBAL\Connection;
30
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
30 31
 use Symfony\Component\HttpFoundation\Request;
31 32
 use Symfony\Component\HttpFoundation\Response;
32 33
 use Symfony\Component\Routing\Annotation\Route;
33 34
 use Symfony\Contracts\Translation\TranslatorInterface;
35
+use vonRotenberg\WeinanlieferungBundle\Event\CheckInCompletedEvent;
34 36
 use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLageModel;
35 37
 use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel;
36 38
 use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel;
... ...
@@ -45,12 +47,19 @@ class SlotAjaxController extends AbstractController
45 47
     private $tokenChecker;
46 48
     private $translator;
47 49
     private $framework;
48
-
49
-    public function __construct(ContaoFramework $framework, TokenChecker $tokenChecker, TranslatorInterface $translator)
50
+    private $eventDispatcher;
51
+
52
+    public function __construct(
53
+        ContaoFramework $framework,
54
+        TokenChecker $tokenChecker,
55
+        TranslatorInterface $translator,
56
+        EventDispatcherInterface $eventDispatcher
57
+    )
50 58
     {
51 59
         $this->framework = $framework;
52 60
         $this->tokenChecker = $tokenChecker;
53 61
         $this->translator = $translator;
62
+        $this->eventDispatcher = $eventDispatcher;
54 63
     }
55 64
 
56 65
     public function __invoke(Request $request)
... ...
@@ -801,6 +810,13 @@ class SlotAjaxController extends AbstractController
801 810
         $Booking->tstamp = $time;
802 811
         $Booking->save();
803 812
 
813
+        // Create reservation data array for the event
814
+        $reservationData = $Booking->row();
815
+
816
+        // Dispatch the check-in completed event
817
+        $event = new CheckInCompletedEvent($reservationData, $Booking);
818
+        $this->eventDispatcher->dispatch($event, CheckInCompletedEvent::NAME);
819
+
804 820
         return new Response('<div class="toast toast--success mx-0"><p>Check-in erfolgreich durchgeführt</p></div>', 200, ['HX-Trigger'=> 'updateWaBooking']);
805 821
     }
806 822
 
807 823
new file mode 100644
... ...
@@ -0,0 +1,59 @@
1
+<?php
2
+
3
+namespace vonRotenberg\WeinanlieferungBundle\Event;
4
+
5
+use Symfony\Contracts\EventDispatcher\Event;
6
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel;
7
+
8
+/**
9
+ * Event that is triggered when a check-in is completed.
10
+ */
11
+class CheckInCompletedEvent extends Event
12
+{
13
+    /**
14
+     * Event name
15
+     */
16
+    public const NAME = 'vonrotenberg.weinanlieferung.checkin_completed';
17
+
18
+    /**
19
+     * @var array The reservation data
20
+     */
21
+    private $reservationData;
22
+
23
+    /**
24
+     * @var WeinanlieferungReservationModel The reservation model
25
+     */
26
+    private $reservationModel;
27
+
28
+    /**
29
+     * CheckInCompletedEvent constructor.
30
+     *
31
+     * @param array $reservationData The reservation data as array
32
+     * @param WeinanlieferungReservationModel $reservationModel The reservation model
33
+     */
34
+    public function __construct(array $reservationData, WeinanlieferungReservationModel $reservationModel)
35
+    {
36
+        $this->reservationData = $reservationData;
37
+        $this->reservationModel = $reservationModel;
38
+    }
39
+
40
+    /**
41
+     * Get the reservation data.
42
+     *
43
+     * @return array
44
+     */
45
+    public function getReservationData(): array
46
+    {
47
+        return $this->reservationData;
48
+    }
49
+
50
+    /**
51
+     * Get the reservation model.
52
+     *
53
+     * @return WeinanlieferungReservationModel
54
+     */
55
+    public function getReservationModel(): WeinanlieferungReservationModel
56
+    {
57
+        return $this->reservationModel;
58
+    }
59
+}
0 60
new file mode 100644
... ...
@@ -0,0 +1,231 @@
1
+<?php
2
+
3
+namespace vonRotenberg\WeinanlieferungBundle\EventListener;
4
+
5
+use Psr\Log\LoggerInterface;
6
+use Psr\Log\LogLevel;
7
+use Contao\CoreBundle\Monolog\ContaoContext;
8
+use Contao\MemberModel;
9
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
10
+use Symfony\Component\Filesystem\Filesystem;
11
+use vonRotenberg\WeinanlieferungBundle\Event\CheckInCompletedEvent;
12
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLageModel;
13
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel;
14
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel;
15
+
16
+/**
17
+ * Listener for the CheckInCompletedEvent.
18
+ * Generates a CSV export file for each checked-in booking.
19
+ */
20
+class CheckInCompletedListener implements EventSubscriberInterface
21
+{
22
+    /**
23
+     * @var LoggerInterface
24
+     */
25
+    private $logger;
26
+
27
+    /**
28
+     * @var string
29
+     */
30
+    private $projectDir;
31
+
32
+    /**
33
+     * Constructor.
34
+     *
35
+     * @param LoggerInterface $logger
36
+     * @param string $projectDir
37
+     */
38
+    public function __construct(LoggerInterface $logger, string $projectDir)
39
+    {
40
+        $this->logger = $logger;
41
+        $this->projectDir = $projectDir;
42
+    }
43
+
44
+    /**
45
+     * Returns an array of event names this subscriber wants to listen to.
46
+     *
47
+     * @return array The event names to listen to
48
+     */
49
+    public static function getSubscribedEvents()
50
+    {
51
+        return [
52
+            CheckInCompletedEvent::NAME => 'onCheckInCompleted',
53
+        ];
54
+    }
55
+
56
+    /**
57
+     * Handle the check-in completed event.
58
+     *
59
+     * @param CheckInCompletedEvent $event
60
+     */
61
+    public function onCheckInCompleted(CheckInCompletedEvent $event)
62
+    {
63
+        // Get the reservation data from the event
64
+        $reservationData = $event->getReservationData();
65
+        $reservationModel = $event->getReservationModel();
66
+
67
+        // Log the event for testing purposes
68
+        $this->logger->log(
69
+            LogLevel::INFO,
70
+            sprintf('Check-in completed for reservation ID: %s', $reservationData['id']),
71
+            ['contao' => new ContaoContext(__METHOD__, 'CHECK_IN')]
72
+        );
73
+
74
+        // Generate and save the CSV export
75
+        $this->generateCsvExport($reservationData, $reservationModel);
76
+    }
77
+
78
+    /**
79
+     * Generate and save the CSV export file.
80
+     *
81
+     * @param array $reservationData
82
+     * @param object $reservationModel
83
+     */
84
+    private function generateCsvExport(array $reservationData, $reservationModel)
85
+    {
86
+        // Create export directory if it doesn't exist
87
+        $exportDir = $this->projectDir . '/export/check_in';
88
+        $filesystem = new Filesystem();
89
+
90
+        if (!$filesystem->exists($exportDir)) {
91
+            $filesystem->mkdir($exportDir, 0755);
92
+        }
93
+
94
+        // Get member data
95
+        $memberId = $reservationData['uid'];
96
+        $memberModel = MemberModel::findById($memberId);
97
+
98
+        if (null === $memberModel) {
99
+            $this->logger->log(
100
+                LogLevel::ERROR,
101
+                sprintf('Could not find member with ID: %s', $memberId),
102
+                ['contao' => new ContaoContext(__METHOD__, 'CHECK_IN_CSV_EXPORT')]
103
+            );
104
+            return;
105
+        }
106
+
107
+        // Get behaelter numbers
108
+        $behaelterNumbers = json_decode($reservationData['behaelter_numbers'], true);
109
+        if (!is_array($behaelterNumbers) || empty($behaelterNumbers)) {
110
+            $this->logger->log(
111
+                LogLevel::ERROR,
112
+                sprintf('No behaelter numbers found for reservation ID: %s', $reservationData['id']),
113
+                ['contao' => new ContaoContext(__METHOD__, 'CHECK_IN_CSV_EXPORT')]
114
+            );
115
+            return;
116
+        }
117
+
118
+        // Format check-in date
119
+        $checkInDate = date('d.m.Y', $reservationData['checked_in_on']);
120
+
121
+        // Get sorten (grape varieties and harvest types)
122
+        $sortenData = [];
123
+        $sortenLeseartPairs = explode(';', $reservationData['sorten']);
124
+        foreach ($sortenLeseartPairs as $pair) {
125
+            if (empty($pair)) {
126
+                continue;
127
+            }
128
+
129
+            list($sorteId, $leseartId) = explode(',', $pair);
130
+
131
+            $rebsorteModel = WeinanlieferungRebsorteModel::findById($sorteId);
132
+            $leseartModel = WeinanlieferungLeseartModel::findById($leseartId);
133
+
134
+            if (null !== $rebsorteModel && null !== $leseartModel) {
135
+                $sortenData[] = [
136
+                    'rebsorte_id' => $rebsorteModel->ident_string,
137
+                    'rebsorte_title' => $rebsorteModel->title,
138
+                    'leseart_id' => $leseartModel->ident_string,
139
+                    'leseart_title' => $leseartModel->title
140
+                ];
141
+            }
142
+        }
143
+
144
+        // Get lagen (locations)
145
+        $lagenData = [];
146
+        $lagenIds = explode(',', $reservationData['lage']);
147
+        foreach ($lagenIds as $lageId) {
148
+            if (empty($lageId)) {
149
+                continue;
150
+            }
151
+
152
+            $lageModel = WeinanlieferungLageModel::findById($lageId);
153
+
154
+            if (null !== $lageModel) {
155
+                $lagenData[] = [
156
+                    'lage_id' => $lageModel->ident_string,
157
+                    'lage_title' => $lageModel->title
158
+                ];
159
+            }
160
+        }
161
+
162
+        // Get ernteart (harvest method)
163
+        $ernteartValues = explode(',', $reservationData['ernteart']);
164
+        $ernteartMapping = [
165
+            'handlese' => 'H',
166
+            'vollernter' => 'V'
167
+        ];
168
+
169
+        // Prepare CSV data
170
+        $csvData = [];
171
+
172
+        // For each behaelter, create a line in the CSV
173
+        foreach ($behaelterNumbers as $behaelterNumber) {
174
+            // Skip special value 9999 if needed
175
+            if ($behaelterNumber === '9999') {
176
+                continue;
177
+            }
178
+
179
+            // Use the first sorte, leseart, and lage if available
180
+            $rebsorteId = !empty($sortenData) ? $sortenData[0]['rebsorte_id'] : '';
181
+            $rebsorteTitle = !empty($sortenData) ? $sortenData[0]['rebsorte_title'] : '';
182
+            $leseartId = !empty($sortenData) ? $sortenData[0]['leseart_id'] : '';
183
+            $leseartTitle = !empty($sortenData) ? $sortenData[0]['leseart_title'] : '';
184
+            $lageId = !empty($lagenData) ? $lagenData[0]['lage_id'] : '';
185
+            $lageTitle = !empty($lagenData) ? $lagenData[0]['lage_title'] : '';
186
+
187
+            // Map ernteart values to H/V
188
+            $ernteart = '';
189
+            foreach ($ernteartValues as $value) {
190
+                if (isset($ernteartMapping[$value])) {
191
+                    $ernteart = $ernteartMapping[$value];
192
+                    break;
193
+                }
194
+            }
195
+
196
+            // Create CSV line
197
+            $csvData[] = [
198
+                $memberModel->memberno, // member id
199
+                $memberModel->firstname . ' ' . $memberModel->lastname, // member first and lastname
200
+                $rebsorteId, // rebsorte identifikator
201
+                $rebsorteTitle, // rebsorte title
202
+                $lageId, // lage identifikator
203
+                $lageTitle, // lage title
204
+                $leseartId, // leseart identifikator
205
+                $leseartTitle, // leseart title
206
+                $ernteart, // ernteart (H for handlese, V for vollernter)
207
+                $behaelterNumber, // behaelter number
208
+                $checkInDate, // check-in date
209
+                $reservationData['behaelter'] // behaelter amount for the whole checked-in booking
210
+            ];
211
+        }
212
+
213
+        // Generate CSV file
214
+        $filename = $reservationData['id'] . '_' . $memberModel->memberno . '.csv';
215
+        $filepath = $exportDir . '/' . $filename;
216
+
217
+        $file = fopen($filepath, 'w');
218
+
219
+        foreach ($csvData as $line) {
220
+            fputcsv($file, $line, ';');
221
+        }
222
+
223
+        fclose($file);
224
+
225
+        $this->logger->log(
226
+            LogLevel::INFO,
227
+            sprintf('CSV export created for reservation ID: %s at %s', $reservationData['id'], $filepath),
228
+            ['contao' => new ContaoContext(__METHOD__, 'CHECK_IN_CSV_EXPORT')]
229
+        );
230
+    }
231
+}