PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / trunk
Booking for Appointments and Events Calendar – Amelia vtrunk
2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / Application / Commands / QrCode / ScanQrCodeCommandHandler.php
ameliabooking / src / Application / Commands / QrCode Last commit date
GetQrCodeCommand.php 6 months ago GetQrCodeCommandHandler.php 6 months ago ScanQrCodeCommand.php 7 months ago ScanQrCodeCommandHandler.php 3 months ago
ScanQrCodeCommandHandler.php
290 lines
1 <?php
2
3 namespace AmeliaBooking\Application\Commands\QrCode;
4
5 use AmeliaBooking\Application\Commands\CommandHandler;
6 use AmeliaBooking\Application\Commands\CommandResult;
7 use AmeliaBooking\Application\Common\Exceptions\AccessDeniedException;
8 use AmeliaBooking\Application\Services\User\UserApplicationService;
9 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
10 use AmeliaBooking\Domain\Entity\Booking\Appointment\CustomerBooking;
11 use AmeliaBooking\Domain\Entity\Booking\Event\Event;
12 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
13 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\CustomerBookingRepository;
14 use AmeliaBooking\Domain\Entity\Entities;
15 use AmeliaBooking\Infrastructure\Repository\Booking\Event\EventRepository;
16 use Interop\Container\Exception\ContainerException;
17 use Slim\Exception\ContainerValueNotFoundException;
18
19 /**
20 * Class ScanQrCodeCommandHandler
21 *
22 * @package AmeliaBooking\Application\Command\QrCode
23 */
24 class ScanQrCodeCommandHandler extends CommandHandler
25 {
26 /**
27 * @var array
28 */
29 public $mandatoryFields = [
30 'ticketManualCode',
31 'scannedAt',
32 ];
33
34 /**
35 * @param ScanQrCodeCommand $command
36 *
37 * @return CommandResult
38 * @throws AccessDeniedException
39 * @throws InvalidArgumentException
40 * @throws QueryExecutionException
41 * @throws ContainerException
42 * @throws ContainerValueNotFoundException
43 */
44 public function handle(ScanQrCodeCommand $command)
45 {
46 $result = new CommandResult();
47
48 /** @var UserApplicationService $userAS */
49 $userAS = $this->container->get('application.user.service');
50
51 if (!$command->getPermissionService()->currentUserCanWrite(Entities::BOOKINGS)) {
52 $user = $this->container->get('logged.in.user');
53
54 if (!$user || $user->getId() === null) {
55 $user = $userAS->getAuthenticatedUser($command->getToken(), false, 'providerCabinet');
56
57 if ($user === null) {
58 $result->setResult(CommandResult::RESULT_ERROR);
59 $result->setMessage('Could not retrieve user');
60 $result->setData(
61 [
62 'reauthorize' => true
63 ]
64 );
65
66 return $result;
67 }
68 }
69 }
70
71 $this->checkMandatoryFields($command);
72
73 /** @var CustomerBookingRepository $bookingRepository */
74 $bookingRepository = $this->container->get('domain.booking.customerBooking.repository');
75 $ticketManualCode = $command->getField('ticketManualCode');
76
77 $ticketData = $bookingRepository->getQrTicketNumber($ticketManualCode);
78 if (!$ticketData) {
79 $result->setResult(CommandResult::RESULT_ERROR);
80 $result->setMessage('Ticket not found');
81 $result->setData([
82 'messageType' => 'error',
83 'message' => 'ticket_not_found',
84 ]);
85
86 return $result;
87 }
88
89 $qrCodes = json_decode($ticketData['qrCodes'], true);
90 $bookingId = $ticketData['id'];
91
92 $scannedAt = $command->getField('scannedAt');
93
94 /** @var CustomerBooking $booking */
95 $booking = $bookingRepository->getById($bookingId);
96
97 if (!$booking) {
98 $result->setResult(CommandResult::RESULT_ERROR);
99 $result->setMessage('Booking not found');
100 $result->setData([
101 'messageType' => 'error',
102 'message' => 'booking_not_found',
103 ]);
104 return $result;
105 }
106
107 $bookingStatus = $booking->getStatus()->getValue();
108 if ($bookingStatus === 'rejected' || $bookingStatus === 'canceled') {
109 $result->setResult(CommandResult::RESULT_ERROR);
110 $result->setMessage('Booking is canceled');
111 $result->setData([
112 'messageType' => 'error',
113 'message' => 'booking_canceled',
114 ]);
115
116 return $result;
117 }
118
119 /** @var EventRepository $eventRepository */
120 $eventRepository = $this->container->get('domain.booking.event.repository');
121 $eventId = $eventRepository->getByBookingId($bookingId)->getId()->getValue();
122 $event = $eventRepository->getById($eventId);
123
124 if (!$event || $event->getStatus()->getValue() === 'rejected') {
125 $result->setResult(CommandResult::RESULT_ERROR);
126 $result->setMessage('Event is canceled');
127 $result->setData([
128 'messageType' => 'error',
129 'message' => 'event_canceled',
130 ]);
131
132 return $result;
133 }
134
135 // Check if the scanned date is within the event's periods
136 if (!$this->isDateWithinEventPeriods($event, $scannedAt)) {
137 $result->setResult(CommandResult::RESULT_ERROR);
138 $result->setMessage('Ticket cannot be scanned for this date');
139 $result->setData([
140 'messageType' => 'error',
141 'message' => 'ticket_not_valid_for_date',
142 ]);
143
144 return $result;
145 }
146
147 $updated = false;
148
149 $type = 'ticket';
150 foreach ($qrCodes as $qrCodeItem) {
151 if ($qrCodeItem['ticketManualCode'] == $ticketManualCode) {
152 $type = $qrCodeItem['type'];
153 break;
154 }
155 }
156
157 if ($type === 'ticket') {
158 foreach ($qrCodes as &$qrCode) {
159 if (
160 $qrCode['type'] === 'ticket' &&
161 isset($qrCode['ticketManualCode']) &&
162 hash_equals($qrCode['ticketManualCode'], $ticketManualCode)
163 ) {
164 if (isset($qrCode['dates'][$scannedAt]) && $qrCode['dates'][$scannedAt] === true) {
165 $result->setResult(CommandResult::RESULT_ERROR);
166 $result->setMessage('Ticket has already been scanned');
167 $result->setData([
168 'messageType' => 'error',
169 'message' => 'ticket_already_scanned',
170 ]);
171
172 return $result;
173 }
174
175 $qrCode['dates'][$scannedAt] = true;
176 $updated = true;
177 break;
178 }
179 }
180 }
181
182 $ticketsControl = 0;
183
184 if ($type === 'booking') {
185 foreach ($qrCodes as &$qrCode) {
186 if (
187 $qrCode['type'] === 'booking' &&
188 isset($qrCode['ticketManualCode']) &&
189 hash_equals($qrCode['ticketManualCode'], $ticketManualCode)
190 ) {
191 if (isset($qrCode['dates'][$scannedAt]) && $qrCode['dates'][$scannedAt] === true) {
192 $result->setResult(CommandResult::RESULT_ERROR);
193 $result->setMessage('Group ticket has already been scanned');
194 $result->setData([
195 'messageType' => 'error',
196 'message' => 'group_ticket_already_scanned',
197 ]);
198
199 return $result;
200 }
201
202 $qrCode['dates'][$scannedAt] = true;
203 $updated = true;
204 break;
205 }
206 }
207
208 // Check if any ticket is already scanned for this date
209 foreach ($qrCodes as $qrCodeItem) {
210 if (isset($qrCodeItem['dates'][$scannedAt]) && $qrCodeItem['dates'][$scannedAt] === true) {
211 $ticketsControl++;
212 }
213 }
214
215 if ($ticketsControl === count($qrCodes)) {
216 $result->setResult(CommandResult::RESULT_ERROR);
217 $result->setMessage('All tickets have already been scanned');
218 $result->setData([
219 'messageType' => 'error',
220 'message' => 'all_already_scanned',
221 ]);
222
223 return $result;
224 }
225
226 $ticketsControl = 0;
227 // Mark all tickets as scanned for this date
228 foreach ($qrCodes as &$qr) {
229 if (!isset($qr['dates'][$scannedAt]) || $qr['dates'][$scannedAt] === false) {
230 $ticketsControl++;
231 $qr['dates'][$scannedAt] = true;
232 }
233 }
234 }
235
236 if (!$updated) {
237 $result->setResult(CommandResult::RESULT_ERROR);
238 $result->setMessage('Ticket not found');
239 $result->setData([
240 'messageType' => 'error',
241 'message' => 'ticket_not_found',
242 ]);
243
244 return $result;
245 }
246
247 $encoded = json_encode($qrCodes, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
248 $bookingRepository->updateFieldById($bookingId, $encoded, 'qrCodes');
249
250 $result->setResult(CommandResult::RESULT_SUCCESS);
251 $result->setMessage('Ticket is valid');
252 $result->setData([
253 'bookingId' => $bookingId,
254 'ticketManualCode' => $ticketManualCode,
255 'type' => $type,
256 'scannedAt' => $scannedAt,
257 'qrCodes' => $qrCodes,
258 'ticketControl' => $type === 'ticket' ? 1 : $ticketsControl,
259 'eventName' => $event ? $event->getName()->getValue() : '',
260 'messageType' => 'success',
261 'message' => 'ticket_is_valid',
262 ]);
263
264 return $result;
265 }
266
267 private function isDateWithinEventPeriods(Event $event, string $scannedAt): bool
268 {
269 if (!$event->getPeriods()) {
270 return false;
271 }
272
273 $scannedDateTime = \DateTime::createFromFormat('Y-m-d', $scannedAt);
274 if (!$scannedDateTime) {
275 return false;
276 }
277
278 foreach ($event->getPeriods()->getItems() as $period) {
279 $periodStart = (clone $period->getPeriodStart()->getValue())->setTime(0, 0, 0);
280 $periodEnd = (clone $period->getPeriodEnd()->getValue())->setTime(23, 59, 59);
281
282 if ($scannedDateTime >= $periodStart && $scannedDateTime <= $periodEnd) {
283 return true;
284 }
285 }
286
287 return false;
288 }
289 }
290