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