PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.3
Booking for Appointments and Events Calendar – Amelia v2.3
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 / Services / Placeholder / PlaceholderService.php
ameliabooking / src / Application / Services / Placeholder Last commit date
AppointmentPlaceholderService.php 2 months ago AppointmentsPlaceholderService.php 5 months ago BasicPackagePlaceholderService.php 6 months ago EventPlaceholderService.php 2 months ago PlaceholderService.php 2 months ago PlaceholderServiceInterface.php 6 months ago
PlaceholderService.php
1304 lines
1 <?php
2
3 /**
4 * @copyright © Melograno Ventures. All rights reserved.
5 * @licence See LICENCE.md for license details.
6 */
7
8 namespace AmeliaBooking\Application\Services\Placeholder;
9
10 use AmeliaBooking\Application\Services\Coupon\AbstractCouponApplicationService;
11 use AmeliaBooking\Application\Services\Helper\HelperService;
12 use AmeliaBooking\Domain\Collection\Collection;
13 use AmeliaBooking\Domain\Common\Exceptions\CouponInvalidException;
14 use AmeliaBooking\Domain\Common\Exceptions\CouponExpiredException;
15 use AmeliaBooking\Domain\Common\Exceptions\CouponUnknownException;
16 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
17 use AmeliaBooking\Domain\Entity\Bookable\Service\Service;
18 use AmeliaBooking\Domain\Entity\Booking\Event\Event;
19 use AmeliaBooking\Domain\Entity\Coupon\Coupon;
20 use AmeliaBooking\Domain\Entity\CustomField\CustomField;
21 use AmeliaBooking\Domain\Entity\Entities;
22 use AmeliaBooking\Domain\Entity\User\AbstractUser;
23 use AmeliaBooking\Domain\Entity\User\Customer;
24 use AmeliaBooking\Domain\Factory\User\UserFactory;
25 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
26 use AmeliaBooking\Domain\Services\Settings\SettingsService;
27 use AmeliaBooking\Domain\ValueObjects\Number\Integer\LoginType;
28 use AmeliaBooking\Domain\ValueObjects\String\BookingStatus;
29 use AmeliaBooking\Domain\ValueObjects\String\PaymentStatus;
30 use AmeliaBooking\Infrastructure\Common\Container;
31 use AmeliaBooking\Infrastructure\Common\Exceptions\NotFoundException;
32 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
33 use AmeliaBooking\Infrastructure\Repository\Bookable\Service\PackageCustomerRepository;
34 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\AppointmentRepository;
35 use AmeliaBooking\Infrastructure\Repository\Booking\Event\EventRepository;
36 use AmeliaBooking\Infrastructure\Repository\Coupon\CouponRepository;
37 use AmeliaBooking\Infrastructure\Repository\CustomField\CustomFieldRepository;
38 use AmeliaBooking\Infrastructure\Repository\User\UserRepository;
39 use AmeliaBooking\Infrastructure\WP\Translations\BackendStrings;
40 use AmeliaBooking\Infrastructure\WP\Translations\FrontendStrings;
41 use AmeliaBooking\Domain\ValueObjects\String\CustomFieldType;
42 use AmeliaBooking\Infrastructure\WP\Translations\LiteBackendStrings;
43 use Exception;
44 use Interop\Container\Exception\ContainerException;
45 use DateTime;
46 use Slim\Exception\ContainerValueNotFoundException;
47
48 /**
49 * Class PlaceholderService
50 *
51 * @package AmeliaBooking\Application\Services\Placeholder
52 */
53 abstract class PlaceholderService implements PlaceholderServiceInterface
54 {
55 /** @var Container */
56 protected $container;
57
58 /**
59 * ProviderApplicationService constructor.
60 *
61 * @param Container $container
62 *
63 * @throws \InvalidArgumentException
64 */
65 public function __construct(Container $container)
66 {
67 $this->container = $container;
68 }
69
70 /**
71 * @param string $text
72 * @param array $data
73 *
74 * @return mixed
75 */
76 public function applyPlaceholders($text, $data)
77 {
78 unset($data['icsFiles']);
79
80 unset($data['providersAppointments']);
81
82 unset($data['invoice_items_booking']);
83 unset($data['invoice_items_extras']);
84 unset($data['invoice_items_event']);
85 unset($data['invoice_dates']);
86 unset($data['invoice_dates_xml']);
87 unset($data['items']);
88 unset($data['qr_code_tickets']);
89
90 $data = array_filter($data, function ($key) {
91 return strpos($key, 'invoice_custom_field') !== 0;
92 }, ARRAY_FILTER_USE_KEY);
93
94 $placeholders = array_map(
95 function ($placeholder) {
96 return "%{$placeholder}%";
97 },
98 array_keys($data)
99 );
100
101 if ($text && strpos($text, '%amelia_dynamic_placeholder_') !== false) {
102 $lastPos = 0;
103
104 $dynamicPlaceholderStart = '%amelia_dynamic_placeholder_';
105
106 while (($lastPos = strpos($text, $dynamicPlaceholderStart, $lastPos)) !== false) {
107 $subText = substr($text, $lastPos + 1);
108
109 $dynamicPlaceholder = substr($subText, 0, strpos($subText, '%'));
110
111 $placeholders[] = '%' . $dynamicPlaceholder . '%';
112
113 $data[$dynamicPlaceholder] = apply_filters(
114 $dynamicPlaceholder,
115 $data
116 );
117
118 $lastPos = $lastPos + strlen($dynamicPlaceholderStart);
119 }
120 }
121
122 return str_replace($placeholders, array_values($data), $text);
123 }
124
125 /**
126 * @return array
127 *
128 * @throws ContainerException
129 */
130 public function getPlaceholdersDummyData($type)
131 {
132 /** @var SettingsService $settingsService */
133 $settingsService = $this->container->get('domain.settings.service');
134
135 /** @var string $paragraphStart */
136 $paragraphStart = $type === 'email' ? '<p>' : '';
137
138 /** @var string $paragraphEnd */
139 $paragraphEnd = $type === 'email' ? '</p>' : ($type === 'whatsapp' ? '; ' : PHP_EOL);
140
141 $companySettings = $settingsService->getCategorySettings('company');
142
143 $timezone = get_option('timezone_string');
144
145 return array_merge(
146 [
147 'booked_customer' =>
148 $paragraphStart .
149 BackendStrings::get('ph_customer_full_name') .
150 ': John Micheal Doe ' .
151 $paragraphEnd .
152 $paragraphStart .
153 BackendStrings::get('ph_customer_phone') .
154 ': 193-951-2600 ' .
155 $paragraphEnd .
156 $paragraphStart .
157 BackendStrings::get('ph_customer_email') .
158 ': customer@domain.com ' .
159 $paragraphEnd,
160 'company_address' => $companySettings['address'],
161 'company_country' => $companySettings['countryCode'],
162 'company_name' => $companySettings['name'],
163 'company_phone' => $companySettings['phone'],
164 'company_website' => $companySettings['website'],
165 'company_vat_number' => $companySettings['vat'],
166 'company_email' => !empty($companySettings['email']) ? $companySettings['email'] : '',
167 'customer_email' => 'customer@domain.com',
168 'customer_first_name' => 'John',
169 'customer_last_name' => 'Doe',
170 'customer_full_name' => 'John Doe',
171 'customer_phone' => '193-951-2600',
172 'customer_note' => 'Customer Note',
173 'customer_panel_url' => $this->container->get('domain.settings.service')->getSetting('roles', 'customerCabinet')['pageUrl'],
174 'coupon_used' => 'code123',
175 'number_of_persons' => 2,
176 'time_zone' => $timezone,
177 'employee_email' => 'employee@domain.com',
178 'employee_first_name' => 'Richard',
179 'employee_last_name' => 'Roe',
180 'employee_full_name' => 'Richard Roe',
181 'employee_phone' => '150-698-1858',
182 'employee_note' => 'Employee Note',
183 'employee_description' => 'Employee Description',
184 'employee_panel_url' => 'https://your_site.com/employee-panel',
185 'location_address' => $companySettings['address'] ? $companySettings['address'] : 'Address 123',
186 'location_phone' => $companySettings['phone'],
187 'location_name' => 'Location Name',
188 'location_latitude' => '40.748441',
189 'location_longitude' => '-73.987853',
190 'location_description' => 'Location Description',
191 ],
192 $this->getEntityPlaceholdersDummyData($type)
193 );
194 }
195
196 /**
197 * @param string|null $locale
198 *
199 * @return array
200 */
201 public function getCompanyData($locale = null)
202 {
203 /** @var SettingsService $settingsService */
204 $settingsService = $this->container->get('domain.settings.service');
205
206 /** @var HelperService $helperService */
207 $helperService = $this->container->get('application.helper.service');
208
209 $companySettings = $settingsService->getCategorySettings('company');
210
211 $companyName = $helperService->getBookingTranslation(
212 $locale,
213 json_encode($companySettings['translations']),
214 'name'
215 ) ?: $companySettings['name'];
216
217 return [
218 'company_address' => $companySettings['address'],
219 'company_country' => $companySettings['countryCode'],
220 'company_name' => $companyName,
221 'company_phone' => $companySettings['phone'],
222 'company_website' => $companySettings['website'],
223 'company_vat_number' => $companySettings['vat'],
224 'company_email' => !empty($companySettings['email']) ? $companySettings['email'] : null,
225 'company_logo' => $companySettings['pictureThumbPath']
226 ];
227 }
228
229 /**
230 * @param array $appointment
231 * @param string $type
232 * @param null $bookingKey
233 * @param null $token
234 *
235 * @return array
236 *
237 * @throws ContainerException
238 */
239 protected function getBookingData($appointment, $type, $bookingKey = null, $token = null, $depositEnabled = null, $isGroup = null, $invoice = false)
240 {
241 /** @var HelperService $helperService */
242 $helperService = $this->container->get('application.helper.service');
243
244 /** @var string $break */
245 $break = $type === 'email' ? '<p><br></p>' : ($type === 'whatsapp' ? '; ' : PHP_EOL);
246
247 $couponsUsed = [];
248
249 $payment = null;
250
251 $invoiceItem = [];
252
253 $paymentLinks = [
254 'payment_link_woocommerce' => '',
255 'payment_link_stripe' => '',
256 'payment_link_paypal' => '',
257 'payment_link_razorpay' => '',
258 'payment_link_mollie' => '',
259 'payment_link_square' => '',
260 'payment_link_barion' => ''
261 ];
262
263 $couponDiscount = 0;
264
265 $amountData = [
266 'price' => 0,
267 'discount' => 0,
268 'deduction' => 0,
269 ];
270
271 // If notification is for provider: Appointment price will be sum of all bookings prices
272 // If notification is for customer: Appointment price will be price of his booking
273 if ($bookingKey === null) {
274 $numberOfPersonsData = [
275 AbstractUser::USER_ROLE_PROVIDER => [
276 BookingStatus::APPROVED => 0,
277 BookingStatus::PENDING => 0,
278 BookingStatus::CANCELED => 0,
279 BookingStatus::REJECTED => 0,
280 BookingStatus::NO_SHOW => 0,
281 BookingStatus::WAITING => 0,
282 ]
283 ];
284
285 foreach ((array)$appointment['bookings'] as $customerBooking) {
286 $amountData = $this->getAmountData($customerBooking, $appointment);
287
288 $expirationDate = null;
289
290 if (!empty($customerBooking['coupon']['expirationDate'])) {
291 $expirationDate = $customerBooking['coupon']['expirationDate'];
292 }
293
294 $startDate = null;
295
296 if (!empty($customerBooking['coupon']['startDate'])) {
297 $startDate = $customerBooking['coupon']['startDate'];
298 }
299
300 if (($amountData['discount'] || $amountData['deduction']) && !empty($customerBooking['info'])) {
301 $customerData = json_decode($customerBooking['info'], true);
302
303 if (!$customerData) {
304 $customerData = [
305 'firstName' => $customerBooking['customer']['firstName'],
306 'lastName' => $customerBooking['customer']['lastName'],
307 ];
308 }
309
310 $couponsUsed[] =
311 BackendStrings::get('customer') . ': ' .
312 $customerData['firstName'] . ' ' . $customerData['lastName'] . ' ' . $break .
313 BackendStrings::get('code') . ': ' .
314 $customerBooking['coupon']['code'] . ' ' . $break .
315 ($amountData['discount'] ? BackendStrings::get('discount_amount') . ': ' .
316 $helperService->getFormattedPrice($amountData['discount']) . ' ' . $break : '') .
317 ($amountData['deduction'] ? BackendStrings::get('deduction') . ': ' .
318 $helperService->getFormattedPrice($amountData['deduction']) . ' ' . $break : '') .
319 ($startDate ? BackendStrings::get('start_date') . ': ' .
320 $startDate . ' ' . $break : '') .
321 ($expirationDate ? BackendStrings::get('expiration_date') . ': ' .
322 $expirationDate : '');
323 }
324
325 $numberOfPersonsData[AbstractUser::USER_ROLE_PROVIDER][$customerBooking['status']] +=
326 empty($customerBooking['ticketsData']) ? $customerBooking['persons'] : array_sum(array_column($customerBooking['ticketsData'], 'persons'));
327
328 $payment = !empty($customerBooking['payments'][0]) ? $customerBooking['payments'][0] : null;
329 }
330
331 $numberOfPersons = [];
332
333 foreach ($numberOfPersonsData[AbstractUser::USER_ROLE_PROVIDER] as $key => $value) {
334 if ($value) {
335 $numberOfPersons[] = BackendStrings::get($key) . ': ' . $value;
336 }
337 }
338
339 $numberOfPersons = implode($break, $numberOfPersons);
340
341 $icsFiles = !empty($appointment['bookings'][0]['icsFiles']) ? $appointment['bookings'][0]['icsFiles'] : [];
342 } else {
343 $amountData = $this->getAmountData($appointment['bookings'][$bookingKey], $appointment, $invoice);
344
345 $couponDiscount = $amountData['discount'] + $amountData['deduction'];
346
347 $expirationDate = null;
348
349 if (!empty($appointment['bookings'][$bookingKey]['coupon']['expirationDate'])) {
350 $expirationDate = $appointment['bookings'][$bookingKey]['coupon']['expirationDate'];
351 }
352
353 $startDate = null;
354
355 if (!empty($appointment['bookings'][$bookingKey]['coupon']['startDate'])) {
356 $startDate = $appointment['bookings'][$bookingKey]['coupon']['startDate'];
357 }
358
359 if (!empty($appointment['bookings'][$bookingKey]['coupon']['code'])) {
360 $couponsUsed[] =
361 $appointment['bookings'][$bookingKey]['coupon']['code'] . ' ' . $break .
362 ($amountData['discount'] ? BackendStrings::get('discount_amount') . ': ' .
363 $helperService->getFormattedPrice($amountData['discount']) . ' ' . $break : '') .
364 ($amountData['deduction'] ? BackendStrings::get('deduction') . ': ' .
365 $helperService->getFormattedPrice($amountData['deduction']) . ' ' . $break : '') .
366 ($startDate ? BackendStrings::get('start_date') . ': ' .
367 $startDate . ' ' . $break : '') .
368 ($expirationDate ? BackendStrings::get('expiration_date') . ': ' .
369 $expirationDate : '');
370 }
371
372 $numberOfPersons =
373 empty($appointment['bookings'][$bookingKey]['ticketsData']) ?
374 $appointment['bookings'][$bookingKey]['persons'] :
375 array_sum(array_column($appointment['bookings'][$bookingKey]['ticketsData'], 'persons'));
376
377 $invoiceItem['invoice_qty'] = $amountData['qty'];
378 $invoiceItem['invoice_unit_price'] = $amountData['unit_price'];
379 $invoiceItem['invoice_subtotal'] = $amountData['subtotal'];
380 $invoiceItem['invoice_tax'] = $amountData['tax'];
381 $invoiceItem['invoice_tax_rate'] = $amountData['tax_rate'];
382 $invoiceItem['invoice_tax_excluded'] = $amountData['tax_excluded'];
383 $invoiceItem['invoice_tax_type'] = $amountData['tax_type'];
384 $invoiceItem['total_tax'] = $amountData['total_tax'];
385 $invoiceItem['invoice_extras_items'] = !empty($amountData['extras_items']) ? $amountData['extras_items'] : null;
386 $invoiceItem['invoice_tickets_tax'] = !empty($amountData['tickets_tax']) ? $amountData['tickets_tax'] : null;
387 $invoiceItem['service_discount'] = !empty($amountData['service_discount']) ? $amountData['service_discount'] : null;
388
389 $icsFiles = !empty($appointment['bookings'][$bookingKey]['icsFiles']) ? $appointment['bookings'][$bookingKey]['icsFiles'] : [];
390
391 $payment = !empty($appointment['bookings'][$bookingKey]['payments'][0]) ? $appointment['bookings'][$bookingKey]['payments'][0] : null;
392
393 $invoiceItem['invoice_paid_amount'] = 0;
394 $invoiceItem['invoice_method'] = '';
395 foreach (!empty($appointment['bookings'][$bookingKey]['payments']) ? $appointment['bookings'][$bookingKey]['payments'] : [] as $p) {
396 if ($p['status'] === PaymentStatus::PARTIALLY_PAID || $p['status'] === PaymentStatus::PAID) {
397 $invoiceItem['invoice_paid_amount'] += $p['amount'];
398 $invoiceItem['invoice_method'] = $p['gateway'];
399 }
400 }
401
402 $invoiceItem['invoice_discount'] = !empty($amountData['full_discount']) && $amountData['full_discount'] > 0 ? $amountData['full_discount'] : 0;
403
404
405 if (!empty($payment['paymentLinks'])) {
406 foreach ($payment['paymentLinks'] as $paymentType => $paymentLink) {
407 $paymentLinks[$paymentType] = $type === 'email' ? '<a href="' . $paymentLink . '">' . $paymentLink . '</a>' : $paymentLink;
408 }
409 }
410 }
411
412 $depositAmount = null;
413 if (!empty($appointment['deposit']) || $depositEnabled) {
414 $depositAmount = $payment ? $payment['amount'] : 0;
415 }
416 $paymentType = '';
417 if ($payment) {
418 switch ($payment['gateway']) {
419 case 'onSite':
420 $paymentType = BackendStrings::get('on_site');
421 break;
422 case 'wc':
423 $paymentType = BackendStrings::get('wc_name');
424 break;
425 case 'square':
426 $paymentType = BackendStrings::get('square');
427 break;
428 default:
429 $paymentType = BackendStrings::get($payment['gateway']);
430 break;
431 }
432 }
433
434 $appointmentPrice = $helperService->getFormattedPrice($amountData['price'] >= 0 ? $amountData['price'] : 0);
435
436 $paymentDueAmount = $payment ?
437 $helperService->getFormattedPrice(
438 ($amountData['price'] >= 0 ? $amountData['price'] : 0) -
439 ($payment['amount'] - (!empty($payment['wcItemTaxValue']) ? $payment['wcItemTaxValue'] : 0))
440 ) : '';
441
442 $bookingKeyForEmployee = null;
443
444 if ($bookingKey === null || $isGroup) {
445 $bookingKeyForEmployee = $isGroup ?
446 $appointment['bookings'][$bookingKey]['id'] : $this->getBookingKeyForEmployee($appointment);
447 }
448
449 /** @var SettingsService $settingsService */
450 $settingsService = $this->container->get('domain.settings.service');
451
452 $dateFormat = $settingsService->getSetting('wordpress', 'dateFormat');
453
454 $customerWaiting = $bookingKey !== null && $appointment['bookings'][$bookingKey]['status'] === BookingStatus::WAITING;
455
456 return array_merge(
457 $paymentLinks,
458 [
459 "appointment_price" => $appointmentPrice,
460 "booking_price" => $appointmentPrice,
461 "{$appointment['type']}_cancel_url" =>
462 $bookingKey !== null && isset($appointment['bookings'][$bookingKey]['id']) ?
463 AMELIA_ACTION_URL . '/bookings/cancel/' . $appointment['bookings'][$bookingKey]['id'] .
464 ($token ? '&token=' . $token : '') . "&type={$appointment['type']}" : '',
465 'appointment_approve_url' =>
466 ($bookingKeyForEmployee !== null || $customerWaiting) ? (AMELIA_ACTION_URL . '/bookings/success/' .
467 ($customerWaiting ? $appointment['bookings'][$bookingKey]['id'] : $bookingKeyForEmployee) .
468 '&token=' . $token) : '',
469 'appointment_reject_url' =>
470 $bookingKeyForEmployee !== null ? (AMELIA_ACTION_URL . '/bookings/reject/' . $bookingKeyForEmployee .
471 '&token=' . $token) : '',
472 "{$appointment['type']}_deposit_payment" => $depositAmount !== null ? $helperService->getFormattedPrice($depositAmount) : '',
473 'payment_type' => $paymentType,
474 'payment_status' => $payment ? $payment['status'] : '',
475 'payment_gateway' => $payment ? $payment['gateway'] : '',
476 'payment_created' => $payment && !empty($payment['created'])
477 ? date_i18n($dateFormat, strtotime($payment['created']))
478 : '',
479 'payment_created_xml' => $payment && !empty($payment['created'])
480 ? date_i18n('Y-m-d', strtotime($payment['created']))
481 : '',
482 'payment_invoice_number' => $payment ? $payment['invoiceNumber'] : '',
483 'payment_gateway_title' => $payment ? $payment['gatewayTitle'] : '',
484 "payment_due_amount" => $paymentDueAmount,
485 'number_of_persons' => $numberOfPersons,
486 'coupon_used' => $couponsUsed ? implode($break, $couponsUsed) : '',
487 'icsFiles' => $icsFiles,
488 'invoice_items_booking' => [$invoiceItem]
489 ]
490 );
491 }
492
493 /** @noinspection MoreThanThreeArgumentsInspection */
494 /**
495 * @param array $appointment
496 * @param string $type
497 * @param null $bookingKey
498 * @param Customer $customerEntity
499 *
500 * @return array
501 *
502 * @throws \Slim\Exception\ContainerException
503 * @throws \InvalidArgumentException
504 * @throws \Slim\Exception\ContainerValueNotFoundException
505 * @throws NotFoundException
506 * @throws QueryExecutionException
507 * @throws ContainerException
508 * @throws \Exception
509 */
510 public function getCustomersData($appointment, $type, $bookingKey = null, $customerEntity = null)
511 {
512 /** @var UserRepository $userRepository */
513 $userRepository = $this->container->get('domain.users.repository');
514
515 /** @var string $paragraphStart */
516 $paragraphStart = $type === 'email' ? '<p>' : '';
517
518 /** @var string $paragraphEnd */
519 $paragraphEnd = $type === 'email' ? '</p>' : ($type === 'whatsapp' ? '; ' : PHP_EOL);
520
521 // If the data is for employee
522 if ($bookingKey === null) {
523 $customers = [];
524 $customerInformationData = [];
525
526 $hasApprovedOrPendingStatus = in_array(
527 BookingStatus::APPROVED,
528 array_column($appointment['bookings'], 'status'),
529 true
530 ) ||
531 in_array(
532 BookingStatus::PENDING,
533 array_column($appointment['bookings'], 'status'),
534 true
535 );
536
537 $bookedCustomerFullName = '';
538 $bookedCustomerEmail = '';
539 $bookedCustomerPhone = '';
540
541 foreach ((array)$appointment['bookings'] as $customerBooking) {
542 /** @var AbstractUser $customer */
543 $customer = $userRepository->getById($customerBooking['customerId']);
544
545 if (
546 (!$hasApprovedOrPendingStatus && $customerBooking['isChangedStatus']) ||
547 ($customerBooking['status'] !== BookingStatus::CANCELED && $customerBooking['status'] !== BookingStatus::REJECTED)
548 ) {
549 if ($customerBooking['info']) {
550 $customerInformationData[] = json_decode($customerBooking['info'], true);
551 } else {
552 $customerInformationData[] = [
553 'firstName' => $customer->getFirstName()->getValue(),
554 'lastName' => $customer->getLastName()->getValue(),
555 'phone' => $customer->getPhone() ? $customer->getPhone()->getValue() : '',
556 ];
557 }
558
559 $customers[] = $customer;
560 }
561
562 if ($customerBooking['isChangedStatus']) {
563 $bookedCustomerFullName = $customer->getFullName();
564 $bookedCustomerEmail = $customer->getEmail() ? $customer->getEmail()->getValue() : '';
565 $bookedCustomerPhone = $customer->getPhone() ? $customer->getPhone()->getValue() : '';
566 }
567 }
568
569 $phones = '';
570 foreach ($customerInformationData as $key => $info) {
571 if ($info['phone']) {
572 $phones .= $info['phone'] . ', ';
573 } else {
574 $phones .= $customers[$key]->getPhone() ? $customers[$key]->getPhone()->getValue() . ', ' : '';
575 }
576 }
577
578 $bookedCustomer =
579 $paragraphStart . BackendStrings::get('ph_customer_full_name') . ': ' . $bookedCustomerFullName . $paragraphEnd;
580
581 $bookedCustomer .=
582 $bookedCustomerPhone ?
583 $paragraphStart . BackendStrings::get('ph_customer_phone') . ': ' . $bookedCustomerPhone . $paragraphEnd :
584 '';
585 $bookedCustomer .=
586 $bookedCustomerEmail ?
587 $paragraphStart . BackendStrings::get('ph_customer_email') . ': ' . $bookedCustomerEmail . $paragraphEnd :
588 '';
589
590 return [
591 'booked_customer' => $paragraphStart ?
592 substr($bookedCustomer, 3, strlen($bookedCustomer) - 7) : $bookedCustomer,
593 'customer_email' => implode(
594 ', ',
595 array_map(
596 function ($customer) {
597 /** @var Customer $customer */
598 return $customer->getEmail()->getValue();
599 },
600 $customers
601 )
602 ),
603 'customer_first_name' => implode(
604 ', ',
605 array_map(
606 function ($info) {
607 return $info['firstName'];
608 },
609 $customerInformationData
610 )
611 ),
612 'customer_last_name' => implode(
613 ', ',
614 array_map(
615 function ($info) {
616 return $info['lastName'];
617 },
618 $customerInformationData
619 )
620 ),
621 'customer_full_name' => implode(
622 ', ',
623 array_map(
624 function ($info) {
625 return $info['firstName'] . ' ' . $info['lastName'];
626 },
627 $customerInformationData
628 )
629 ),
630 'customer_phone' => substr($phones, 0, -2),
631 'customer_phone_local' => str_replace('+', '', substr($phones, 0, -2)),
632 'customer_note' => implode(
633 ', ',
634 array_map(
635 function ($customer) {
636 /** @var Customer $customer */
637 return $customer->getNote() ? $customer->getNote()->getValue() : '';
638 },
639 $customers
640 )
641 )
642 ];
643 }
644
645 // If data is for customer
646 /** @var Customer $customer */
647 $customer = $customerEntity ?: (
648 !empty($appointment['bookings'][$bookingKey]['customer'])
649 ? UserFactory::create($appointment['bookings'][$bookingKey]['customer'])
650 : $userRepository->getById($appointment['bookings'][$bookingKey]['customerId'])
651 );
652
653 $info = !empty($appointment['bookings'][$bookingKey]['info']) ?
654 json_decode($appointment['bookings'][$bookingKey]['info']) : null;
655
656 if ($info && $info->phone) {
657 $phone = $info->phone;
658 } else {
659 $phone = $customer->getPhone() ? $customer->getPhone()->getValue() : '';
660 }
661
662 /** @var HelperService $helperService */
663 $helperService = $this->container->get('application.helper.service');
664
665 return [
666 'customer_email' => $customer->getEmail() ? $customer->getEmail()->getValue() : '',
667 'customer_first_name' => $info ? $info->firstName : $customer->getFirstName()->getValue(),
668 'customer_last_name' => $info ? $info->lastName : $customer->getLastName()->getValue(),
669 'customer_full_name' => $info ? $info->firstName . ' ' . $info->lastName : $customer->getFullName(),
670 'customer_phone' => $phone,
671 'customer_phone_country' => $customer->getCountryPhoneIso() ? $customer->getCountryPhoneIso()->getValue() : null,
672 'customer_phone_local' => !empty($phone) ? str_replace('+', '', $phone) : '',
673 'customer_note' => $customer->getNote() ? $customer->getNote()->getValue() : '',
674 'customer_panel_url' => $helperService->getCustomerCabinetUrl(
675 $customer->getEmail()->getValue(),
676 $type,
677 !empty($appointment['bookingStart']) ? explode(' ', $appointment['bookingStart'])[0] : null,
678 !empty($appointment['bookingEnd']) ? explode(' ', $appointment['bookingEnd'])[0] : null,
679 $info && property_exists($info, 'locale') ? $info->locale : ''
680 )
681 ];
682 }
683
684 /**
685 * @param array $appointment
686 * @param string $type
687 * @param null $bookingKey
688 *
689 * @return array
690 * @throws \Slim\Exception\ContainerValueNotFoundException
691 * @throws QueryExecutionException
692 * @throws \Exception
693 */
694 public function getCustomFieldsData($appointment, $type, $bookingKey = null)
695 {
696 /** @var SettingsService $settingsService */
697 $settingsService = $this->container->get('domain.settings.service');
698
699 $dateFormat = $settingsService->getSetting('wordpress', 'dateFormat');
700
701 $customFieldsData = [];
702
703 $bookingCustomFieldsKeys = [];
704
705 if ($bookingKey === null) {
706 $sendAllCustomFields =
707 $settingsService->getSetting('notifications', 'sendAllCF') ||
708 (array_key_exists('sendCF', $appointment) && $appointment['sendCF']);
709 foreach ($appointment['bookings'] as $booking) {
710 if (
711 (!$booking['isChangedStatus'] || (array_key_exists('isLastBooking', $booking) && !$booking['isLastBooking']))
712 && !(isset($appointment['isRescheduled']) ? $appointment['isRescheduled'] : false) && !$sendAllCustomFields
713 ) {
714 continue;
715 }
716
717 if (
718 sizeof($appointment['bookings']) > 1 &&
719 ($booking['status'] === BookingStatus::CANCELED || $booking['status'] === BookingStatus::REJECTED)
720 ) {
721 continue;
722 }
723
724 $bookingCustomFields = !empty($booking['customFields']) ? json_decode($booking['customFields'], true) : null;
725
726 if ($booking['customerId'] && (!isset($booking['customer']) || !isset($booking['customer']['customFields']))) {
727 /** @var UserRepository $userRepository */
728 $userRepository = $this->container->get('domain.users.repository');
729
730 $booking['customer'] = $userRepository->getById($booking['customerId'])->toArray();
731 }
732
733 $customerCustomFields = !empty($booking['customer']['customFields']) ? json_decode($booking['customer']['customFields'], true) : null;
734
735 if ($customerCustomFields) {
736 $bookingCustomFields = $bookingCustomFields ? ($bookingCustomFields + $customerCustomFields) : $customerCustomFields;
737 }
738
739
740 if ($bookingCustomFields) {
741 foreach ($bookingCustomFields as $bookingCustomFieldKey => $bookingCustomField) {
742 if (!empty($bookingCustomField['value']) && !empty($bookingCustomField['type'])) {
743 if ($bookingCustomField['type'] === 'datepicker') {
744 $bookingCustomField['value'] = $this->formatDatepickerValue($bookingCustomField['value'], $dateFormat);
745 }
746
747 if (
748 $bookingCustomField['type'] === 'file' &&
749 (!empty($appointment['provider']) || !empty($appointment['providers']))
750 ) {
751 /** @var HelperService $helperService */
752 $helperService = $this->container->get('application.helper.service');
753
754 /** @var array $jwtSettings */
755 $jwtSettings = $settingsService->getSetting('roles', 'urlAttachment');
756
757 $provider_email = !empty($appointment['provider']) ?
758 $appointment['provider']['email'] : $appointment['providers'][0]['email'];
759
760 $token = $helperService->getGeneratedJWT(
761 $provider_email,
762 $jwtSettings['headerJwtSecret'],
763 DateTimeService::getNowDateTimeObject()->getTimestamp() + $jwtSettings['tokenValidTime'],
764 LoginType::AMELIA_URL_TOKEN
765 );
766
767 $files = '';
768
769 if ($bookingCustomField['value']) {
770 $entityId = $booking['id'];
771
772 if ($customerCustomFields && array_key_exists($bookingCustomFieldKey, $customerCustomFields)) {
773 $entityId = $booking['customerId'];
774 }
775
776 foreach ($bookingCustomField['value'] as $index => $file) {
777 $files .= '<a href="'
778 . AMELIA_ACTION_URL . '/fields/' . $bookingCustomFieldKey . '/' . $entityId . '/' . $index . '&token=' . $token
779 . '">' . $file['name'] . '</a>';
780 }
781
782 $bookingCustomField['value'] = $files;
783 }
784 }
785
786 if (
787 $bookingCustomField['type'] === 'file' &&
788 (empty($appointment['provider']) && empty($appointment['providers']))
789 ) {
790 continue;
791 }
792
793 if (array_key_exists('custom_field_' . $bookingCustomFieldKey, $customFieldsData)) {
794 $value = $bookingCustomField['type'] === CustomFieldType::ADDRESS ? (
795 $type === 'email' ?
796 '<a href="https://maps.google.com/?q=' .
797 $bookingCustomField['value'] . '" target="_blank">' . $bookingCustomField['value'] .
798 '</a>' :
799 'https://maps.google.com/?q=' . str_replace(' ', '+', $bookingCustomField['value'])
800 ) : $bookingCustomField['value'];
801 $customFieldsData['custom_field_' . $bookingCustomFieldKey]
802 .= is_array($value)
803 ? '; ' . implode('; ', $value) :
804 '; ' . $value;
805 } else {
806 $value = $bookingCustomField['type'] === CustomFieldType::ADDRESS ? (
807 $type === 'email' ?
808 '<a href="https://maps.google.com/?q=' .
809 $bookingCustomField['value'] . '" target="_blank">' . $bookingCustomField['value'] .
810 '</a>' :
811 'https://maps.google.com/?q=' . str_replace(' ', '+', $bookingCustomField['value'])
812 ) : $bookingCustomField['value'];
813 $customFieldsData['custom_field_' . $bookingCustomFieldKey] =
814 is_array($value)
815 ? implode('; ', $value) : $value;
816 }
817
818 $bookingCustomFieldsKeys[(int)$bookingCustomFieldKey] = true;
819 }
820 }
821 }
822 }
823 } else {
824 if (!empty($appointment['bookings'][$bookingKey]['customFields'])) {
825 $bookingCustomFields = !is_array($appointment['bookings'][$bookingKey]['customFields']) ?
826 json_decode($appointment['bookings'][$bookingKey]['customFields'], true) :
827 $appointment['bookings'][$bookingKey]['customFields'];
828 } else {
829 $bookingCustomFields = [];
830 }
831
832 if (
833 !empty($appointment['bookings'][$bookingKey]['customerId']) &&
834 (!isset($appointment['bookings'][$bookingKey]['customer']) || !isset($appointment['bookings'][$bookingKey]['customer']['customFields']))
835 ) {
836 /** @var UserRepository $userRepository */
837 $userRepository = $this->container->get('domain.users.repository');
838
839 $appointment['bookings'][$bookingKey]['customer'] = $userRepository->getById($appointment['bookings'][$bookingKey]['customerId'])->toArray();
840 }
841
842 if (!empty($appointment['bookings'][$bookingKey]['customer']['customFields'])) {
843 $customerCustomFields = !is_array($appointment['bookings'][$bookingKey]['customer']['customFields']) ?
844 json_decode($appointment['bookings'][$bookingKey]['customer']['customFields'], true) :
845 $appointment['bookings'][$bookingKey]['customer']['customFields'];
846
847 $bookingCustomFields += $customerCustomFields;
848 }
849
850 if ($bookingCustomFields) {
851 foreach ((array)$bookingCustomFields as $bookingCustomFieldKey => $bookingCustomField) {
852 $bookingCustomFieldsKeys[(int)$bookingCustomFieldKey] = true;
853
854 if (
855 is_array($bookingCustomField) &&
856 array_key_exists('type', $bookingCustomField) &&
857 $bookingCustomField['type'] === 'file'
858 ) {
859 continue;
860 }
861
862 if (
863 is_array($bookingCustomField) &&
864 array_key_exists('type', $bookingCustomField) &&
865 $bookingCustomField['type'] === 'datepicker' &&
866 !empty($bookingCustomField['value'])
867 ) {
868 $bookingCustomField['value'] = $this->formatDatepickerValue($bookingCustomField['value'], $dateFormat);
869 }
870
871 $rawValue = '';
872 if (isset($bookingCustomField['value'])) {
873 $rawValue = is_array($bookingCustomField['value'])
874 ? implode('; ', $bookingCustomField['value']) : $bookingCustomField['value'];
875 $value = $bookingCustomField['type'] === CustomFieldType::ADDRESS ? (
876 $type === 'email' ?
877 '<a href="https://maps.google.com/?q=' .
878 $rawValue . '" target="_blank">' . $rawValue .
879 '</a>' :
880 'https://maps.google.com/?q=' . str_replace(' ', '+', $rawValue)
881 ) : $rawValue;
882 $customFieldsData['custom_field_' . $bookingCustomFieldKey] = $value;
883 } else {
884 $customFieldsData['custom_field_' . $bookingCustomFieldKey] = '';
885 }
886
887 $customFieldsData['invoice_custom_field_' . $bookingCustomFieldKey] = [
888 'label' => $bookingCustomField['label'],
889 'type' => $bookingCustomField['type'],
890 'value' => $rawValue ?: '/',
891 'components' => $bookingCustomField['components'] ?? null
892 ];
893 }
894 }
895 }
896
897 /** @var CustomFieldRepository $customFieldRepository */
898 $customFieldRepository = $this->container->get('domain.customField.repository');
899
900 /** @var Collection $customFields */
901 $customFields = $customFieldRepository->getAll();
902
903 /** @var CustomField $customField */
904 foreach ($customFields->getItems() as $customField) {
905 if (!array_key_exists($customField->getId()->getValue(), $bookingCustomFieldsKeys)) {
906 $customFieldsData['custom_field_' . $customField->getId()->getValue()] = '';
907 }
908
909 if (array_key_exists('invoice_custom_field_' . $customField->getId()->getValue(), $customFieldsData)) {
910 if (!$customField->getIncludeInInvoice() || !$customField->getIncludeInInvoice()->getValue()) {
911 unset($customFieldsData['invoice_custom_field_' . $customField->getId()->getValue()]);
912 } else {
913 $customFieldsData['invoice_custom_field_' . $customField->getId()->getValue()]['label'] =
914 $customField->getLabel()->getValue();
915 }
916 } elseif ($customField->getIncludeInInvoice() && $customField->getIncludeInInvoice()->getValue()) {
917 $customFieldsData['invoice_custom_field_' . $customField->getId()->getValue()] = [
918 'label' => $customField->getLabel()->getValue(),
919 'type' => $customField->getType()->getValue(),
920 'value' => '/',
921 'components' => null
922 ];
923 }
924
925 if ($customField->getType()->getValue() === 'content') {
926 switch ($appointment['type']) {
927 case (Entities::APPOINTMENT):
928 /** @var Service $service */
929 foreach ($customField->getServices()->getItems() as $service) {
930 if ($service->getId()->getValue() === $appointment['serviceId']) {
931 $customFieldsData['custom_field_' . $customField->getId()->getValue()] =
932 $customField->getLabel()->getValue();
933 break;
934 }
935 }
936
937 break;
938
939 case (Entities::EVENT):
940 /** @var Event $event */
941 foreach ($customField->getEvents()->getItems() as $event) {
942 if ($event->getId()->getValue() === $appointment['id']) {
943 $customFieldsData['custom_field_' . $customField->getId()->getValue()] =
944 $customField->getLabel()->getValue();
945 break;
946 }
947 }
948
949 break;
950 }
951 }
952 }
953
954 return $customFieldsData;
955 }
956
957 /**
958 * @param array $appointment
959 * @param string $type
960 * @param null $bookingKey
961 *
962 * @return array
963 * @throws ContainerException
964 * @throws QueryExecutionException
965 * @throws InvalidArgumentException
966 */
967 public function getCouponsData($appointment, $type, $bookingKey = null)
968 {
969 $couponsData = [];
970
971 /** @var string $break */
972 $break = $type === 'email' ? '<p><br></p>' : ($type === 'whatsapp' ? '; ' : PHP_EOL);
973
974 if ($bookingKey !== null) {
975 /** @var HelperService $helperService */
976 $helperService = $this->container->get('application.helper.service');
977
978 /** @var CouponRepository $couponRepository */
979 $couponRepository = $this->container->get('domain.coupon.repository');
980
981 /** @var AbstractCouponApplicationService $couponAS */
982 $couponAS = $this->container->get('application.coupon.service');
983
984 /** @var Collection $customerReservations */
985 $customerReservations = new Collection();
986
987 $type = $appointment['type'];
988 $customerId = $type !== Entities::PACKAGE ? $appointment['bookings'][$bookingKey]['customerId'] : $appointment['customer']['id'];
989 $couponsCriteria = [
990 'notExpired' => true,
991 'notificationInterval' => true,
992 ];
993
994 if (!$customerId) {
995 return $couponsData;
996 }
997
998 switch ($type) {
999 case Entities::APPOINTMENT:
1000 $couponsCriteria['entityIds'] = [$appointment['serviceId']];
1001
1002 $couponsCriteria['entityType'] = Entities::SERVICE;
1003
1004 break;
1005
1006 case Entities::EVENT:
1007 $couponsCriteria['entityIds'] = [$appointment['id']];
1008
1009 $couponsCriteria['entityType'] = Entities::EVENT;
1010
1011 break;
1012
1013 case Entities::PACKAGE:
1014 $couponsCriteria['entityIds'] = [$appointment['id']];
1015
1016 $couponsCriteria['entityType'] = Entities::PACKAGE;
1017
1018 break;
1019 }
1020
1021 /** @var Collection $entityCoupons */
1022 $entityCoupons = $couponAS->getAllByCriteria($couponsCriteria);
1023
1024 if (!$entityCoupons->length()) {
1025 return $couponsData;
1026 }
1027
1028 switch ($type) {
1029 case Entities::APPOINTMENT:
1030 /** @var AppointmentRepository $appointmentRepository */
1031 $appointmentRepository = $this->container->get('domain.booking.appointment.repository');
1032
1033 $customerReservations = $appointmentRepository->getPeriodAppointments(
1034 [
1035 'customerId' => $customerId,
1036 'skipServices' => true,
1037 'skipProviders' => true,
1038 'skipCustomers' => true,
1039 'skipPayments' => true,
1040 'skipExtras' => true,
1041 'skipCoupons' => true,
1042 'status' => BookingStatus::APPROVED,
1043 'bookingStatus' => BookingStatus::APPROVED,
1044 'services' => [
1045 $appointment['serviceId']
1046 ]
1047 ]
1048 );
1049
1050 break;
1051
1052 case Entities::EVENT:
1053 /** @var EventRepository $eventRepository */
1054 $eventRepository = $this->container->get('domain.booking.event.repository');
1055
1056 /** @var Collection $eventsBookings */
1057 $eventsBookings = $eventRepository->getBookingsByCriteria(
1058 [
1059 'ids' => [$appointment['id']],
1060 'customerId' => $customerId,
1061 'customerBookingStatus' => BookingStatus::APPROVED,
1062 'fetchBookings' => false,
1063 'fetchBookingsTickets' => false,
1064 'fetchBookingsUsers' => false,
1065 'fetchBookingsPayments' => false,
1066 ]
1067 );
1068
1069 /** @var Collection $customerReservations */
1070 $customerReservations = new Collection();
1071
1072 /** @var Collection $eventBookings */
1073 foreach ($eventsBookings->getItems() as $eventBookings) {
1074 /** @var Collection $booking */
1075 foreach ($eventBookings->getItems() as $bookingId => $booking) {
1076 $customerReservations->addItem($booking, $bookingId);
1077 }
1078 }
1079
1080 break;
1081
1082 case Entities::PACKAGE:
1083 /** @var PackageCustomerRepository $packageCustomerRepository */
1084 $packageCustomerRepository = $this->container->get('domain.bookable.packageCustomer.repository');
1085
1086 $customerReservations = $packageCustomerRepository->getFiltered(
1087 [
1088 'packages' => [$appointment['id']],
1089 'customerId' => $customerId,
1090 'bookingStatus' => BookingStatus::APPROVED,
1091 ]
1092 );
1093
1094 break;
1095 }
1096
1097 foreach (array_diff($couponRepository->getIds(), $entityCoupons->keys()) as $couponId) {
1098 $couponsData["coupon_{$couponId}"] = '';
1099 }
1100
1101 /** @var Coupon $coupon */
1102 foreach ($entityCoupons->getItems() as $coupon) {
1103 $sendCoupon = (
1104 $customerReservations->length() &&
1105 !$coupon->getNotificationRecurring()->getValue() &&
1106 $customerReservations->length() === $coupon->getNotificationInterval()->getValue()
1107 ) || (
1108 $customerReservations->length() &&
1109 $coupon->getNotificationRecurring()->getValue() &&
1110 $customerReservations->length() % $coupon->getNotificationInterval()->getValue() === 0
1111 );
1112
1113 try {
1114 if ($sendCoupon && $couponAS->inspectCoupon($coupon, $customerId, true, true)) {
1115 $couponsData["coupon_{$coupon->getId()->getValue()}"] =
1116 FrontendStrings::getCommonStrings()['coupon_send_text'] . ' ' .
1117 $coupon->getCode()->getValue() . ' ' . $break .
1118 ($coupon->getDeduction() && $coupon->getDeduction()->getValue() ?
1119 BackendStrings::get('deduction') . ' ' .
1120 $helperService->getFormattedPrice($coupon->getDeduction()->getValue()) . ' ' . $break
1121 : ''
1122 ) .
1123 ($coupon->getDiscount() && $coupon->getDiscount()->getValue() ?
1124 BackendStrings::get('discount_amount') . ' ' .
1125 $coupon->getDiscount()->getValue() . '% ' . $break
1126 : '') .
1127 ($coupon->getStartDate() && $coupon->getStartDate()->getValue() ?
1128 BackendStrings::get('start_date') . ': ' .
1129 date_i18n($coupon->getStartDate()->getValue()->format('Y-m-d')) . ' ' : '') .
1130 ($coupon->getExpirationDate() && $coupon->getExpirationDate()->getValue() ?
1131 BackendStrings::get('expiration_date') . ': ' .
1132 date_i18n($coupon->getExpirationDate()->getValue()->format('Y-m-d')) : '');
1133 } else {
1134 $couponsData["coupon_{$coupon->getId()->getValue()}"] = '';
1135 }
1136 } catch (CouponUnknownException $e) {
1137 $couponsData["coupon_{$coupon->getId()->getValue()}"] = '';
1138 } catch (CouponInvalidException $e) {
1139 $couponsData["coupon_{$coupon->getId()->getValue()}"] = '';
1140 } catch (CouponExpiredException $e) {
1141 $couponsData["coupon_{$coupon->getId()->getValue()}"] = '';
1142 }
1143 }
1144 }
1145
1146 return $couponsData;
1147 }
1148
1149 /**
1150 * @param array $entity
1151 *
1152 * @param string $subject
1153 * @param string $body
1154 * @param int $userId
1155 * @return array
1156 */
1157 public function reParseContentForProvider($entity, $subject, $body, $userId)
1158 {
1159 $employeeSubject = $subject;
1160
1161 $employeeBody = $body;
1162
1163 return [
1164 'body' => $employeeBody,
1165 'subject' => $employeeSubject,
1166 ];
1167 }
1168
1169 /**
1170 * @param array $appointment
1171 * @param int|null $bookingKey
1172 *
1173 * @return string|null
1174 */
1175 protected function getLocale($appointment, $bookingKey)
1176 {
1177 /** @var HelperService $helperService */
1178 $helperService = $this->container->get('application.helper.service');
1179
1180 if (!empty($appointment['bookings'][$bookingKey]['customer']['translations'])) {
1181 return $helperService->getLocaleFromTranslations(
1182 $appointment['bookings'][$bookingKey]['customer']['translations']
1183 );
1184 } elseif (!empty($appointment['bookings'][$bookingKey]['info'])) {
1185 return $helperService->getLocaleFromBooking(
1186 $appointment['bookings'][$bookingKey]['info']
1187 );
1188 }
1189
1190 return null;
1191 }
1192
1193 /**
1194 * @param array $reservation
1195 * @param int|null $bookingKey
1196 *
1197 * @return void
1198 *
1199 * @throws ContainerValueNotFoundException
1200 * @throws NotFoundException
1201 * @throws QueryExecutionException
1202 * @throws ContainerException
1203 * @throws Exception
1204 */
1205 protected function setData(&$reservation, $bookingKey = null)
1206 {
1207 $info = !empty($reservation['bookings'][$bookingKey]['info']) ?
1208 json_decode($reservation['bookings'][$bookingKey]['info'], true) : null;
1209
1210 if (
1211 $bookingKey !== null &&
1212 (
1213 !empty($reservation['bookings'][$bookingKey]['customerId']) ||
1214 !empty($reservation['bookings'][$bookingKey]['customer']['id'])
1215 ) &&
1216 (
1217 ($info && empty($info['locale'])) ||
1218 (
1219 !$info &&
1220 !empty($reservation['bookings'][$bookingKey]['customer']) &&
1221 empty($reservation['bookings'][$bookingKey]['customer']['translations'])
1222 )
1223 )
1224 ) {
1225 /** @var UserRepository $userRepository */
1226 $userRepository = $this->container->get('domain.users.repository');
1227
1228 /** @var AbstractUser $customer */
1229 $customer = $userRepository->getById(
1230 !empty($reservation['bookings'][$bookingKey]['customerId']) ?
1231 $reservation['bookings'][$bookingKey]['customerId'] :
1232 $reservation['bookings'][$bookingKey]['customer']['id']
1233 );
1234
1235 if ($customer->getTranslations()) {
1236 if ($info) {
1237 $translations = json_decode($customer->getTranslations()->getValue(), true);
1238
1239 if ($translations && !empty($translations['defaultLanguage'])) {
1240 $info['locale'] = $translations['defaultLanguage'];
1241
1242 $reservation['bookings'][$bookingKey]['info'] = json_encode($info);
1243 }
1244 } else {
1245 $reservation['bookings'][$bookingKey]['customer']['translations'] =
1246 $customer->getTranslations()->getValue();
1247 }
1248 }
1249 }
1250 }
1251
1252 /**
1253 * @param array $appointment
1254 *
1255 * @return int|null
1256 */
1257 protected function getBookingKeyForEmployee($appointment)
1258 {
1259 foreach ($appointment['bookings'] as $booking) {
1260 if ($booking['isLastBooking'] || $booking['isChangedStatus']) {
1261 return $booking['id'];
1262 }
1263 }
1264
1265 if (!empty($appointment['isRescheduled']) && $appointment['isRescheduled']) {
1266 return $appointment['bookings'][0]['id'];
1267 }
1268
1269 return null;
1270 }
1271
1272 /**
1273 * Normalize and format datepicker field value.
1274 * Accepts values like 'YYYY-MM-DD' or ISO 8601 'YYYY-MM-DDTHH:MM:SS(.u)Z'.
1275 * Returns formatted date string on success, or the original value if parsing fails.
1276 *
1277 * @param string $value
1278 * @param string $dateFormat WordPress date format
1279 * @return string
1280 */
1281 protected function formatDatepickerValue($value, $dateFormat)
1282 {
1283 if (empty($value)) {
1284 return $value;
1285 }
1286
1287 $savedDate = (string)$value;
1288 if (preg_match('/^(\d{4}-\d{2}-\d{2})/', $savedDate, $m)) {
1289 $savedDate = $m[1];
1290 } else {
1291 return $value;
1292 }
1293
1294 // Parse/format datepicker values in UTC to preserve the selected calendar date
1295 // regardless of customer or site timezone offsets.
1296 $date = DateTime::createFromFormat('!Y-m-d', $savedDate, new \DateTimeZone('UTC'));
1297 if ($date instanceof DateTime) {
1298 return wp_date($dateFormat, $date->getTimestamp(), new \DateTimeZone('UTC'));
1299 }
1300
1301 return $value;
1302 }
1303 }
1304