PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.2
Booking for Appointments and Events Calendar – Amelia v2.2
2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / Application / Services / Placeholder / PlaceholderService.php
ameliabooking / src / Application / Services / Placeholder Last commit date
AppointmentPlaceholderService.php 4 months ago AppointmentsPlaceholderService.php 6 months ago BasicPackagePlaceholderService.php 8 months ago EventPlaceholderService.php 4 months ago PlaceholderService.php 4 months ago PlaceholderServiceInterface.php 8 months ago
PlaceholderService.php
1297 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_country' => $customer->getCountryPhoneIso() ? $customer->getCountryPhoneIso()->getValue() : null,
667 'customer_phone_local' => !empty($phone) ? str_replace('+', '', $phone) : '',
668 'customer_note' => $customer->getNote() ? $customer->getNote()->getValue() : '',
669 'customer_panel_url' => $helperService->getCustomerCabinetUrl(
670 $customer->getEmail()->getValue(),
671 $type,
672 !empty($appointment['bookingStart']) ? explode(' ', $appointment['bookingStart'])[0] : null,
673 !empty($appointment['bookingEnd']) ? explode(' ', $appointment['bookingEnd'])[0] : null,
674 $info && property_exists($info, 'locale') ? $info->locale : ''
675 )
676 ];
677 }
678
679 /**
680 * @param array $appointment
681 * @param string $type
682 * @param null $bookingKey
683 *
684 * @return array
685 * @throws \Slim\Exception\ContainerValueNotFoundException
686 * @throws QueryExecutionException
687 * @throws \Exception
688 */
689 public function getCustomFieldsData($appointment, $type, $bookingKey = null)
690 {
691 /** @var SettingsService $settingsService */
692 $settingsService = $this->container->get('domain.settings.service');
693
694 $dateFormat = $settingsService->getSetting('wordpress', 'dateFormat');
695
696 $customFieldsData = [];
697
698 $bookingCustomFieldsKeys = [];
699
700 if ($bookingKey === null) {
701 $sendAllCustomFields =
702 $settingsService->getSetting('notifications', 'sendAllCF') ||
703 (array_key_exists('sendCF', $appointment) && $appointment['sendCF']);
704 foreach ($appointment['bookings'] as $booking) {
705 if (
706 (!$booking['isChangedStatus'] || (array_key_exists('isLastBooking', $booking) && !$booking['isLastBooking']))
707 && !(isset($appointment['isRescheduled']) ? $appointment['isRescheduled'] : false) && !$sendAllCustomFields
708 ) {
709 continue;
710 }
711
712 if (
713 sizeof($appointment['bookings']) > 1 &&
714 ($booking['status'] === BookingStatus::CANCELED || $booking['status'] === BookingStatus::REJECTED)
715 ) {
716 continue;
717 }
718
719 $bookingCustomFields = !empty($booking['customFields']) ? json_decode($booking['customFields'], true) : null;
720
721 if ($booking['customerId'] && !isset($booking['customer'])) {
722 /** @var UserRepository $userRepository */
723 $userRepository = $this->container->get('domain.users.repository');
724
725 $booking['customer'] = $userRepository->getById($booking['customerId'])->toArray();
726 }
727
728 $customerCustomFields = !empty($booking['customer']['customFields']) ? json_decode($booking['customer']['customFields'], true) : null;
729
730 if ($customerCustomFields) {
731 $bookingCustomFields = $bookingCustomFields ? ($bookingCustomFields + $customerCustomFields) : $customerCustomFields;
732 }
733
734
735 if ($bookingCustomFields) {
736 foreach ($bookingCustomFields as $bookingCustomFieldKey => $bookingCustomField) {
737 if (!empty($bookingCustomField['value']) && !empty($bookingCustomField['type'])) {
738 if ($bookingCustomField['type'] === 'datepicker') {
739 $bookingCustomField['value'] = $this->formatDatepickerValue($bookingCustomField['value'], $dateFormat);
740 }
741
742 if (
743 $bookingCustomField['type'] === 'file' &&
744 (!empty($appointment['provider']) || !empty($appointment['providers']))
745 ) {
746 /** @var HelperService $helperService */
747 $helperService = $this->container->get('application.helper.service');
748
749 /** @var array $jwtSettings */
750 $jwtSettings = $settingsService->getSetting('roles', 'urlAttachment');
751
752 $provider_email = !empty($appointment['provider']) ?
753 $appointment['provider']['email'] : $appointment['providers'][0]['email'];
754
755 $token = $helperService->getGeneratedJWT(
756 $provider_email,
757 $jwtSettings['headerJwtSecret'],
758 DateTimeService::getNowDateTimeObject()->getTimestamp() + $jwtSettings['tokenValidTime'],
759 LoginType::AMELIA_URL_TOKEN
760 );
761
762 $files = '';
763
764 if ($bookingCustomField['value']) {
765 $entityId = $booking['id'];
766
767 if ($customerCustomFields && array_key_exists($bookingCustomFieldKey, $customerCustomFields)) {
768 $entityId = $booking['customerId'];
769 }
770
771 foreach ($bookingCustomField['value'] as $index => $file) {
772 $files .= '<a href="'
773 . AMELIA_ACTION_URL . '/fields/' . $bookingCustomFieldKey . '/' . $entityId . '/' . $index . '&token=' . $token
774 . '">' . $file['name'] . '</a>';
775 }
776
777 $bookingCustomField['value'] = $files;
778 }
779 }
780
781 if (
782 $bookingCustomField['type'] === 'file' &&
783 (empty($appointment['provider']) && empty($appointment['providers']))
784 ) {
785 continue;
786 }
787
788 if (array_key_exists('custom_field_' . $bookingCustomFieldKey, $customFieldsData)) {
789 $value = $bookingCustomField['type'] === CustomFieldType::ADDRESS ? (
790 $type === 'email' ?
791 '<a href="https://maps.google.com/?q=' .
792 $bookingCustomField['value'] . '" target="_blank">' . $bookingCustomField['value'] .
793 '</a>' :
794 'https://maps.google.com/?q=' . str_replace(' ', '+', $bookingCustomField['value'])
795 ) : $bookingCustomField['value'];
796 $customFieldsData['custom_field_' . $bookingCustomFieldKey]
797 .= is_array($value)
798 ? '; ' . implode('; ', $value) :
799 '; ' . $value;
800 } else {
801 $value = $bookingCustomField['type'] === CustomFieldType::ADDRESS ? (
802 $type === 'email' ?
803 '<a href="https://maps.google.com/?q=' .
804 $bookingCustomField['value'] . '" target="_blank">' . $bookingCustomField['value'] .
805 '</a>' :
806 'https://maps.google.com/?q=' . str_replace(' ', '+', $bookingCustomField['value'])
807 ) : $bookingCustomField['value'];
808 $customFieldsData['custom_field_' . $bookingCustomFieldKey] =
809 is_array($value)
810 ? implode('; ', $value) : $value;
811 }
812
813 $bookingCustomFieldsKeys[(int)$bookingCustomFieldKey] = true;
814 }
815 }
816 }
817 }
818 } else {
819 if (!empty($appointment['bookings'][$bookingKey]['customFields'])) {
820 $bookingCustomFields = !is_array($appointment['bookings'][$bookingKey]['customFields']) ?
821 json_decode($appointment['bookings'][$bookingKey]['customFields'], true) :
822 $appointment['bookings'][$bookingKey]['customFields'];
823 } else {
824 $bookingCustomFields = [];
825 }
826
827 if (
828 !empty($appointment['bookings'][$bookingKey]['customerId']) &&
829 !isset($appointment['bookings'][$bookingKey]['customer'])
830 ) {
831 /** @var UserRepository $userRepository */
832 $userRepository = $this->container->get('domain.users.repository');
833
834 $appointment['bookings'][$bookingKey]['customer'] = $userRepository->getById($appointment['bookings'][$bookingKey]['customerId'])->toArray();
835 }
836
837 if (!empty($appointment['bookings'][$bookingKey]['customer']['customFields'])) {
838 $customerCustomFields = !is_array($appointment['bookings'][$bookingKey]['customer']['customFields']) ?
839 json_decode($appointment['bookings'][$bookingKey]['customer']['customFields'], true) :
840 $appointment['bookings'][$bookingKey]['customer']['customFields'];
841
842 $bookingCustomFields += $customerCustomFields;
843 }
844
845 if ($bookingCustomFields) {
846 foreach ((array)$bookingCustomFields as $bookingCustomFieldKey => $bookingCustomField) {
847 $bookingCustomFieldsKeys[(int)$bookingCustomFieldKey] = true;
848
849 if (
850 is_array($bookingCustomField) &&
851 array_key_exists('type', $bookingCustomField) &&
852 $bookingCustomField['type'] === 'file'
853 ) {
854 continue;
855 }
856
857 if (
858 is_array($bookingCustomField) &&
859 array_key_exists('type', $bookingCustomField) &&
860 $bookingCustomField['type'] === 'datepicker' &&
861 !empty($bookingCustomField['value'])
862 ) {
863 $bookingCustomField['value'] = $this->formatDatepickerValue($bookingCustomField['value'], $dateFormat);
864 }
865
866 $rawValue = '';
867 if (isset($bookingCustomField['value'])) {
868 $rawValue = is_array($bookingCustomField['value'])
869 ? implode('; ', $bookingCustomField['value']) : $bookingCustomField['value'];
870 $value = $bookingCustomField['type'] === CustomFieldType::ADDRESS ? (
871 $type === 'email' ?
872 '<a href="https://maps.google.com/?q=' .
873 $rawValue . '" target="_blank">' . $rawValue .
874 '</a>' :
875 'https://maps.google.com/?q=' . str_replace(' ', '+', $rawValue)
876 ) : $rawValue;
877 $customFieldsData['custom_field_' . $bookingCustomFieldKey] = $value;
878 } else {
879 $customFieldsData['custom_field_' . $bookingCustomFieldKey] = '';
880 }
881
882 $customFieldsData['invoice_custom_field_' . $bookingCustomFieldKey] = [
883 'label' => $bookingCustomField['label'],
884 'type' => $bookingCustomField['type'],
885 'value' => $rawValue ?: '/',
886 'components' => $bookingCustomField['components'] ?? null
887 ];
888 }
889 }
890 }
891
892 /** @var CustomFieldRepository $customFieldRepository */
893 $customFieldRepository = $this->container->get('domain.customField.repository');
894
895 /** @var Collection $customFields */
896 $customFields = $customFieldRepository->getAll();
897
898 /** @var CustomField $customField */
899 foreach ($customFields->getItems() as $customField) {
900 if (!array_key_exists($customField->getId()->getValue(), $bookingCustomFieldsKeys)) {
901 $customFieldsData['custom_field_' . $customField->getId()->getValue()] = '';
902 }
903
904 if (array_key_exists('invoice_custom_field_' . $customField->getId()->getValue(), $customFieldsData)) {
905 if (!$customField->getIncludeInInvoice() || !$customField->getIncludeInInvoice()->getValue()) {
906 unset($customFieldsData['invoice_custom_field_' . $customField->getId()->getValue()]);
907 } else {
908 $customFieldsData['invoice_custom_field_' . $customField->getId()->getValue()]['label'] =
909 $customField->getLabel()->getValue();
910 }
911 } elseif ($customField->getIncludeInInvoice() && $customField->getIncludeInInvoice()->getValue()) {
912 $customFieldsData['invoice_custom_field_' . $customField->getId()->getValue()] = [
913 'label' => $customField->getLabel()->getValue(),
914 'type' => $customField->getType()->getValue(),
915 'value' => '/',
916 'components' => null
917 ];
918 }
919
920 if ($customField->getType()->getValue() === 'content') {
921 switch ($appointment['type']) {
922 case (Entities::APPOINTMENT):
923 /** @var Service $service */
924 foreach ($customField->getServices()->getItems() as $service) {
925 if ($service->getId()->getValue() === $appointment['serviceId']) {
926 $customFieldsData['custom_field_' . $customField->getId()->getValue()] =
927 $customField->getLabel()->getValue();
928 break;
929 }
930 }
931
932 break;
933
934 case (Entities::EVENT):
935 /** @var Event $event */
936 foreach ($customField->getEvents()->getItems() as $event) {
937 if ($event->getId()->getValue() === $appointment['id']) {
938 $customFieldsData['custom_field_' . $customField->getId()->getValue()] =
939 $customField->getLabel()->getValue();
940 break;
941 }
942 }
943
944 break;
945 }
946 }
947 }
948
949 return $customFieldsData;
950 }
951
952 /**
953 * @param array $appointment
954 * @param string $type
955 * @param null $bookingKey
956 *
957 * @return array
958 * @throws ContainerException
959 * @throws QueryExecutionException
960 * @throws InvalidArgumentException
961 */
962 public function getCouponsData($appointment, $type, $bookingKey = null)
963 {
964 $couponsData = [];
965
966 /** @var string $break */
967 $break = $type === 'email' ? '<p><br></p>' : ($type === 'whatsapp' ? '; ' : PHP_EOL);
968
969 if ($bookingKey !== null) {
970 /** @var HelperService $helperService */
971 $helperService = $this->container->get('application.helper.service');
972
973 /** @var CouponRepository $couponRepository */
974 $couponRepository = $this->container->get('domain.coupon.repository');
975
976 /** @var AbstractCouponApplicationService $couponAS */
977 $couponAS = $this->container->get('application.coupon.service');
978
979 /** @var Collection $customerReservations */
980 $customerReservations = new Collection();
981
982 $type = $appointment['type'];
983 $customerId = $type !== Entities::PACKAGE ? $appointment['bookings'][$bookingKey]['customerId'] : $appointment['customer']['id'];
984 $couponsCriteria = [
985 'notExpired' => true,
986 'notificationInterval' => true,
987 ];
988
989 if (!$customerId) {
990 return $couponsData;
991 }
992
993 switch ($type) {
994 case Entities::APPOINTMENT:
995 $couponsCriteria['entityIds'] = [$appointment['serviceId']];
996
997 $couponsCriteria['entityType'] = Entities::SERVICE;
998
999 break;
1000
1001 case Entities::EVENT:
1002 $couponsCriteria['entityIds'] = [$appointment['id']];
1003
1004 $couponsCriteria['entityType'] = Entities::EVENT;
1005
1006 break;
1007
1008 case Entities::PACKAGE:
1009 $couponsCriteria['entityIds'] = [$appointment['id']];
1010
1011 $couponsCriteria['entityType'] = Entities::PACKAGE;
1012
1013 break;
1014 }
1015
1016 /** @var Collection $entityCoupons */
1017 $entityCoupons = $couponAS->getAllByCriteria($couponsCriteria);
1018
1019 if (!$entityCoupons->length()) {
1020 return $couponsData;
1021 }
1022
1023 switch ($type) {
1024 case Entities::APPOINTMENT:
1025 /** @var AppointmentRepository $appointmentRepository */
1026 $appointmentRepository = $this->container->get('domain.booking.appointment.repository');
1027
1028 $customerReservations = $appointmentRepository->getPeriodAppointments(
1029 [
1030 'customerId' => $customerId,
1031 'skipServices' => true,
1032 'skipProviders' => true,
1033 'skipCustomers' => true,
1034 'skipPayments' => true,
1035 'skipExtras' => true,
1036 'skipCoupons' => true,
1037 'status' => BookingStatus::APPROVED,
1038 'bookingStatus' => BookingStatus::APPROVED,
1039 'services' => [
1040 $appointment['serviceId']
1041 ]
1042 ]
1043 );
1044
1045 break;
1046
1047 case Entities::EVENT:
1048 /** @var EventRepository $eventRepository */
1049 $eventRepository = $this->container->get('domain.booking.event.repository');
1050
1051 /** @var Collection $eventsBookings */
1052 $eventsBookings = $eventRepository->getBookingsByCriteria(
1053 [
1054 'ids' => [$appointment['id']],
1055 'customerId' => $customerId,
1056 'customerBookingStatus' => BookingStatus::APPROVED,
1057 'fetchBookings' => false,
1058 'fetchBookingsTickets' => false,
1059 'fetchBookingsUsers' => false,
1060 'fetchBookingsPayments' => false,
1061 ]
1062 );
1063
1064 /** @var Collection $customerReservations */
1065 $customerReservations = new Collection();
1066
1067 /** @var Collection $eventBookings */
1068 foreach ($eventsBookings->getItems() as $eventBookings) {
1069 /** @var Collection $booking */
1070 foreach ($eventBookings->getItems() as $bookingId => $booking) {
1071 $customerReservations->addItem($booking, $bookingId);
1072 }
1073 }
1074
1075 break;
1076
1077 case Entities::PACKAGE:
1078 /** @var PackageCustomerRepository $packageCustomerRepository */
1079 $packageCustomerRepository = $this->container->get('domain.bookable.packageCustomer.repository');
1080
1081 $customerReservations = $packageCustomerRepository->getFiltered(
1082 [
1083 'packages' => [$appointment['id']],
1084 'customerId' => $customerId,
1085 'bookingStatus' => BookingStatus::APPROVED,
1086 ]
1087 );
1088
1089 break;
1090 }
1091
1092 foreach (array_diff($couponRepository->getIds(), $entityCoupons->keys()) as $couponId) {
1093 $couponsData["coupon_{$couponId}"] = '';
1094 }
1095
1096 /** @var Coupon $coupon */
1097 foreach ($entityCoupons->getItems() as $coupon) {
1098 $sendCoupon = (
1099 $customerReservations->length() &&
1100 !$coupon->getNotificationRecurring()->getValue() &&
1101 $customerReservations->length() === $coupon->getNotificationInterval()->getValue()
1102 ) || (
1103 $customerReservations->length() &&
1104 $coupon->getNotificationRecurring()->getValue() &&
1105 $customerReservations->length() % $coupon->getNotificationInterval()->getValue() === 0
1106 );
1107
1108 try {
1109 if ($sendCoupon && $couponAS->inspectCoupon($coupon, $customerId, true, true)) {
1110 $couponsData["coupon_{$coupon->getId()->getValue()}"] =
1111 FrontendStrings::getCommonStrings()['coupon_send_text'] . ' ' .
1112 $coupon->getCode()->getValue() . ' ' . $break .
1113 ($coupon->getDeduction() && $coupon->getDeduction()->getValue() ?
1114 BackendStrings::get('deduction') . ' ' .
1115 $helperService->getFormattedPrice($coupon->getDeduction()->getValue()) . ' ' . $break
1116 : ''
1117 ) .
1118 ($coupon->getDiscount() && $coupon->getDiscount()->getValue() ?
1119 BackendStrings::get('discount_amount') . ' ' .
1120 $coupon->getDiscount()->getValue() . '% ' . $break
1121 : '') .
1122 ($coupon->getStartDate() && $coupon->getStartDate()->getValue() ?
1123 BackendStrings::get('start_date') . ': ' .
1124 date_i18n($coupon->getStartDate()->getValue()->format('Y-m-d')) . ' ' : '') .
1125 ($coupon->getExpirationDate() && $coupon->getExpirationDate()->getValue() ?
1126 BackendStrings::get('expiration_date') . ': ' .
1127 date_i18n($coupon->getExpirationDate()->getValue()->format('Y-m-d')) : '');
1128 } else {
1129 $couponsData["coupon_{$coupon->getId()->getValue()}"] = '';
1130 }
1131 } catch (CouponUnknownException $e) {
1132 $couponsData["coupon_{$coupon->getId()->getValue()}"] = '';
1133 } catch (CouponInvalidException $e) {
1134 $couponsData["coupon_{$coupon->getId()->getValue()}"] = '';
1135 } catch (CouponExpiredException $e) {
1136 $couponsData["coupon_{$coupon->getId()->getValue()}"] = '';
1137 }
1138 }
1139 }
1140
1141 return $couponsData;
1142 }
1143
1144 /**
1145 * @param array $entity
1146 *
1147 * @param string $subject
1148 * @param string $body
1149 * @param int $userId
1150 * @return array
1151 */
1152 public function reParseContentForProvider($entity, $subject, $body, $userId)
1153 {
1154 $employeeSubject = $subject;
1155
1156 $employeeBody = $body;
1157
1158 return [
1159 'body' => $employeeBody,
1160 'subject' => $employeeSubject,
1161 ];
1162 }
1163
1164 /**
1165 * @param array $appointment
1166 * @param int|null $bookingKey
1167 *
1168 * @return string|null
1169 */
1170 protected function getLocale($appointment, $bookingKey)
1171 {
1172 /** @var HelperService $helperService */
1173 $helperService = $this->container->get('application.helper.service');
1174
1175 if (!empty($appointment['bookings'][$bookingKey]['customer']['translations'])) {
1176 return $helperService->getLocaleFromTranslations(
1177 $appointment['bookings'][$bookingKey]['customer']['translations']
1178 );
1179 } elseif (!empty($appointment['bookings'][$bookingKey]['info'])) {
1180 return $helperService->getLocaleFromBooking(
1181 $appointment['bookings'][$bookingKey]['info']
1182 );
1183 }
1184
1185 return null;
1186 }
1187
1188 /**
1189 * @param array $reservation
1190 * @param int|null $bookingKey
1191 *
1192 * @return void
1193 *
1194 * @throws ContainerValueNotFoundException
1195 * @throws NotFoundException
1196 * @throws QueryExecutionException
1197 * @throws ContainerException
1198 * @throws Exception
1199 */
1200 protected function setData(&$reservation, $bookingKey = null)
1201 {
1202 $info = !empty($reservation['bookings'][$bookingKey]['info']) ?
1203 json_decode($reservation['bookings'][$bookingKey]['info'], true) : null;
1204
1205 if (
1206 $bookingKey !== null &&
1207 (
1208 !empty($reservation['bookings'][$bookingKey]['customerId']) ||
1209 !empty($reservation['bookings'][$bookingKey]['customer']['id'])
1210 ) &&
1211 (
1212 ($info && empty($info['locale'])) ||
1213 (
1214 !$info &&
1215 !empty($reservation['bookings'][$bookingKey]['customer']) &&
1216 empty($reservation['bookings'][$bookingKey]['customer']['translations'])
1217 )
1218 )
1219 ) {
1220 /** @var UserRepository $userRepository */
1221 $userRepository = $this->container->get('domain.users.repository');
1222
1223 /** @var AbstractUser $customer */
1224 $customer = $userRepository->getById(
1225 !empty($reservation['bookings'][$bookingKey]['customerId']) ?
1226 $reservation['bookings'][$bookingKey]['customerId'] :
1227 $reservation['bookings'][$bookingKey]['customer']['id']
1228 );
1229
1230 if ($customer->getTranslations()) {
1231 if ($info) {
1232 $translations = json_decode($customer->getTranslations()->getValue(), true);
1233
1234 if ($translations && !empty($translations['defaultLanguage'])) {
1235 $info['locale'] = $translations['defaultLanguage'];
1236
1237 $reservation['bookings'][$bookingKey]['info'] = json_encode($info);
1238 }
1239 } else {
1240 $reservation['bookings'][$bookingKey]['customer']['translations'] =
1241 $customer->getTranslations()->getValue();
1242 }
1243 }
1244 }
1245 }
1246
1247 /**
1248 * @param array $appointment
1249 *
1250 * @return int|null
1251 */
1252 protected function getBookingKeyForEmployee($appointment)
1253 {
1254 foreach ($appointment['bookings'] as $booking) {
1255 if ($booking['isLastBooking'] || $booking['isChangedStatus']) {
1256 return $booking['id'];
1257 }
1258 }
1259
1260 if (!empty($appointment['isRescheduled']) && $appointment['isRescheduled']) {
1261 return $appointment['bookings'][0]['id'];
1262 }
1263
1264 return null;
1265 }
1266
1267 /**
1268 * Normalize and format datepicker field value.
1269 * Accepts values like 'YYYY-MM-DD' or ISO 8601 'YYYY-MM-DDTHH:MM:SS(.u)Z'.
1270 * Returns formatted date string on success, or the original value if parsing fails.
1271 *
1272 * @param string $value
1273 * @param string $dateFormat WordPress date format
1274 * @return string
1275 */
1276 protected function formatDatepickerValue($value, $dateFormat)
1277 {
1278 if (empty($value)) {
1279 return $value;
1280 }
1281
1282 $savedDate = (string)$value;
1283 if (preg_match('/^(\d{4}-\d{2}-\d{2})/', $savedDate, $m)) {
1284 $savedDate = $m[1];
1285 } else {
1286 return $value;
1287 }
1288
1289 $date = DateTime::createFromFormat('Y-m-d', $savedDate);
1290 if ($date instanceof DateTime) {
1291 return date_i18n($dateFormat, $date->getTimestamp());
1292 }
1293
1294 return $value;
1295 }
1296 }
1297