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