PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.20
Booking for Appointments and Events Calendar – Amelia v1.2.20
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 / Services / Placeholder / AppointmentPlaceholderService.php
ameliabooking / src / Application / Services / Placeholder Last commit date
AppointmentPlaceholderService.php 1 year ago AppointmentsPlaceholderService.php 1 year ago BasicPackagePlaceholderService.php 1 year ago EventPlaceholderService.php 1 year ago PlaceholderService.php 1 year ago PlaceholderServiceInterface.php 1 year ago
AppointmentPlaceholderService.php
1110 lines
1 <?php
2 /**
3 * @copyright © TMS-Plugins. All rights reserved.
4 * @licence See LICENCE.md for license details.
5 */
6
7 namespace AmeliaBooking\Application\Services\Placeholder;
8
9 use AmeliaBooking\Application\Services\Booking\AppointmentApplicationService;
10 use AmeliaBooking\Application\Services\Helper\HelperService;
11 use AmeliaBooking\Domain\Collection\Collection;
12 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
13 use AmeliaBooking\Domain\Entity\Bookable\AbstractBookable;
14 use AmeliaBooking\Domain\Entity\Bookable\Service\Category;
15 use AmeliaBooking\Domain\Entity\Bookable\Service\Extra;
16 use AmeliaBooking\Domain\Entity\Bookable\Service\Service;
17 use AmeliaBooking\Domain\Entity\Booking\Appointment\CustomerBooking;
18 use AmeliaBooking\Domain\Entity\Coupon\Coupon;
19 use AmeliaBooking\Domain\Entity\Entities;
20 use AmeliaBooking\Domain\Entity\Location\Location;
21 use AmeliaBooking\Domain\Entity\User\AbstractUser;
22 use AmeliaBooking\Domain\Entity\User\Provider;
23 use AmeliaBooking\Domain\Factory\Bookable\Service\ServiceFactory;
24 use AmeliaBooking\Domain\Factory\Booking\Appointment\CustomerBookingFactory;
25 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
26 use AmeliaBooking\Domain\Services\Reservation\ReservationServiceInterface;
27 use AmeliaBooking\Domain\Services\Settings\SettingsService;
28 use AmeliaBooking\Domain\ValueObjects\String\BookingStatus;
29 use AmeliaBooking\Domain\ValueObjects\String\PaymentStatus;
30 use AmeliaBooking\Infrastructure\Common\Exceptions\NotFoundException;
31 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
32 use AmeliaBooking\Infrastructure\Repository\Bookable\Service\CategoryRepository;
33 use AmeliaBooking\Infrastructure\Repository\Bookable\Service\ExtraRepository;
34 use AmeliaBooking\Infrastructure\Repository\Bookable\Service\ServiceRepository;
35 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\CustomerBookingRepository;
36 use AmeliaBooking\Infrastructure\Repository\Coupon\CouponRepository;
37 use AmeliaBooking\Infrastructure\Repository\Location\LocationRepository;
38 use AmeliaBooking\Infrastructure\Repository\User\UserRepository;
39 use AmeliaBooking\Infrastructure\WP\Translations\BackendStrings;
40 use DateTime;
41 use Exception;
42 use Interop\Container\Exception\ContainerException;
43 use Slim\Exception\ContainerValueNotFoundException;
44
45 /**
46 * Class AppointmentPlaceholderService
47 *
48 * @package AmeliaBooking\Application\Services\Placeholder
49 */
50 class AppointmentPlaceholderService extends PlaceholderService
51 {
52 /**
53 *
54 * @return array
55 *
56 * @throws ContainerException
57 */
58 public function getEntityPlaceholdersDummyData($type)
59 {
60 /** @var SettingsService $settingsService */
61 $settingsService = $this->container->get('domain.settings.service');
62
63 /** @var HelperService $helperService */
64 $helperService = $this->container->get('application.helper.service');
65
66 $companySettings = $settingsService->getCategorySettings('company');
67
68 $dateFormat = $settingsService->getSetting('wordpress', 'dateFormat');
69 $timeFormat = $settingsService->getSetting('wordpress', 'timeFormat');
70
71 $timeZone = get_option('timezone_string');
72
73 if (empty($timeZone)) {
74 $gmtOffset = get_option('gmt_offset');
75 $timeZone = sprintf('Etc/GMT%+d', -$gmtOffset);
76 }
77
78 $dateTime = new DateTime("now", new \DateTimeZone($timeZone));
79
80 $appointment_date = $dateTime->format($dateFormat);
81 $appointment_date_time = $dateTime->format($dateFormat . ' ' . $timeFormat);
82 $appointment_start_time = $dateTime->format($timeFormat);
83 $appointment_end_time = (new DateTime("now +1 hour", new \DateTimeZone($timeZone)))->format($timeFormat);
84
85 return [
86 'appointment_id' => '1',
87 'appointment_date' => $appointment_date,
88 'appointment_date_time' => $appointment_date_time,
89 'appointment_start_time' => $appointment_start_time,
90 'appointment_end_time' => $appointment_end_time,
91 'appointment_notes' => 'Appointment note',
92 'appointment_price' => $helperService->getFormattedPrice(100),
93 'payment_due_amount' => $helperService->getFormattedPrice(80),
94 'appointment_cancel_url' => 'http://cancel_url.com',
95 'appointment_approve_url' => 'http://approve_url.com',
96 'appointment_reject_url' => 'http://reject_url.com',
97 'zoom_join_url' => $type === 'email' ?
98 '<a href="#">' . BackendStrings::getCommonStrings()['zoom_click_to_join'] . '</a>' : 'https://join_zoom_link.com',
99 'zoom_host_url' => $type === 'email' ?
100 '<a href="#">' . BackendStrings::getCommonStrings()['zoom_click_to_start'] . '</a>' : 'https://start_zoom_link.com',
101 'google_meet_url' => $type === 'email' ?
102 '<a href="#">' . BackendStrings::getCommonStrings()['google_meet_join'] . '</a>' : 'https://join_google_meet_link.com',
103 'lesson_space_url' => $type === 'email' ?
104 '<a href="#">' . BackendStrings::getCommonStrings()['lesson_space_join'] . '</a>' : 'https://lessonspace.com/room-id',
105 'microsoft_teams_url' => $type === 'email' ?
106 '<a href="#">' . BackendStrings::getCommonStrings()['microsoft_teams_join'] . '</a>' : 'https://join_microsoft_teams_link.com',
107 'appointment_duration' => $helperService->secondsToNiceDuration(1800),
108 'appointment_deposit_payment' => $helperService->getFormattedPrice(20),
109 'appointment_status' => BackendStrings::getCommonStrings()['approved'],
110 'category_name' => 'Category Name',
111 'service_description' => 'Service Description',
112 'reservation_description' => 'Service Description',
113 'service_duration' => $helperService->secondsToNiceDuration(5400),
114 'service_name' => 'Service Name',
115 'service_id' => '123',
116 'reservation_name' => 'Service Name',
117 'service_price' => $helperService->getFormattedPrice(100),
118 'service_extras' => 'Extra1, Extra2, Extra3',
119 'service_extras_details' => '<p>Extra1: ($1.00 x 1) x 3</p><p>Extra2: ($2.00 x 2)</p><p>Extra3: ($3.00 x 3)</p>' .
120 '<p>-------------------------</p>' . '<p>' . BackendStrings::getCommonStrings()['extras_total_price'] . ' $16.00</p>'
121
122 ];
123 }
124
125 /**
126 * @param array $appointment
127 * @param int $bookingKey
128 * @param string $type
129 * @param string $token
130 * @param bool $invoice
131 * @param string $notificationType
132 *
133 * @return array
134 *
135 * @throws InvalidArgumentException
136 * @throws ContainerValueNotFoundException
137 * @throws NotFoundException
138 * @throws QueryExecutionException
139 * @throws ContainerException
140 * @throws Exception
141 */
142 public function getAppointmentPlaceholderData(
143 $appointment,
144 $bookingKey = null,
145 $type = null,
146 $token = null,
147 $invoice = false,
148 $notificationType = null
149 ) {
150 $data = [];
151
152 $this->setData($appointment, $bookingKey);
153
154 $data = array_merge($data, $this->getAppointmentData($appointment, $bookingKey, $type));
155 $data = array_merge($data, $this->getServiceData($appointment, $bookingKey, $type));
156 $data = array_merge($data, $this->getEmployeeData($appointment, $bookingKey));
157 $data = array_merge($data, $this->getBookingData($appointment, $type, $bookingKey, $token, $data['deposit'], null, $invoice));
158 $data = array_merge($data, $this->getCustomFieldsData($appointment, $type, $bookingKey));
159 $data = array_merge($data, $notificationType ? $this->getCouponsData($appointment, $type, $bookingKey) : []);
160
161 return $data;
162 }
163
164 /**
165 * @param array $appointment
166 * @param int $bookingKey
167 * @param string $type
168 * @param AbstractUser $customer
169 * @param array $allBookings
170 * @param bool $invoice
171 * @param string $notificationType
172 *
173 * @return array
174 *
175 * @throws InvalidArgumentException
176 * @throws ContainerValueNotFoundException
177 * @throws NotFoundException
178 * @throws QueryExecutionException
179 * @throws ContainerException
180 * @throws Exception
181 */
182 public function getPlaceholdersData(
183 $appointment,
184 $bookingKey = null,
185 $type = null,
186 $customer = null,
187 $allBookings = null,
188 $invoice = false,
189 $notificationType = null
190 ) {
191 /** @var CustomerBookingRepository $bookingRepository */
192 $bookingRepository = $this->container->get('domain.booking.customerBooking.repository');
193
194 $bookingKeyForEmployee = null;
195
196 if ($bookingKey === null) {
197 $bookingKeyForEmployee = $this->getBookingKeyForEmployee($appointment);
198 }
199
200 $token = isset($appointment['bookings'][$bookingKey]) ?
201 $bookingRepository->getToken($appointment['bookings'][$bookingKey]['id']) :
202 ($bookingKeyForEmployee ? $bookingRepository->getToken($bookingKeyForEmployee) : null);
203
204 $token = isset($token['token']) ? $token['token'] : null;
205
206 $data = [];
207
208 $this->setData($appointment, $bookingKey);
209
210 $locale = $this->getLocale($appointment, $bookingKey);
211
212 $data = array_merge($data, $this->getAppointmentPlaceholderData($appointment, $bookingKey, $type, $token, false, $notificationType));
213 $data = array_merge($data, $this->getRecurringAppointmentsData($appointment, $bookingKey, $type, 'recurring', $bookingKeyForEmployee));
214 if (empty($customer)) {
215 $data = array_merge($data, $this->getGroupedAppointmentData($appointment, $bookingKey, $type, $token));
216 }
217 $data = array_merge($data, $this->getCompanyData($bookingKey !== null ? $locale : null));
218 $data = array_merge($data, $this->getCustomersData($appointment, $type, $bookingKey, $customer));
219
220 return $data;
221 }
222
223
224 /**
225 * @param array $reservationData
226 *
227 * @return array
228 *
229 * @throws InvalidArgumentException
230 * @throws ContainerValueNotFoundException
231 * @throws NotFoundException
232 * @throws QueryExecutionException
233 * @throws ContainerException
234 * @throws Exception
235 */
236 public function getInvoicePlaceholdersData($reservationData)
237 {
238 $type = 'email';
239
240 $data = [];
241
242 $appointment = $reservationData['appointment'];
243 $bookingKey = array_search($reservationData['booking']['id'], array_column($appointment['bookings'], 'id'));
244
245 $this->setData($appointment, $bookingKey);
246
247 $locale = $this->getLocale($appointment, $bookingKey);
248
249 $appointments = array_merge([['appointment' => $appointment, 'booking' => $reservationData['booking']]], $reservationData['recurring']);
250
251 foreach ($appointments as $recurringAppointment) {
252 $appointment = $recurringAppointment['appointment'];
253 $bookingKey = array_search($recurringAppointment['booking']['id'], array_column($appointment['bookings'], 'id'));
254
255 $placeholders = $this->getAppointmentPlaceholderData($appointment, $bookingKey, $type, null, true);
256 $invoiceItem = $placeholders['invoice_items_booking'][0];
257
258 $data['invoice_number'] = $placeholders['payment_invoice_number'];
259 $data['invoice_method'] = !empty($placeholders['payment_gateway_title']) ? $placeholders['payment_gateway_title'] : $placeholders['payment_type'];
260 $data['invoice_issued'] = $placeholders['payment_created'];
261
262 $index = "service_{$appointment['serviceId']}_{$recurringAppointment['booking']['price']}";
263 if (!empty($data['items'][$index])) {
264 $data['items'][$index]['invoice_qty'] += $invoiceItem['invoice_qty'];
265 $data['items'][$index]['invoice_subtotal'] += $invoiceItem['invoice_subtotal'];
266 $data['items'][$index]['invoice_discount'] += $invoiceItem['invoice_discount'];
267 $data['items'][$index]['invoice_tax'] += $invoiceItem['invoice_tax'];
268 $data['items'][$index]['invoice_paid_amount'] += $invoiceItem['invoice_paid_amount'];
269 } else {
270 $data['items'][$index] = $invoiceItem;
271 $data['items'][$index]['item_name'] = $placeholders['service_name'];
272 }
273
274 $extraItems = $placeholders['invoice_items_extras'];
275 $extraItemsTaxes = $invoiceItem['invoice_extras_tax'];
276 foreach ($extraItems as $extraItem) {
277 $index = $extraItem['item_index'];
278 if (!empty($data['items'][$index])) {
279 $data['items'][$index]['invoice_qty'] += $extraItem['invoice_qty'];
280 $data['items'][$index]['invoice_subtotal'] += $extraItems['invoice_subtotal'];
281 $data['items'][$index]['invoice_tax'] += $extraItems['invoice_tax']['amount'];
282 $data['items'][$index]['invoice_tax_rate'] += $extraItems['invoice_tax']['rate'];
283 } else {
284 $data['items'][$index] = array_merge(
285 $extraItem,
286 !empty($extraItemsTaxes[$extraItem['item_id']]) ?
287 ['invoice_tax' => $extraItemsTaxes[$extraItem['item_id']]['amount'], 'invoice_tax_rate' => $extraItemsTaxes[$extraItem['item_id']]['rate'], 'invoice_tax_excluded' => $extraItemsTaxes[$extraItem['item_id']]['excluded']] :
288 []
289 );
290 }
291 }
292 }
293
294 $data['items'] = array_values($data['items']);
295
296 $data = array_merge($data, $this->getCompanyData($bookingKey !== null ? $locale : null));
297 $data = array_merge($data, $this->getCustomersData($appointment, $type, $bookingKey));
298
299 return $data;
300 }
301
302 /**
303 * @param $appointment
304 * @param null $bookingKey
305 * @param string $type
306 *
307 * @return array
308 *
309 * @throws Exception
310 */
311 protected function getAppointmentData($appointment, $bookingKey = null, $type = null)
312 {
313 /** @var UserRepository $userRepository */
314 $userRepository = $this->container->get('domain.users.repository');
315
316 /** @var SettingsService $settingsService */
317 $settingsService = $this->container->get('domain.settings.service');
318
319 $dateFormat = $settingsService->getSetting('wordpress', 'dateFormat');
320 $timeFormat = $settingsService->getSetting('wordpress', 'timeFormat');
321
322 if ($appointment['providerId'] && empty($appointment['provider'])) {
323 /** @var Provider $user */
324 $user = $userRepository->getById($appointment['providerId']);
325
326 $appointment['provider'] = $user->toArray();
327 }
328
329 if ($bookingKey !== null && $appointment['bookings'][$bookingKey]['utcOffset'] !== null
330 && $settingsService->getSetting('general', 'showClientTimeZone')
331 ) {
332 $info = !empty($appointment['bookings'][$bookingKey]['info'])
333 ? json_decode($appointment['bookings'][$bookingKey]['info'], true)
334 : null;
335
336 $timeZone = !empty($info['timeZone']) ? $info['timeZone'] : '';
337
338 $bookingStart = DateTimeService::getClientUtcCustomDateTimeObject(
339 DateTimeService::getCustomDateTimeInUtc($appointment['bookingStart']),
340 $appointment['bookings'][$bookingKey]['utcOffset']
341 );
342
343 $bookingEnd = DateTimeService::getClientUtcCustomDateTimeObject(
344 DateTimeService::getCustomDateTimeInUtc($appointment['bookingEnd']),
345 $appointment['bookings'][$bookingKey]['utcOffset']
346 );
347
348 $oldBookingStart = !empty($appointment['initialAppointmentDateTime']) ? DateTimeService::getClientUtcCustomDateTimeObject(
349 DateTimeService::getCustomDateTimeInUtc($appointment['initialAppointmentDateTime']['bookingStart']),
350 $appointment['bookings'][$bookingKey]['utcOffset']
351 ) : '';
352
353 $oldBookingEnd = !empty($appointment['initialAppointmentDateTime']) ? DateTimeService::getClientUtcCustomDateTimeObject(
354 DateTimeService::getCustomDateTimeInUtc($appointment['initialAppointmentDateTime']['bookingEnd']),
355 $appointment['bookings'][$bookingKey]['utcOffset']
356 ) : '';
357 } else if ($bookingKey === null && !empty($appointment['provider']['timeZone'])) {
358 $timeZone = $appointment['provider']['timeZone'];
359
360 $bookingStart = DateTimeService::getDateTimeObjectInTimeZone(
361 DateTimeService::getCustomDateTimeObject(
362 $appointment['bookingStart']
363 )->setTimezone(new \DateTimeZone($timeZone))->format('Y-m-d H:i:s'),
364 'UTC'
365 );
366
367 $bookingEnd = DateTimeService::getDateTimeObjectInTimeZone(
368 DateTimeService::getCustomDateTimeObject(
369 $appointment['bookingEnd']
370 )->setTimezone(new \DateTimeZone($timeZone))->format('Y-m-d H:i:s'),
371 'UTC'
372 );
373
374 $oldBookingStart = !empty($appointment['initialAppointmentDateTime']) ?
375 DateTimeService::getDateTimeObjectInTimeZone(
376 DateTimeService::getCustomDateTimeObject(
377 $appointment['initialAppointmentDateTime']['bookingStart']
378 )->setTimezone(new \DateTimeZone($timeZone))->format('Y-m-d H:i:s'),
379 'UTC'
380 ) : '';
381
382 $oldBookingEnd = !empty($appointment['initialAppointmentDateTime']) ?
383 DateTimeService::getDateTimeObjectInTimeZone(
384 DateTimeService::getCustomDateTimeObject(
385 $appointment['initialAppointmentDateTime']['bookingEnd']
386 )->setTimezone(new \DateTimeZone($timeZone))->format('Y-m-d H:i:s'),
387 'UTC'
388 ) : '';
389 } else {
390 $timeZone = get_option('timezone_string');
391
392 $bookingStart = DateTime::createFromFormat('Y-m-d H:i:s', $appointment['bookingStart']);
393
394 $bookingEnd = DateTime::createFromFormat('Y-m-d H:i:s', $appointment['bookingEnd']);
395
396 $oldBookingStart = !empty($appointment['initialAppointmentDateTime']) ?
397 DateTime::createFromFormat('Y-m-d H:i:s', $appointment['initialAppointmentDateTime']['bookingStart']) : '';
398
399 $oldBookingEnd = !empty($appointment['initialAppointmentDateTime']) ?
400 DateTime::createFromFormat('Y-m-d H:i:s', $appointment['initialAppointmentDateTime']['bookingEnd']) : '';
401 }
402
403 $zoomStartUrl = '';
404 $zoomJoinUrl = '';
405
406 $lessonSpaceLink = '';
407 if (array_key_exists('lessonSpace', $appointment) && $appointment['lessonSpace']) {
408 $lessonSpaceLink = $type === 'email' ?
409 '<a href="' . $appointment['lessonSpace'] . '">' . BackendStrings::getCommonStrings()['lesson_space_join'] . '</a>'
410 : $appointment['lessonSpace'];
411 }
412
413 if (isset($appointment['zoomMeeting']['joinUrl'], $appointment['zoomMeeting']['startUrl'])) {
414 $zoomStartUrl = $appointment['zoomMeeting']['startUrl'];
415 $zoomJoinUrl = $appointment['zoomMeeting']['joinUrl'];
416 }
417
418 $googleMeetUrl = '';
419 if (array_key_exists('googleMeetUrl', $appointment) && $appointment['googleMeetUrl']) {
420 $googleMeetUrl = $type === 'email' ?
421 '<a href="' . $appointment['googleMeetUrl'] . '">' . BackendStrings::getCommonStrings()['google_meet_join'] . '</a>'
422 : $appointment['googleMeetUrl'];
423 }
424
425 $microsoftTeamsUrl = '';
426 if (array_key_exists('microsoftTeamsUrl', $appointment) && $appointment['microsoftTeamsUrl']) {
427 $microsoftTeamsUrl = $type === 'email' ?
428 '<a href="' . $appointment['microsoftTeamsUrl'] . '">' . BackendStrings::getCommonStrings()['microsoft_teams_join'] . '</a>'
429 : $appointment['microsoftTeamsUrl'];
430 }
431
432 return [
433 'appointment_id' => !empty($appointment['id']) ? $appointment['id'] : '',
434 'appointment_status' => BackendStrings::getCommonStrings()[$appointment['status']],
435 'appointment_notes' => !empty($appointment['internalNotes']) ? $appointment['internalNotes'] : '',
436 'appointment_date' => date_i18n($dateFormat, $bookingStart->getTimestamp()),
437 'appointment_date_time' => date_i18n($dateFormat . ' ' . $timeFormat, $bookingStart->getTimestamp()),
438 'appointment_start_time' => date_i18n($timeFormat, $bookingStart->getTimestamp()),
439 'appointment_end_time' => date_i18n($timeFormat, $bookingEnd->getTimestamp()),
440 'initial_appointment_date' => !empty($oldBookingStart) ? date_i18n($dateFormat, $oldBookingStart->getTimestamp()) : '',
441 'initial_appointment_date_time' => !empty($oldBookingStart) ? date_i18n($dateFormat . ' ' . $timeFormat, $oldBookingStart->getTimestamp()) : '',
442 'initial_appointment_start_time' => !empty($oldBookingStart) ? date_i18n($timeFormat, $oldBookingStart->getTimestamp()) : '',
443 'initial_appointment_end_time' => !empty($oldBookingEnd) ? date_i18n($timeFormat, $oldBookingEnd->getTimestamp()) : '',
444 'lesson_space_url' => $lessonSpaceLink,
445 'zoom_host_url' => $zoomStartUrl && $type === 'email' ?
446 '<a href="' . $zoomStartUrl . '">' . BackendStrings::getCommonStrings()['zoom_click_to_start'] . '</a>'
447 : $zoomStartUrl,
448 'zoom_join_url' => $zoomJoinUrl && $type === 'email' ?
449 '<a href="' . $zoomJoinUrl . '">' . BackendStrings::getCommonStrings()['zoom_click_to_join'] . '</a>'
450 : $zoomJoinUrl,
451 'google_meet_url' => $googleMeetUrl,
452 'microsoft_teams_url' => $microsoftTeamsUrl,
453 'time_zone' => $timeZone,
454 ];
455 }
456
457 /**
458 * @param $appointmentArray
459 * @param $bookingKey
460 *
461 * @return array
462 * @throws ContainerValueNotFoundException
463 * @throws NotFoundException
464 * @throws QueryExecutionException
465 * @throws ContainerException
466 * @throws InvalidArgumentException
467 */
468 private function getServiceData($appointmentArray, $bookingKey, $type)
469 {
470 /** @var CategoryRepository $categoryRepository */
471 $categoryRepository = $this->container->get('domain.bookable.category.repository');
472 /** @var ServiceRepository $serviceRepository */
473 $serviceRepository = $this->container->get('domain.bookable.service.repository');
474
475 /** @var HelperService $helperService */
476 $helperService = $this->container->get('application.helper.service');
477 /** @var AppointmentApplicationService $appointmentAS */
478 $appointmentAS = $this->container->get('application.booking.appointment.service');
479
480 /** @var Service $service */
481 $service = $serviceRepository->getByIdWithExtras($appointmentArray['serviceId']);
482 /** @var Category $category */
483 $category = $categoryRepository->getById($service->getCategoryId()->getValue());
484
485 $locale = $this->getLocale($appointmentArray, $bookingKey);
486
487 $categoryName = $helperService->getBookingTranslation(
488 $bookingKey !== null ? $locale : null,
489 $category->getTranslations() ? $category->getTranslations()->getValue() : null,
490 'name'
491 ) ?: $category->getName()->getValue();
492
493 $serviceName = $helperService->getBookingTranslation(
494 $bookingKey !== null ? $locale : null,
495 $service->getTranslations() ? $service->getTranslations()->getValue() : null,
496 'name'
497 ) ?: $service->getName()->getValue();
498
499 $serviceDescription = $helperService->getBookingTranslation(
500 $bookingKey !== null ? $locale : null,
501 $service->getTranslations() ? $service->getTranslations()->getValue() : null,
502 'description'
503 ) ?: ($service->getDescription() ? $service->getDescription()->getValue() : '');
504
505 $servicePrices = [];
506
507 $serviceDurations = [];
508
509 if ($bookingKey === null) {
510 foreach ($appointmentArray['bookings'] as $booking) {
511 if ($booking['status'] === BookingStatus::CANCELED || $booking['status'] === BookingStatus::REJECTED) {
512 continue;
513 }
514
515 $duration = $booking['duration'] ? $booking['duration'] : $service->getDuration()->getValue();
516
517 $price = $appointmentAS->getBookingPriceForServiceDuration(
518 $service,
519 $duration
520 );
521
522 $servicePrices[] = $helperService->getFormattedPrice($price);
523
524 $serviceDurations[$duration] = $helperService->secondsToNiceDuration($duration);
525 }
526 } else {
527 $duration = !empty($appointmentArray['bookings'][$bookingKey]['duration'])
528 ? $appointmentArray['bookings'][$bookingKey]['duration'] : $service->getDuration()->getValue();
529
530 $price = $appointmentAS->getBookingPriceForServiceDuration(
531 $service,
532 $duration
533 );
534
535 $servicePrices[] = $helperService->getFormattedPrice(
536 $price
537 );
538
539 $serviceDurations[$duration] = $helperService->secondsToNiceDuration(
540 $duration
541 );
542 }
543
544 $data = [
545 'category_name' => $categoryName,
546 'category_id' => $category->getId()->getValue(),
547 'service_description' => $serviceDescription,
548 'reservation_description' => $serviceDescription,
549 'service_duration' => implode(', ', $serviceDurations),
550 'service_name' => $serviceName,
551 'service_id' => $service->getId()->getValue(),
552 'reservation_name' => $serviceName,
553 'service_price' => implode(', ', array_unique($servicePrices)),
554 ];
555
556 $bookingExtras = [];
557
558 $persons = 1;
559
560 foreach ((array)$appointmentArray['bookings'] as $key => $booking) {
561 if (($bookingKey === null && ($booking['isChangedStatus'] || $booking['status'] === BookingStatus::APPROVED
562 || $booking['status'] === BookingStatus::PENDING)) || $bookingKey === $key
563 ) {
564 foreach ((array)$booking['extras'] as $bookingExtra) {
565 $bookingExtras[$bookingExtra['extraId']] = [
566 'quantity' => $bookingExtra['quantity'],
567 'price' => $bookingExtra['price']
568 ];
569 }
570
571 $persons = $booking['persons'];
572 }
573 }
574
575 /** @var ExtraRepository $extraRepository */
576 $extraRepository = $this->container->get('domain.bookable.extra.repository');
577
578 /** @var Collection $extras */
579 $extras = $extraRepository->getAllIndexedById();
580
581 $duration = $service->getDuration()->getValue();
582
583 if ($bookingKey !== null) {
584 $duration = !empty($appointmentArray['bookings'][$bookingKey]['duration']) ?
585 $appointmentArray['bookings'][$bookingKey]['duration'] : $duration;
586
587 foreach ($appointmentArray['bookings'][$bookingKey]['extras'] as $bookingExtra) {
588 /** @var Extra $extra */
589 $extra = $extras->getItem($bookingExtra['extraId']);
590
591 $duration += $extra->getDuration() ? $extra->getDuration()->getValue() * $bookingExtra['quantity'] : 0;
592 }
593 } else {
594 $maxBookingDuration = 0;
595
596 foreach ($appointmentArray['bookings'] as $booking) {
597 $bookingDuration = $booking['duration'] ? $booking['duration'] : $duration;
598
599 foreach ($booking['extras'] as $bookingExtra) {
600 /** @var Extra $extra */
601 $extra = $extras->getItem($bookingExtra['extraId']);
602
603 $bookingDuration += $extra->getDuration() ?
604 $extra->getDuration()->getValue() * $bookingExtra['quantity'] : 0;
605 }
606
607 if ($bookingDuration > $maxBookingDuration &&
608 ($booking['status'] === BookingStatus::APPROVED || $booking['status'] === BookingStatus::PENDING)
609 ) {
610 $maxBookingDuration = $bookingDuration;
611 }
612 }
613
614 $duration = $maxBookingDuration;
615 }
616
617 $data['appointment_duration'] = $helperService->secondsToNiceDuration($duration);
618
619 /** @var string $break */
620 $break = $type === 'whatsapp' ? '; ' : PHP_EOL;
621
622 $lastBookingExtraIds = [];
623 if ($bookingKey === null) {
624 $lastBookingId = $this->getBookingKeyForEmployee($appointmentArray);
625
626 $lastBooking = array_filter(
627 $appointmentArray['bookings'],
628 function ($b) use ($lastBookingId) {
629 return $b['id'] === $lastBookingId;
630 }
631 );
632
633 foreach ($lastBooking ? array_shift($lastBooking)['extras'] : [] as $ex) {
634 $lastBookingExtraIds[$ex['extraId']] = $ex;
635 }
636 }
637
638 $allExtraNames = "";
639 $allExtraDetails = "";
640 $allExtraSum = 0;
641
642 $invoiceItems = [];
643
644 /** @var Extra $extra */
645 foreach ($extras->getItems() as $extra) {
646 $extraId = $extra->getId()->getValue();
647
648 $data["service_extra_{$extraId}_name"] =
649 array_key_exists($extraId, $bookingExtras) ? $extra->getName()->getValue() : '';
650
651 $data["service_extra_{$extraId}_name"] = $helperService->getBookingTranslation(
652 $bookingKey !== null ? $locale : null,
653 $data["service_extra_{$extraId}_name"] && $extra->getTranslations() ?
654 $extra->getTranslations()->getValue() : null,
655 'name'
656 ) ?: $data["service_extra_{$extraId}_name"];
657
658 $data["service_extra_{$extraId}_quantity"] =
659 array_key_exists($extraId, $bookingExtras) ? $bookingExtras[$extraId]['quantity'] : '';
660
661 $data["service_extra_{$extraId}_price"] = array_key_exists($extraId, $bookingExtras) ?
662 $helperService->getFormattedPrice($extra->getPrice()->getValue()) : '';
663
664 $multiplyByNumberOfPeople = ($extra->getAggregatedPrice() === null ? $service->getAggregatedPrice()->getValue()
665 : $extra->getAggregatedPrice()->getValue()) && $persons !== 1;
666
667 if (!empty($data["service_extra_{$extraId}_name"])) {
668 $allExtraNames .= $data["service_extra_{$extraId}_name"].', ';
669 }
670
671 if (array_key_exists($extraId, $bookingExtras) && $bookingExtras[$extraId]['quantity'] !== 0) {
672 if ($bookingKey === null) {
673 if (!array_key_exists($extraId, $lastBookingExtraIds)) {
674 continue;
675 }
676 }
677 $allExtraDetails .= ($type === 'email' ? '<p>' : '') . $extra->getName()->getValue() . ': (' .
678 $helperService->getFormattedPrice($extra->getPrice()->getValue()) . ' x ' .
679 $bookingExtras[$extraId]['quantity'] . ') ' .
680 ($multiplyByNumberOfPeople ? ('x ' . $persons) : '') .
681 ($type === 'email' ? '</p>' : $break);
682
683 $allExtraSum += $extra->getPrice()->getValue() * $bookingExtras[$extraId]['quantity'] *
684 ($multiplyByNumberOfPeople ? $persons : 1);
685
686 $invoiceItems[] = [
687 'item_id' => $extraId,
688 'item_index' => "extra_{$extraId}_{$bookingExtras[$extraId]['price']}",
689 'item_name' => $extra->getName()->getValue(),
690 'invoice_qty' => $bookingExtras[$extraId]['quantity'] * $persons,
691 'invoice_unit_price' => $bookingExtras[$extraId]['price'],
692 'invoice_subtotal' => $bookingExtras[$extraId]['quantity'] * $bookingExtras[$extraId]['price'] *
693 ($multiplyByNumberOfPeople ? $persons : 1),
694 'invoice_tax' => 0
695 ];
696 }
697 }
698
699 $data["service_extras"] = substr($allExtraNames, 0, -2);
700
701 $data['deposit'] = $service->getDeposit() && $service->getDeposit()->getValue();
702
703 $data['invoice_items_extras'] = $invoiceItems;
704
705 $data["service_extras_details"] = $allExtraDetails ? ($allExtraDetails . ($type !== 'whatsapp' ?
706 ($type === 'email' ? '<p>' : $break) . "-------------------------" . ($type === 'email' ? '</p>' : $break) : '') .
707 ($type === 'email' ? '<p>' : '') . BackendStrings::getCommonStrings()['extras_total_price'] .
708 " {$helperService->getFormattedPrice($allExtraSum)}" . ($type === 'email' ? '</p>' : '')) : "";
709
710 return $data;
711 }
712
713 /**
714 * @param $appointment
715 * @param $bookingKey
716 *
717 * @return array
718 *
719 * @throws ContainerValueNotFoundException
720 * @throws NotFoundException
721 * @throws QueryExecutionException
722 */
723 public function getEmployeeData($appointment, $bookingKey = null)
724 {
725 /** @var HelperService $helperService */
726 $helperService = $this->container->get('application.helper.service');
727
728 /** @var UserRepository $userRepository */
729 $userRepository = $this->container->get('domain.users.repository');
730
731 /** @var LocationRepository $locationRepository */
732 $locationRepository = $this->container->get('domain.locations.repository');
733
734 /** @var SettingsService $settingsService */
735 $settingsService = $this->container->get('domain.settings.service');
736
737 /** @var Provider $user */
738 $user = $userRepository->getById($appointment['providerId']);
739
740 $locale = $this->getLocale($appointment, $bookingKey);
741
742 if (!empty($appointment['locationId'])) {
743 $locationId = $appointment['locationId'];
744 } else {
745 $locationId = $user->getLocationId() ? $user->getLocationId()->getValue() : null;
746 }
747
748 /** @var Location $location */
749 $location = $locationId ? $locationRepository->getById($locationId) : null;
750
751 $locationName = $settingsService->getSetting('company', 'address');
752
753 $locationDescription = '';
754
755 if ($location) {
756 $locationName = $helperService->getBookingTranslation(
757 $bookingKey !== null ? $locale : null,
758 $location->getTranslations() ? $location->getTranslations()->getValue() : null,
759 'name'
760 ) ?: $location->getName()->getValue();
761
762 $locationDescription = $helperService->getBookingTranslation(
763 $bookingKey !== null ? $locale : null,
764 $location->getTranslations() ? $location->getTranslations()->getValue() : null,
765 'description'
766 ) ?: ($location->getDescription() ? $location->getDescription()->getValue() : '');
767 }
768
769 $firstName = $helperService->getBookingTranslation(
770 $bookingKey !== null ? $locale : null,
771 $user->getTranslations() ? $user->getTranslations()->getValue() : null,
772 'firstName'
773 ) ?: $user->getFirstName()->getValue();
774
775 $lastName = $helperService->getBookingTranslation(
776 $bookingKey !== null ? $locale : null,
777 $user->getTranslations() ? $user->getTranslations()->getValue() : null,
778 'lastName'
779 ) ?: $user->getLastName()->getValue();
780
781 $userDescription = $helperService->getBookingTranslation(
782 $bookingKey !== null ? $locale : null,
783 $user->getTranslations() ? $user->getTranslations()->getValue() : null,
784 'description'
785 ) ?: ($user->getDescription() ? $user->getDescription()->getValue() : '');
786
787 return [
788 'employee_id' => $user->getId()->getValue(),
789 'employee_email' => $user->getEmail()->getValue(),
790 'employee_first_name' => $firstName,
791 'employee_last_name' => $lastName,
792 'employee_full_name' => $firstName . ' ' . $lastName,
793 'employee_phone' => $user->getPhone()->getValue(),
794 'employee_note' => $user->getNote() ? $user->getNote()->getValue() : '',
795 'employee_description' => $userDescription,
796 'employee_panel_url' => trim(
797 $this->container->get('domain.settings.service')
798 ->getSetting('roles', 'providerCabinet')['pageUrl']
799 ),
800 'location_address' => !$location ?
801 $settingsService->getSetting('company', 'address') : $location->getAddress()->getValue(),
802 'location_phone' => !$location ?
803 $settingsService->getSetting('company', 'phone') : $location->getPhone()->getValue(),
804 'location_id' => $locationId,
805 'location_name' => $locationName,
806 'location_description' => $locationDescription,
807 'location_latitude' => $location && $location->getCoordinates() ? $location->getCoordinates()->getLatitude() : null,
808 'location_longitude' => $location && $location->getCoordinates() ? $location->getCoordinates()->getLongitude() : null,
809 ];
810 }
811
812 /**
813 * @param array $appointment
814 * @param int $bookingKey
815 * @param string $type
816 * @param string $placeholderType
817 *
818 * @return array
819 *
820 * @throws ContainerValueNotFoundException
821 * @throws NotFoundException
822 * @throws QueryExecutionException
823 * @throws ContainerException
824 * @throws Exception
825 */
826 public function getRecurringAppointmentsData($appointment, $bookingKey, $type, $placeholderType, $bookingKeyForEmployee = null)
827 {
828 if (!array_key_exists('recurring', $appointment)) {
829 return [
830 "{$placeholderType}_appointments_details" => ''
831 ];
832 }
833
834 /** @var CustomerBookingRepository $bookingRepository */
835 $bookingRepository = $this->container->get('domain.booking.customerBooking.repository');
836
837 /** @var PlaceholderService $placeholderService */
838 $placeholderService = $this->container->get("application.placeholder.appointment.service");
839
840 /** @var SettingsService $settingsService */
841 $settingsService = $this->container->get('domain.settings.service');
842
843 $appointmentsSettings = $settingsService->getCategorySettings('appointments');
844
845 $recurringAppointmentDetails = [];
846
847 foreach ($appointment['recurring'] as $recurringData) {
848 $recurringBookingKey = null;
849 $recurringBookingKeyForEmployee = null;
850
851 $isForCustomer =
852 $bookingKey !== null ||
853 (isset($appointment['isForCustomer']) && $appointment['isForCustomer']);
854
855 if ($isForCustomer) {
856 foreach ($recurringData['appointment']['bookings'] as $key => $recurringBooking) {
857 if (isset($recurringData['booking']['id'])) {
858 if ($recurringBooking['id'] === $recurringData['booking']['id']) {
859 $recurringBookingKey = $key;
860 }
861 } else if ($recurringBooking['customerId'] === $appointment['bookings'][$bookingKey]['customerId']) {
862 $recurringBookingKey = $key;
863 }
864 }
865
866 if ($recurringBookingKey === null) {
867 return [];
868 }
869 } elseif ($bookingKeyForEmployee !== null) {
870 foreach ($recurringData['appointment']['bookings'] as $key => $recurringBooking) {
871 if (isset($recurringData['booking']['id'])) {
872 if ($recurringBooking['id'] === $recurringData['booking']['id']) {
873 $recurringBookingKeyForEmployee = $key;
874 }
875 } else {
876 $recurringBookingKeyForEmployee = $bookingKeyForEmployee;
877 }
878 }
879 }
880
881 $token =
882 $recurringBookingKey !== null &&
883 isset(
884 $recurringData['appointment']['bookings'][$recurringBookingKey],
885 $recurringData['appointment']['bookings'][$recurringBookingKey]['id']
886 ) ? $bookingRepository->getToken($recurringData['appointment']['bookings'][$recurringBookingKey]['id']) :(
887 $recurringBookingKeyForEmployee !== null &&
888 isset(
889 $recurringData['appointment']['bookings'][$recurringBookingKeyForEmployee],
890 $recurringData['appointment']['bookings'][$recurringBookingKeyForEmployee]['id']
891 ) ? $bookingRepository->getToken($recurringData['appointment']['bookings'][$recurringBookingKeyForEmployee]['id']) :
892 null);
893
894 $recurringPlaceholders = [];
895
896 $recurringPlaceholders = array_merge(
897 $recurringPlaceholders,
898 $this->getEmployeeData($recurringData['appointment'], $recurringBookingKey)
899 );
900
901 $recurringPlaceholders = array_merge(
902 $recurringPlaceholders,
903 $this->getAppointmentData($recurringData['appointment'], $recurringBookingKey, $type)
904 );
905
906 $recurringPlaceholders = array_merge(
907 $recurringPlaceholders,
908 $this->getServiceData($recurringData['appointment'], $recurringBookingKey, $type)
909 );
910
911 $recurringPlaceholders = array_merge(
912 $recurringPlaceholders,
913 $this->getCustomFieldsData($recurringData['appointment'], $type, $recurringBookingKey)
914 );
915
916 $recurringPlaceholders = array_merge(
917 $recurringPlaceholders,
918 $this->getBookingData(
919 $recurringData['appointment'],
920 $type,
921 $recurringBookingKey,
922 isset($token['token']) ? $token['token'] : null,
923 $recurringPlaceholders['deposit']
924 )
925 );
926
927 unset($recurringPlaceholders['icsFiles']);
928
929 if (!$isForCustomer) {
930 if (isset($recurringPlaceholders['appointment_cancel_url'])) {
931 $recurringPlaceholders['appointment_cancel_url'] = '';
932 }
933
934 $recurringPlaceholders['zoom_join_url'] = '';
935 } else {
936 $recurringPlaceholders['employee_panel_url'] = '';
937
938 $recurringPlaceholders['zoom_host_url'] = '';
939 }
940
941 $placeholderString =
942 $placeholderType .
943 'Placeholders' .
944 ($isForCustomer && $placeholderType === 'package' ? 'Customer' : '') .
945 ($isForCustomer && $placeholderType === 'recurring' ? 'Customer' : '') .
946 ($isForCustomer && $placeholderType === 'cart' ? 'Customer' : '') .
947 ($type === 'email' ? '' : 'Sms');
948
949 /** @var HelperService $helperService */
950 $helperService = $this->container->get('application.helper.service');
951
952 $content = $helperService->getBookingTranslation(
953 $recurringBookingKey !== null ? $helperService->getLocaleFromBooking($recurringData['appointment']['bookings'][$recurringBookingKey]['info']) : null,
954 json_encode($appointmentsSettings['translations']),
955 $placeholderString
956 ) ?: $appointmentsSettings[$placeholderString];
957
958 if ($type === 'whatsapp') {
959 $content = str_replace(array("\n","\r"), '; ', $content);
960 $content = preg_replace('!\s+!', ' ', $content);
961 }
962
963 $recurringAppointmentDetails[] = $placeholderService->applyPlaceholders(
964 $content,
965 $recurringPlaceholders
966 );
967 }
968
969 return [
970 "{$placeholderType}_appointments_details" => $recurringAppointmentDetails ? implode(
971 $type === 'email' ? '<p><br></p>' : ($type === 'whatsapp' ? '; ' : PHP_EOL),
972 $recurringAppointmentDetails
973 ) : ''
974 ];
975 }
976
977 /**
978 * @param array $appointment
979 * @param int $bookingKey
980 * @param string $type
981 *
982 * @return array
983 *
984 * @throws ContainerValueNotFoundException
985 * @throws NotFoundException
986 * @throws QueryExecutionException
987 * @throws ContainerException
988 * @throws Exception
989 */
990 public function getGroupedAppointmentData($appointment, $bookingKey, $type)
991 {
992 /** @var PlaceholderService $placeholderService */
993 $placeholderService = $this->container->get("application.placeholder.appointment.service");
994
995 /** @var SettingsService $settingsService */
996 $settingsService = $this->container->get('domain.settings.service');
997
998 $appointmentsSettings = $settingsService->getCategorySettings('appointments');
999
1000 $groupAppointmentDetails = [];
1001
1002 if ($bookingKey) {
1003 return [
1004 "group_appointment_details" => ''
1005 ];
1006 }
1007
1008 foreach ($appointment['bookings'] as $bookingId => $booking) {
1009 if ($booking['status'] === BookingStatus::CANCELED || $booking['status'] === BookingStatus::REJECTED || $booking['status'] === BookingStatus::NO_SHOW) {
1010 continue;
1011 }
1012
1013 /** @var CustomerBookingRepository $bookingRepository */
1014 $bookingRepository = $this->container->get('domain.booking.customerBooking.repository');
1015
1016 $token = $bookingRepository->getToken($appointment['bookings'][$bookingId]['id']);
1017
1018 $groupPlaceholders = array_merge(
1019 $this->getAppointmentData($appointment, $bookingId, $type),
1020 $this->getServiceData($appointment, $bookingId, $type),
1021 $this->getCustomFieldsData($appointment, $type, $bookingId),
1022 $this->getCustomersData($appointment, $type, $bookingId),
1023 $this->getBookingData(
1024 $appointment,
1025 $type,
1026 $bookingId,
1027 isset($token['token']) ? $token['token'] : null,
1028 null,
1029 true
1030 )
1031 );
1032
1033 $content = $appointmentsSettings['groupAppointmentPlaceholder' . ($type === null || $type === 'email' ? '' : 'Sms')] ;
1034 if ($type === 'email') {
1035 $content = str_replace(array("\n","\r"), '', $content);
1036 } else if ($type === 'whatsapp') {
1037 $content = str_replace(array("\n","\r"), '; ', $content);
1038 $content = preg_replace('!\s+!', ' ', $content);
1039 }
1040
1041 $groupAppointmentDetails[] = $placeholderService->applyPlaceholders(
1042 $content,
1043 $groupPlaceholders
1044 );
1045 }
1046
1047 return [
1048 "group_appointment_details" => $groupAppointmentDetails ? implode(
1049 $type === 'email' ? '<p><br></p>' : ($type === 'whatsapp' ? '; ' : PHP_EOL),
1050 $groupAppointmentDetails
1051 ) : ''
1052 ];
1053 }
1054
1055 /**
1056 * @param array $bookingArray
1057 * @param array $entity
1058 *
1059 * @return array
1060 * @throws InvalidArgumentException
1061 * @throws NotFoundException
1062 * @throws QueryExecutionException
1063 */
1064 public function getAmountData(&$bookingArray, $entity, $invoice = false)
1065 {
1066 /** @var ReservationServiceInterface $reservationService */
1067 $reservationService = $this->container->get('application.reservation.service')->get(Entities::APPOINTMENT);
1068
1069 if (!empty($bookingArray['couponId']) && empty($bookingArray['coupon'])) {
1070 /** @var CouponRepository $couponRepository */
1071 $couponRepository = $this->container->get('domain.coupon.repository');
1072
1073 /** @var Coupon $coupon */
1074 $coupon = $couponRepository->getById($bookingArray['couponId']);
1075
1076 $bookingArray['coupon'] = $coupon ? $coupon->toArray() : null;
1077 }
1078
1079 $extras = [];
1080
1081 foreach ($bookingArray['extras'] as $extra) {
1082 $extras[$extra['extraId']] = [
1083 'price' => $extra['price'],
1084 'aggregatedPrice' => !!$extra['aggregatedPrice'],
1085 ];
1086 }
1087
1088 /** @var AbstractBookable $bookable */
1089 $bookable = ServiceFactory::create(
1090 [
1091 'price' => $bookingArray['price'],
1092 'aggregatedPrice' => !empty($bookingArray['aggregatedPrice']),
1093 'extras' => $extras,
1094 ]
1095 );
1096
1097 /** @var CustomerBooking $booking */
1098 $booking = CustomerBookingFactory::create(
1099 [
1100 'persons' => $bookingArray['persons'],
1101 'coupon' => !empty($bookingArray['coupon']) ? $bookingArray['coupon'] : null,
1102 'extras' => $bookingArray['extras'],
1103 'tax' => !empty($bookingArray['tax']) ? $bookingArray['tax'] : null,
1104 ]
1105 );
1106
1107 return $reservationService->getPaymentAmount($booking, $bookable, $invoice);
1108 }
1109 }
1110