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