PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.2
Booking for Appointments and Events Calendar – Amelia v2.2
2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 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 / Booking / Appointment / ApproveBookingRemotelyCommandHandler.php
ameliabooking / src / Application / Commands / Booking / Appointment Last commit date
AddAppointmentCommand.php 1 year ago AddAppointmentCommandHandler.php 4 months ago AddBookingCommand.php 1 year ago AddBookingCommandHandler.php 1 year ago ApproveBookingRemotelyCommand.php 1 year ago ApproveBookingRemotelyCommandHandler.php 6 months ago CancelBookingCommand.php 1 year ago CancelBookingCommandHandler.php 8 months ago CancelBookingRemotelyCommand.php 1 year ago CancelBookingRemotelyCommandHandler.php 6 months ago DeleteAppointmentCommand.php 1 year ago DeleteAppointmentCommandHandler.php 1 year ago DeleteBookingCommand.php 1 year ago DeleteBookingCommandHandler.php 1 year ago DeleteBookingRemotelyCommand.php 1 year ago DeleteBookingRemotelyCommandHandler.php 8 months ago GetAppointmentBookingsCommand.php 8 months ago GetAppointmentBookingsCommandHandler.php 5 months ago GetAppointmentCommand.php 7 years ago GetAppointmentCommandHandler.php 6 months ago GetAppointmentsCommand.php 1 year ago GetAppointmentsCommandHandler.php 6 months ago GetIcsCommand.php 8 months ago GetIcsCommandHandler.php 4 years ago GetTimeSlotsCommand.php 1 year ago GetTimeSlotsCommandHandler.php 6 months ago ReassignBookingCommand.php 1 year ago ReassignBookingCommandHandler.php 5 months ago RejectBookingRemotelyCommand.php 1 year ago RejectBookingRemotelyCommandHandler.php 6 months ago SuccessfulBookingCommand.php 1 year ago SuccessfulBookingCommandHandler.php 1 year ago UpdateAppointmentCommand.php 1 year ago UpdateAppointmentCommandHandler.php 4 months ago UpdateAppointmentNoteCommand.php 4 months ago UpdateAppointmentNoteCommandHandler.php 4 months ago UpdateAppointmentStatusCommand.php 1 year ago UpdateAppointmentStatusCommandHandler.php 7 months ago UpdateAppointmentTimeCommand.php 1 year ago UpdateAppointmentTimeCommandHandler.php 5 months ago UpdateBookingStatusCommand.php 1 year ago UpdateBookingStatusCommandHandler.php 7 months ago
ApproveBookingRemotelyCommandHandler.php
222 lines
1 <?php
2
3 namespace AmeliaBooking\Application\Commands\Booking\Appointment;
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\Booking\BookingFallbackService;
9 use AmeliaBooking\Application\Services\User\CustomerApplicationService;
10 use AmeliaBooking\Domain\Common\Exceptions\BookingCancellationException;
11 use AmeliaBooking\Domain\Entity\Booking\Appointment\Appointment;
12 use AmeliaBooking\Domain\Entity\Booking\Appointment\CustomerBooking;
13 use AmeliaBooking\Domain\Entity\Entities;
14 use AmeliaBooking\Domain\Entity\User\AbstractUser;
15 use AmeliaBooking\Domain\Services\Reservation\ReservationServiceInterface;
16 use AmeliaBooking\Domain\Services\Settings\SettingsService;
17 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
18 use AmeliaBooking\Domain\ValueObjects\String\BookingStatus;
19 use AmeliaBooking\Domain\ValueObjects\String\Token;
20 use AmeliaBooking\Infrastructure\Common\Exceptions\NotFoundException;
21 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
22 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\AppointmentRepository;
23 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\CustomerBookingRepository;
24 use AmeliaBooking\Infrastructure\Repository\User\ProviderRepository;
25 use AmeliaBooking\Infrastructure\WP\Translations\BackendStrings;
26 use Slim\Exception\ContainerException;
27 use Slim\Exception\ContainerValueNotFoundException;
28 use UnexpectedValueException;
29
30 /**
31 * Class ApproveBookingRemotelyCommandHandler
32 *
33 * @package AmeliaBooking\Application\Commands\Booking\Appointment
34 */
35 class ApproveBookingRemotelyCommandHandler extends CommandHandler
36 {
37 /**
38 * @var array
39 */
40 public $mandatoryFields = [
41 'token',
42 ];
43
44 /**
45 * @throws UnexpectedValueException
46 * @throws ContainerException
47 * @throws \InvalidArgumentException
48 * @throws ContainerValueNotFoundException
49 * @throws QueryExecutionException
50 * @throws InvalidArgumentException
51 * @throws AccessDeniedException
52 * @throws NotFoundException
53 * @throws BookingCancellationException
54 */
55 public function handle(ApproveBookingRemotelyCommand $command): CommandResult
56 {
57 $this->checkMandatoryFields($command);
58
59 $result = new CommandResult();
60
61 $type = Entities::APPOINTMENT;
62
63 /** @var CustomerApplicationService $customerAS */
64 $customerAS = $this->container->get('application.user.customer.service');
65
66 /** @var CustomerBookingRepository $bookingRepository */
67 $bookingRepository = $this->container->get('domain.booking.customerBooking.repository');
68
69 /** @var AbstractUser $user */
70 $user = $this->container->get('logged.in.user');
71
72 /** @var CustomerBooking $booking */
73 $booking = $bookingRepository->getById((int)$command->getArg('id'));
74
75 /** @var SettingsService $settingsService */
76 $settingsService = $this->container->get('domain.settings.service');
77
78 $notificationSettings = $settingsService->getCategorySettings('notifications');
79
80 if ($booking === null) {
81 if (!empty($notificationSettings['approveErrorUrl'])) {
82 $result->setUrl($notificationSettings['approveErrorUrl']);
83 return $result;
84 }
85
86 return $result->setHtml(BookingFallbackService::getFallbackHtml('failed'));
87 }
88
89 $token = $bookingRepository->getToken((int)$command->getArg('id'));
90
91 if (empty($token['token'])) {
92 throw new AccessDeniedException('You are not allowed to update booking status');
93 }
94
95 $booking->setToken(new Token($token['token']));
96
97 if (
98 $booking->getStatus()->getValue() !== BookingStatus::WAITING &&
99 !$customerAS->isCustomerBooking($booking, $user, $command->getField('token'))
100 ) {
101 throw new AccessDeniedException('You are not allowed to update booking status');
102 }
103
104 if ($booking->getStatus()->getValue() === BookingStatus::WAITING) {
105 try {
106 $this->validateWaitingListCapacity($booking);
107 } catch (BookingCancellationException $e) {
108 $appointmentSettings = $settingsService->getCategorySettings('appointments');
109 $waitingListDeniedUrl = !empty($appointmentSettings['waitingListAppointments']['redirectUrlDenied']) ?
110 $appointmentSettings['waitingListAppointments']['redirectUrlDenied'] : '';
111
112 if (!empty($waitingListDeniedUrl)) {
113 $result->setUrl($waitingListDeniedUrl);
114 return $result;
115 }
116
117 if (!empty($notificationSettings['approveErrorUrl'])) {
118 $result->setUrl($notificationSettings['approveErrorUrl']);
119 return $result;
120 }
121
122 return $result->setHtml(BookingFallbackService::getFallbackHtml('failed'));
123 }
124 }
125
126 /** @var ReservationServiceInterface $reservationService */
127 $reservationService = $this->container->get('application.reservation.service')->get($type);
128
129 $status = BookingStatus::APPROVED;
130
131 do_action('amelia_before_booking_approved_link', $booking ? $booking->toArray() : null);
132
133 $bookingData = $reservationService->updateStatus($booking, $status);
134
135 $result->setResult(CommandResult::RESULT_SUCCESS);
136 $result->setMessage('Successfully updated booking status');
137 $result->setData(
138 array_merge(
139 $bookingData,
140 [
141 'type' => $type,
142 'status' => $status,
143 'message' => BackendStrings::get('appointment_status_changed') . strtolower(BackendStrings::get($status))
144 ]
145 )
146 );
147
148 if ($notificationSettings['approveSuccessUrl'] && $result->getResult() === CommandResult::RESULT_SUCCESS) {
149 $result->setUrl($notificationSettings['approveSuccessUrl']);
150
151 do_action('amelia_after_booking_approved_link', $booking ? $booking->toArray() : null);
152 return $result;
153 }
154
155 if ($notificationSettings['approveErrorUrl'] && $result->getResult() === CommandResult::RESULT_ERROR) {
156 $result->setUrl($notificationSettings['approveErrorUrl']);
157
158 return $result;
159 }
160 // No redirect URL defined - show fallback page
161 if ($result->getResult() === CommandResult::RESULT_SUCCESS) {
162 return $result->setHtml(BookingFallbackService::getFallbackHtml('approved'));
163 }
164 return $result->setHtml(BookingFallbackService::getFallbackHtml('approved_with_issues'));
165 }
166
167 /**
168 * Validates waiting list appointment capacity
169 *
170 * @throws ContainerValueNotFoundException
171 * @throws ContainerException
172 * @throws BookingCancellationException
173 * @throws NotFoundException
174 */
175 private function validateWaitingListCapacity(CustomerBooking $booking): void
176 {
177 /** @var AppointmentRepository $appointmentRepo */
178 $appointmentRepo = $this->container->get('domain.booking.appointment.repository');
179
180 /** @var Appointment $appointment */
181 $appointment = $appointmentRepo->getById($booking->getAppointmentId()->getValue());
182
183 if ($appointment === null) {
184 throw new NotFoundException('This appointment does not exist!');
185 }
186
187 /** @var ProviderRepository $providerRepository */
188 $providerRepository = $this->container->get('domain.users.providers.repository');
189
190 $capacity = $providerRepository->getMaxCapacityByServiceId(
191 $appointment->getProviderId()->getValue(),
192 $appointment->getServiceId()->getValue()
193 );
194
195 $currentBookedPersons = $this->calculateCurrentBookedPersons($appointment);
196 $availableCapacity = $capacity - $currentBookedPersons;
197
198 if ($availableCapacity < $booking->getPersons()->getValue()) {
199 throw new BookingCancellationException(
200 'This slot is already taken! Available capacity: ' . $availableCapacity . ', requested: ' . $booking->getPersons()->getValue()
201 );
202 }
203 }
204
205 /**
206 * Calculates the total number of persons currently booked for an appointment
207 */
208 private function calculateCurrentBookedPersons(Appointment $appointment): int
209 {
210 $persons = 0;
211 $approvedStatuses = [BookingStatus::APPROVED, BookingStatus::PENDING];
212
213 foreach ($appointment->getBookings()->getItems() as $existingBooking) {
214 if (in_array($existingBooking->getStatus()->getValue(), $approvedStatuses)) {
215 $persons += $existingBooking->getPersons()->getValue();
216 }
217 }
218
219 return $persons;
220 }
221 }
222