PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.8
Booking for Appointments and Events Calendar – Amelia v2.4.8
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 / v3 / src / assets / js / public / booking.js
ameliabooking / v3 / src / assets / js / public Last commit date
actions.js 1 month ago booking.js 1 day ago cabinet.js 1 month ago cart.js 1 day ago catalog.js 1 day ago coupon.js 1 month ago customFields.js 1 month ago customFontFace.js 1 month ago eventFormCssVars.js 1 month ago events.js 1 month ago facebookPixel.js 1 month ago ivy.js 1 month ago package.js 1 day ago panel.js 1 month ago public.js 1 month ago renderActions.js 1 month ago restore.js 1 day ago slots.js 1 day ago stripePaymentIntent.js 1 day ago translation.js 1 month ago user.js 1 month ago
booking.js
1166 lines
1 import moment from 'moment'
2 import { useAppointmentsAmountInfo } from '../common/appointments'
3 import { useCart } from './cart'
4 import httpClient from '../../../plugins/axios.js'
5 import { settings, locale } from '../../../plugins/settings.js'
6 import { useUtcValue, useLocalValue, useUtcValueOffset, useStringFromDate } from '../common/date.js'
7 import { reactive, ref, computed } from 'vue'
8 import { useCartItem } from './cart'
9 import { useServices } from '../common/appointments'
10 import { usePackageAmount } from './package'
11 import useAction from './actions'
12 import { useUrlQueryParams } from '../common/helper'
13 import { useAmount, useEntityTax } from '../common/pricing'
14
15 const globalLabels = reactive(window.wpAmeliaLabels)
16 let errorMessage = ref()
17
18 function normalizeDatepickerValue(value) {
19 if (!value) {
20 return null
21 }
22
23 if (typeof value === 'string') {
24 const matches = value.match(/^(\d{4}-\d{2}-\d{2})/)
25 if (matches) {
26 return matches[1]
27 }
28 }
29
30 const dateObject = value instanceof Date ? value : new Date(value)
31
32 return useStringFromDate(dateObject)
33 }
34
35 function usePaymentError(store, callback) {
36 store.commit('booking/setLoading', false)
37
38 callback()
39 }
40
41 function buildFormData(formData, data, parentKey) {
42 if (data && typeof data === 'object' && !(data instanceof Date) && !(data instanceof File)) {
43 Object.keys(data).forEach((key) => {
44 buildFormData(formData, data[key], parentKey ? `${parentKey}[${key}]` : key)
45 })
46 } else {
47 formData.append(parentKey, data !== null ? data : '')
48 }
49 }
50
51 function useBookingData(
52 store,
53 formData,
54 mandatoryJson = false,
55 paymentData = {},
56 recaptcha = null,
57 ) {
58 let bookableType = store.getters['bookableType/getType']
59 ? store.getters['bookableType/getType']
60 : store.getters['booking/getBookableType']
61
62 let customFields = {}
63
64 let customerCustomFields = {}
65
66 let bookableTypeCustomFields =
67 bookableType !== 'event'
68 ? store.getters['entities/getCustomFields']
69 : store.getters['customFields/getFilteredCustomFieldsArray']
70
71 let allCustomFields = bookableTypeCustomFields.reduce((acc, field) => {
72 acc[field.id] = field
73 return acc
74 }, {})
75
76 let availableCustomFields =
77 bookableType !== 'event'
78 ? store.getters['booking/getAvailableCustomFields']
79 : store.getters['customFields/getCustomFields']
80
81 let attachments = bookableType !== 'event' ? store.getters['booking/getAttachments'] : {}
82
83 if (bookableType !== 'event') {
84 for (let id in availableCustomFields) {
85 if (
86 availableCustomFields[id].type === 'file' &&
87 Object.keys(attachments).length &&
88 id in attachments &&
89 attachments[id].length
90 ) {
91 let uploadCustomField = {
92 label: availableCustomFields[id].label,
93 value: [],
94 type: 'file',
95 }
96
97 for (let i = 0; i < attachments[id].length; i++) {
98 uploadCustomField.value.push({
99 name: attachments[id][i].name,
100 })
101 }
102
103 customFields[id] = uploadCustomField
104 } else if (availableCustomFields[id].type !== 'content') {
105 customFields[id] = availableCustomFields[id]
106 }
107
108 if (availableCustomFields[id].type === 'datepicker') {
109 customFields[id].value = normalizeDatepickerValue(availableCustomFields[id].value)
110 }
111 }
112 } else {
113 for (let key in availableCustomFields) {
114 if (availableCustomFields[key].type !== 'content') {
115 customFields[availableCustomFields[key].id] = {
116 label: availableCustomFields[key].label,
117 type: availableCustomFields[key].type,
118 value: availableCustomFields[key].value,
119 components: availableCustomFields[key].components,
120 }
121 }
122
123 if (availableCustomFields[key].type === 'file') {
124 customFields[availableCustomFields[key].id].value = []
125 if (availableCustomFields[key].value.length) {
126 attachments[availableCustomFields[key].id] = availableCustomFields[key].value
127 }
128 for (let i = 0; i < availableCustomFields[key].value.length; i++) {
129 customFields[availableCustomFields[key].id].value.push({
130 name: availableCustomFields[key].value[i].name,
131 })
132 }
133 }
134
135 if (availableCustomFields[key].type === 'datepicker') {
136 customFields[availableCustomFields[key].id].value = normalizeDatepickerValue(
137 availableCustomFields[key].value,
138 )
139 }
140 }
141 }
142
143 for (let key in customFields) {
144 if (allCustomFields[key].saveType === 'customer') {
145 customerCustomFields[key] = customFields[key]
146 delete customFields[key]
147 }
148 }
149
150 let deposit =
151 bookableType !== 'event'
152 ? !store.getters['booking/getPaymentDeposit']
153 : !store.getters['payment/getPaymentDeposit']
154
155 let componentProps = formData
156 ? {
157 state: bookableType !== 'event' ? store.state.booking : useEventsStoreData(store),
158 form: formData,
159 }
160 : null
161
162 if (formData && 'form' in componentProps && 'sidebar' in componentProps.form) {
163 componentProps.form.sidebar.forEach((item) => {
164 item.data.forEach((data) => {
165 if (data && 'value' in data) {
166 data.value = data.value.replace(/"/g, "'")
167 }
168 })
169 })
170 }
171
172 if (
173 formData &&
174 'state' in componentProps &&
175 'customFields' in componentProps.state &&
176 'customFields' in componentProps.state.customFields
177 ) {
178 Object.keys(componentProps.state.customFields.customFields).forEach((key) => {
179 if ('options' in componentProps.state.customFields.customFields[key]) {
180 delete componentProps.state.customFields.customFields[key].options
181 }
182 })
183 }
184
185 if (
186 formData &&
187 'state' in componentProps &&
188 'tickets' in componentProps.state &&
189 'tickets' in componentProps.state.tickets
190 ) {
191 componentProps.state.tickets.tickets.forEach((ticket, index) => {
192 if ('translations' in ticket) {
193 delete componentProps.state.tickets.tickets[index].translations
194 }
195 })
196 }
197
198 const gateway =
199 bookableType !== 'event'
200 ? store.getters['booking/getPaymentGateway']
201 : store.getters['payment/getPaymentGateway']
202
203 let jsonData = {
204 type: bookableType,
205 bookings: [
206 {
207 customFields: customFields,
208 deposit: deposit,
209 locale: locale,
210 utcOffset: null,
211 customerId:
212 bookableType !== 'event'
213 ? store.getters['booking/getCustomerId']
214 : store.getters['customerInfo/getCustomerId'],
215 customer: {
216 id:
217 bookableType !== 'event'
218 ? store.getters['booking/getCustomerId']
219 : store.getters['customerInfo/getCustomerId'],
220 firstName:
221 bookableType !== 'event'
222 ? store.getters['booking/getCustomerFirstName']
223 : store.getters['customerInfo/getCustomerFirstName'],
224 lastName:
225 bookableType !== 'event'
226 ? store.getters['booking/getCustomerLastName']
227 : store.getters['customerInfo/getCustomerLastName'],
228 email:
229 bookableType !== 'event'
230 ? store.getters['booking/getCustomerEmail']
231 : store.getters['customerInfo/getCustomerEmail'],
232 phone:
233 bookableType !== 'event'
234 ? store.getters['booking/getCustomerPhone']
235 : store.getters['customerInfo/getCustomerPhone'],
236 subscribeToMailchimp:
237 bookableType !== 'event'
238 ? store.getters['booking/getCustomerSubscribe']
239 : store.getters['customerInfo/getCustomerSubscribe'],
240 countryPhoneIso:
241 bookableType !== 'event'
242 ? store.getters['booking/getCustomerCountryPhoneIso']
243 : store.getters['customerInfo/getCustomerCountryPhoneIso'],
244 externalId:
245 bookableType !== 'event'
246 ? store.getters['booking/getCustomerExternalId']
247 : store.getters['customerInfo/getCustomerExternalId'],
248 translations: store.getters['booking/getCustomerTranslations'],
249 customFields: customerCustomFields,
250 },
251 },
252 ],
253 payment: Object.assign(
254 {
255 gateway: gateway,
256 currency: settings.payments.currencyCode,
257 },
258 {
259 data: paymentData,
260 },
261 ),
262 recaptcha: recaptcha,
263 locale: locale,
264 timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
265 urlParams: useUrlQueryParams(window.location.href),
266 componentProps: componentProps,
267 returnUrl: location.href,
268 ivy: store.getters['getIvy'],
269 }
270
271 let coupon = null
272
273 switch (bookableType) {
274 case 'appointment': {
275 coupon = store.getters['booking/getCoupon']
276
277 jsonData.couponCode =
278 coupon && (coupon.required || coupon.discount || coupon.deduction) ? coupon.code : null
279
280 jsonData.notifyParticipants = 1
281
282 let appointments = useAppointmentBookingData(store)
283
284 jsonData.isCart = useCart(store).length > 1 ? 1 : 0
285
286 jsonData.bookings[0].utcOffset = appointments[0].utcOffset
287
288 jsonData.bookings[0].extras = appointments[0].extras
289
290 jsonData.bookings[0].persons = appointments[0].persons
291
292 jsonData.bookings[0].duration = appointments[0].duration
293
294 jsonData.recurring = appointments.slice(1)
295
296 jsonData.package = []
297
298 if (store.getters['appointmentWaitingListOptions/getIsWaitingListSlot']) {
299 jsonData.bookings[0].status = 'waiting'
300 }
301
302 jsonData = Object.assign(jsonData, appointments[0])
303
304 break
305 }
306
307 case 'package':
308 jsonData = Object.assign(jsonData, usePackageBookingData(store))
309 jsonData.bookings[0].extras = []
310 jsonData.bookings[0].persons = 1
311
312 break
313
314 case 'event':
315 jsonData = Object.assign(jsonData, {
316 eventId: store.getters['eventBooking/getSelectedEventId'],
317 })
318
319 coupon = store.getters['coupon/getCoupon']
320
321 jsonData = Object.assign(jsonData, {
322 couponCode:
323 coupon && (coupon.required || coupon.discount || coupon.deduction) ? coupon.code : null,
324 })
325
326 jsonData.bookings[0] = Object.assign(jsonData.bookings[0], {
327 ticketsData: store.getters['tickets/getTicketsData'],
328 })
329
330 jsonData.bookings[0].persons = store.getters['persons/getPersons']
331
332 jsonData.bookings[0].utcOffset = settings.general.showClientTimeZone
333 ? useUtcValueOffset(
334 store.getters['eventEntities/getEvent'](
335 store.getters['eventBooking/getSelectedEventId'],
336 )['periods'][0]['periodStart'],
337 )
338 : null
339
340 if (store.getters['eventWaitingListOptions/getAvailability']) {
341 jsonData.bookings[0].status = 'waiting'
342 }
343
344 break
345 }
346
347 let bookingData = jsonData
348 let requestOptions = {}
349
350 if (bookingData.componentProps && bookingData.componentProps.state && bookableType !== 'event') {
351 bookingData.componentProps.state.appointments.forEach((a) => {
352 Object.keys(a.services).forEach((s) => {
353 a.services[s].slots = []
354 })
355 })
356 }
357
358 const cacheComponentProps = bookingData.componentProps
359
360 if (Object.keys(attachments).length && !mandatoryJson) {
361 bookingData = new FormData()
362
363 buildFormData(bookingData, jsonData)
364
365 for (let id in attachments) {
366 attachments[id].forEach((item, index) => {
367 bookingData.append('files[' + id + '][' + index + ']', item.raw)
368 })
369 }
370
371 requestOptions = {
372 headers: {
373 'Content-Type': 'multipart/form-data',
374 },
375 }
376 }
377
378 if (gateway === 'mollie' || gateway === 'wc' || gateway === 'barion' || gateway === 'razorpay') {
379 sessionStorage.setItem(
380 'ameliaCacheData',
381 JSON.stringify({ request: cacheComponentProps, paymentMethod: gateway, type: bookableType }),
382 )
383 }
384
385 return {
386 data: bookingData,
387 options: requestOptions,
388 // Kept separate from `data`, which becomes FormData once files are attached.
389 componentProps: cacheComponentProps,
390 }
391 }
392
393 function useEventsStoreData(store) {
394 return {
395 eventId: store.getters['eventBooking/getSelectedEventId'],
396 bookableType: store.getters['bookableType/getType'],
397 coupon: store.getters['coupon/getCoupon'],
398 customerInfo: store.getters['customerInfo/getAllData'],
399 customFields: store.getters['customFields/getAllData'],
400 payment: store.getters['payment/getAllData'],
401 tickets: store.getters['tickets/getAllData'],
402 persons: store.getters['persons/getAllData'],
403 pagination: store.getters['pagination/getAllData'],
404 params: store.getters['params/getAllData'],
405 eventsDisplay: store.getters['eventEntities/getEventsDisplay'],
406 }
407 }
408
409 function useAppointmentBookingData(store) {
410 let appointments = []
411
412 useCart(store).forEach((item) => {
413 if (item.serviceId && item.serviceId in item.services) {
414 item.services[item.serviceId].list.forEach((appointment) => {
415 let bookingStart = appointment.date ? appointment.date + ' ' + appointment.time : null
416
417 appointments.push({
418 serviceId: item.serviceId,
419 providerId: appointment.providerId,
420 locationId: appointment.locationId,
421 bookingStart: settings.general.showClientTimeZone
422 ? useUtcValue(bookingStart)
423 : bookingStart,
424 utcOffset: settings.general.showClientTimeZone ? useUtcValueOffset(bookingStart) : null,
425 extras: appointment.extras,
426 persons: appointment.persons,
427 duration: appointment.duration,
428 })
429 })
430 }
431 })
432
433 return appointments
434 }
435
436 function usePackageBookingData(store) {
437 let appointments = []
438
439 let rules = []
440
441 let cartItem = useCartItem(store)
442
443 let activeItemServices = cartItem.services
444
445 for (let serviceId in activeItemServices) {
446 activeItemServices[serviceId].list.forEach((appointment) => {
447 if (appointment.date && appointment.time) {
448 let utcOffset = null
449
450 let bookingStart = appointment.date + ' ' + appointment.time
451
452 if (settings.general.showClientTimeZone) {
453 bookingStart = useUtcValue(bookingStart)
454
455 utcOffset = useUtcValueOffset(bookingStart)
456 }
457
458 appointments.push({
459 bookingStart: bookingStart,
460 serviceId: parseInt(serviceId),
461 providerId: appointment.providerId,
462 locationId: appointment.locationId,
463 utcOffset: utcOffset,
464 notifyParticipants: 1,
465 })
466 }
467 })
468
469 rules.push({
470 serviceId: parseInt(serviceId),
471 providerId: activeItemServices[serviceId].providerId
472 ? activeItemServices[serviceId].providerId
473 : null,
474 locationId: activeItemServices[serviceId].locationId
475 ? activeItemServices[serviceId].locationId
476 : null,
477 })
478 }
479
480 let coupon = store.getters['booking/getCoupon']
481
482 return {
483 package: appointments,
484 packageId: store.getters['booking/getPackageId'],
485 packageRules: rules,
486 utcOffset: useUtcValueOffset(null),
487 deposit: !store.getters['booking/getPaymentDeposit'],
488 couponCode:
489 coupon && (coupon.required || coupon.discount || coupon.deduction) ? coupon.code : null,
490 }
491 }
492
493 function createBooking(store, requestData, successCallback, errorCallback) {
494 httpClient
495 .post('/bookings', requestData.data, requestData.options)
496 .then(successCallback)
497 .catch(errorCallback)
498 }
499
500 function useCreateBooking(store, requestData, successCallback, errorCallback) {
501 let bookableType = store.getters['bookableType/getType']
502 ? store.getters['bookableType/getType']
503 : store.getters['booking/getBookableType']
504 useAction(
505 store,
506 useBookingData(store, null, true, {}, null).data,
507 'beforeBooking',
508 bookableType,
509 () => {
510 createBooking(store, requestData, successCallback, errorCallback)
511 },
512 (message) => {
513 errorCallback({ response: { data: { data: { message: message } } } })
514 },
515 )
516 }
517
518 function useBookingError(response, store) {
519 let bookableType = store.getters['bookableType/getType']
520 ? store.getters['bookableType/getType']
521 : store.getters['booking/getBookableType']
522 let message = globalLabels['payment_error_default']
523
524 if (
525 response &&
526 typeof response === 'object' &&
527 response.data &&
528 typeof response.data === 'object'
529 ) {
530 if ('onSitePayment' in response.data && response.data.onSitePayment === true) {
531 store.commit('payment/setOnSitePayment', true)
532 }
533
534 if ('customerAlreadyBooked' in response.data && response.data.customerAlreadyBooked === true) {
535 message =
536 bookableType === 'event'
537 ? globalLabels['customer_already_booked_ev']
538 : globalLabels['customer_already_booked_app']
539 } else if (
540 'timeSlotUnavailable' in response.data &&
541 response.data.timeSlotUnavailable === true
542 ) {
543 message =
544 bookableType === 'event'
545 ? globalLabels['maximum_capacity_reached']
546 : globalLabels['time_slot_unavailable']
547 } else if ('customerBlocked' in response.data && response.data.customerBlocked === true) {
548 message = globalLabels['customer_blocked']
549 } else if (
550 'bookingsLimitReached' in response.data &&
551 response.data.bookingsLimitReached === true
552 ) {
553 message = globalLabels['bookings_limit_reached']
554 } else if (
555 'eventBookingUnavailable' in response.data &&
556 response.data.eventBookingUnavailable === true
557 ) {
558 message = globalLabels['event_booking_unavailable']
559 } else if ('emailError' in response.data && response.data.emailError === true) {
560 message = globalLabels['email_exist_error']
561 } else if ('phoneError' in response.data && response.data.phoneError === true) {
562 message = globalLabels['phone_exist_error']
563 } else if ('couponUnknown' in response.data && response.data.couponUnknown === true) {
564 message = globalLabels['coupon_unknown']
565 } else if ('couponInvalid' in response.data && response.data.couponInvalid === true) {
566 message = globalLabels['coupon_invalid']
567 } else if ('couponExpired' in response.data && response.data.couponExpired === true) {
568 message = globalLabels['coupon_expired']
569 } else if ('couponMissing' in response.data && response.data.couponMissing === true) {
570 message = globalLabels['coupon_missing']
571 } else if ('emailRequired' in response.data && response.data.emailRequired === true) {
572 message = globalLabels['email_required']
573 } else if ('paymentSuccessful' in response.data && response.data.paymentSuccessful === false) {
574 if (response.data.message) {
575 message = response.data.message
576 } else {
577 message = globalLabels['payment_error']
578 }
579 } else if (
580 'bookingAlreadyInWcCart' in response.data &&
581 response.data.bookingAlreadyInWcCart === true
582 ) {
583 message = globalLabels['booking_already_in_wc_cart']
584 } else if ('wcError' in response.data && response.data.wcError === true) {
585 message = globalLabels['wc_error']
586 } else if ('recaptchaError' in response.data && response.data.recaptchaError === true) {
587 message = globalLabels['recaptcha_invalid_error']
588 } else if (
589 'packageBookingUnavailable' in response.data &&
590 response.data.packageBookingUnavailable === true
591 ) {
592 message = globalLabels['package_booking_unavailable']
593 } else if ('message' in response.data && response.data.message) {
594 message = response.data.message
595 }
596 }
597
598 return message
599 }
600
601 function useNotify(store, response, success, error) {
602 if (!settings.general.runInstantPostBookingActions) {
603 let request = {}
604
605 switch (response.type) {
606 case 'appointment':
607 request = getAppointmentNotifyData(store, response)
608
609 break
610
611 case 'package':
612 request = getPackageNotifyData(response)
613
614 break
615
616 case 'event':
617 request = getEventNotifyData(response)
618
619 break
620 }
621
622 httpClient
623 .post(
624 '/bookings/success/' + request.id + '&nocache=' + new Date().getTime(),
625 request.data,
626 {},
627 )
628 .then(success)
629 .catch(error)
630 }
631 }
632
633 function getAppointmentNotifyData(store, response) {
634 let recurringData = []
635
636 response.recurring.forEach((recurring) => {
637 recurringData.push({
638 type: 'appointment',
639 id: recurring.booking.id,
640 appointmentStatusChanged: recurring.appointmentStatusChanged,
641 })
642 })
643
644 return {
645 id: response.booking.id,
646 data: {
647 type: useCart(store).length > 1 ? 'cart' : 'appointment',
648 appointmentStatusChanged: response.appointmentStatusChanged,
649 recurring: recurringData,
650 packageId: null,
651 customer: response.customer,
652 paymentId: 'paymentId' in response && response.paymentId ? response.paymentId : null,
653 packageCustomerId: null,
654 isPackageAppointment: response.isPackageAppointment,
655 },
656 }
657 }
658
659 function getPackageNotifyData(response) {
660 let bookings = []
661 let bookingId = 0
662 response.package.forEach((packData, index) => {
663 if (index > 0) {
664 bookings.push({
665 type: 'appointment',
666 id: packData.booking.id,
667 appointmentStatusChanged: packData.appointmentStatusChanged,
668 })
669 } else {
670 bookingId = packData.booking.id
671 }
672 })
673
674 return {
675 id: bookingId,
676 data: {
677 type: 'package',
678 appointmentStatusChanged: response.appointmentStatusChanged,
679 recurring: bookings,
680 packageId: response.packageId,
681 customer: response.customer,
682 paymentId: 'paymentId' in response && response.paymentId ? response.paymentId : null,
683 packageCustomerId: response.packageCustomerId,
684 isPackageAppointment: true,
685 },
686 }
687 }
688
689 function getEventNotifyData(response) {
690 return {
691 id: response.booking.id,
692 data: {
693 type: 'event',
694 appointmentStatusChanged: response.appointmentStatusChanged,
695 paymentId: 'paymentId' in response && response.paymentId ? response.paymentId : null,
696 },
697 }
698 }
699
700 function getSingleAppointmentData(store, date, data) {
701 let services = useServices(store)
702
703 let bookable = services.find((i) => i.id === data.appointment.serviceId)
704
705 return {
706 appointmentId: data.appointment ? data.appointment.id : '',
707 bookings: [data.booking],
708 bookingId: data.booking.id,
709 serviceId: data.appointment.serviceId,
710 providerId: data.appointment.providerId,
711 locationId: data.appointment.locationId,
712 title: bookable.name,
713 description: bookable.description,
714 start: settings.general.showClientTimeZone
715 ? useLocalValue(date.start)
716 : data.appointment.bookingStart,
717 end: settings.general.showClientTimeZone
718 ? useLocalValue(date.end)
719 : data.appointment.bookingEnd,
720 utcStart: moment.utc(date.start.replace(/ /g, 'T')).toDate(),
721 utcEnd: moment.utc(date.end.replace(/ /g, 'T')).toDate(),
722 cfAddress: getAddressFromCF(store),
723 }
724 }
725
726 function runAction(store, response) {
727 useAction(
728 store,
729 {
730 appointmentId: response.appointment ? response.appointment.id : null,
731 payment: Object.assign(response.payment, { currency: settings.payments.currencyCode }),
732 ...(response.type === 'event' && {
733 event: response.event || null,
734 number_of_persons: response.booking ? response.booking.persons : null,
735 }),
736 ...(response.isCart && {
737 providerId: response.appointment ? response.appointment.providerId : null,
738 locationId: response.appointment ? response.appointment.locationId : null,
739 serviceId: response.appointment ? response.appointment.serviceId : null,
740 isCartAppointment: response.isCart,
741 }),
742 },
743 response.payment.gateway === 'onSite' ? 'Schedule' : 'Purchase',
744 response.type,
745 null,
746 null,
747 )
748
749 if (response.recurring.length) {
750 response.recurring.forEach((item) => {
751 useAction(
752 store,
753 {
754 appointmentId: item.appointment ? item.appointment.id : null,
755 payment: Object.assign(item.booking.payments[0], {
756 currency: settings.payments.currencyCode,
757 }),
758 ...(response.isCart && {
759 providerId: item.appointment ? item.appointment.providerId : null,
760 locationId: item.appointment ? item.appointment.locationId : null,
761 serviceId: item.appointment ? item.appointment.serviceId : null,
762 isCartAppointment: true,
763 }),
764 },
765 item.booking.payments[0].gateway === 'onSite' ? 'Schedule' : 'Purchase',
766 item.type,
767 null,
768 null,
769 )
770 })
771 }
772 }
773
774 function getAddressFromCF(store) {
775 let bookingCustomFields = store.getters['booking/getAvailableCustomFields']
776 if (bookingCustomFields) {
777 for (let i = 0; i < Object.values(bookingCustomFields).length; i++) {
778 let cf = Object.values(bookingCustomFields)[i]
779 let cfId = Object.keys(bookingCustomFields)[i]
780 let customField = store.getters['entities/getCustomField'](cfId)
781 if (cf.value && customField && customField.type === 'address' && customField.useAsLocation) {
782 return cf.value
783 }
784 }
785 }
786 return null
787 }
788
789 function useAppointmentCalendarData(store, response) {
790 let appointments = []
791
792 let payments = []
793 let paymentAmount = 0
794
795 response.utcTime.forEach((date) => {
796 appointments.push(getSingleAppointmentData(store, date, response))
797
798 payments.push(response.payment)
799
800 paymentAmount = response.booking.payments[0].amount
801 })
802
803 response.recurring.forEach((data) => {
804 data.utcTime.forEach((date) => {
805 appointments.push(getSingleAppointmentData(store, date, data))
806 })
807
808 if (useCart(store).length > 1 || response.bookable.recurringPayment > 0) {
809 payments.push(data.booking.payments[0])
810
811 paymentAmount += data.booking.payments[0].amount
812 }
813 })
814
815 runAction(store, response)
816
817 let amountInfo = useAppointmentsAmountInfo(store)
818
819 let totalAmount = 0
820
821 amountInfo.forEach((item) => {
822 totalAmount +=
823 item.prepaid.totalAmount +
824 item.postpaid.totalAmount -
825 item.prepaid.discountAmount -
826 item.postpaid.discountAmount +
827 item.prepaid.taxAmount +
828 item.postpaid.taxAmount
829 })
830
831 return {
832 type: 'appointment',
833 data: appointments,
834 token: response.booking.token,
835 payments: payments,
836 paymentAmount: paymentAmount,
837 price: totalAmount,
838 customerCabinetUrl: response.customerCabinetUrl,
839 }
840 }
841
842 function usePackageCalendarData(store, response) {
843 let token = ''
844
845 let appointments = []
846
847 response.package.forEach((data, index) => {
848 if (index === 0) {
849 token = data.booking.token
850 }
851
852 data.utcTime.forEach((date) => {
853 appointments.push(getSingleAppointmentData(store, date, data))
854 })
855 })
856
857 runAction(store, response)
858
859 let pack = store.getters['entities/getPackage'](store.getters['booking/getPackageId'])
860
861 let tax = useEntityTax(store, store.getters['booking/getPackageId'], 'package')
862
863 let amountData = useAmount(
864 pack,
865 store.getters['booking/getCoupon'],
866 tax && settings.payments.taxes.enabled
867 ? Object.assign({}, tax, { excluded: settings.payments.taxes.excluded })
868 : null,
869 usePackageAmount(pack),
870 false,
871 )
872
873 return {
874 type: 'package',
875 data: appointments,
876 token: token,
877 payments: [response.payment],
878 paymentAmount: response.payment.amount,
879 payment: response.payment,
880 price:
881 amountData.deposit && response.payment.gateway !== 'onSite'
882 ? amountData.deposit
883 : amountData.price - amountData.discount + amountData.tax,
884 customerCabinetUrl: response.customerCabinetUrl,
885 }
886 }
887
888 function useEventCalendarData(store, response) {
889 let locationAddress = ''
890
891 if (response.event.location) {
892 locationAddress = response.event.location.address
893 } else if (response.event.customLocation) {
894 locationAddress = response.event.customLocation
895 }
896
897 let data = []
898
899 let responsePeriods = settings.general.showClientTimeZone
900 ? response.utcTime
901 : response.event.periods
902
903 let periodsArr = []
904 responsePeriods.forEach(function (date) {
905 let keyStart = settings.general.showClientTimeZone ? 'start' : 'periodStart'
906 let keyEnd = settings.general.showClientTimeZone ? 'end' : 'periodEnd'
907 let periodStartDate = moment(date[keyStart].split(' ')[0], 'YYYY-MM-DD')
908 let periodEndDate = moment(date[keyEnd].split(' ')[0], 'YYYY-MM-DD')
909 let periodStartTime = moment(date[keyStart].split(' ')[1], 'HH:mm:ss').format('HH:mm:ss')
910 let periodEndTime = moment(date[keyEnd].split(' ')[1], 'HH:mm:ss').format('HH:mm:ss')
911
912 if (periodEndTime === '00:00:00') {
913 periodEndTime = '24:00:00'
914 periodEndDate.subtract(1, 'days')
915 }
916
917 /** if the period in the event lasts for several days */
918 if (periodStartDate.diff(periodEndDate, 'days') < 0) {
919 let periodDates = []
920
921 while (periodStartDate.isSameOrBefore(periodEndDate)) {
922 periodDates.push(periodStartDate.format('YYYY-MM-DD'))
923 periodStartDate.add(1, 'days')
924 }
925
926 periodDates.forEach((dayPeriod) => {
927 periodsArr.push({
928 start: settings.general.showClientTimeZone
929 ? useLocalValue(dayPeriod + ' ' + periodStartTime)
930 : dayPeriod + ' ' + periodStartTime,
931 end: settings.general.showClientTimeZone
932 ? useLocalValue(dayPeriod + ' ' + periodEndTime)
933 : dayPeriod + ' ' + periodEndTime,
934 })
935 })
936 } else {
937 periodsArr.push({
938 start: settings.general.showClientTimeZone
939 ? useLocalValue(periodStartDate.format('YYYY-MM-DD') + ' ' + periodStartTime)
940 : periodStartDate.format('YYYY-MM-DD') + ' ' + periodStartTime,
941 end: settings.general.showClientTimeZone
942 ? useLocalValue(periodEndDate.format('YYYY-MM-DD') + ' ' + periodEndTime)
943 : periodEndDate.format('YYYY-MM-DD') + ' ' + periodEndTime,
944 })
945 }
946 })
947
948 periodsArr
949 .sort((a, b) => moment(a.start, 'YYYY-MM-DD HH:mm:ss') - moment(b.start, 'YYYY-MM-DD HH:mm:ss'))
950 .forEach((item) => {
951 data.push({
952 eventId: response.event.id,
953 bookingId: response.booking.id,
954 locationId: response.event.locationId,
955 organizerId: response.event.organizerId,
956 title: response.event.name,
957 description: response.event.description,
958 start: item.start,
959 end: item.end,
960 utcStart: moment.utc(item.start.replace(/ /g, 'T')).toDate(),
961 utcEnd: moment.utc(item.end.replace(/ /g, 'T')).toDate(),
962 cfAddress: getAddressFromCF(store),
963 })
964 })
965
966 let ticketsData = []
967
968 response.booking.ticketsData.forEach((ticket) => {
969 if (ticket.persons) {
970 let ticketInEvent = response.event.customTickets.find((t) => t.id === ticket.eventTicketId)
971 ticket.name = ticketInEvent.name
972
973 ticketsData.push(ticket)
974 }
975 })
976
977 let amSettings = computed(() => store.getters['getSettings'])
978 let afterBookingUrl = computed(() => {
979 let entitySettings = response.event.settings
980 ? JSON.parse(response.event.settings)
981 : amSettings.value
982 let url = ''
983 if (
984 'general' in entitySettings &&
985 'redirectUrlAfterAppointment' in entitySettings.general &&
986 entitySettings.general.redirectUrlAfterAppointment
987 ) {
988 url = entitySettings.general.redirectUrlAfterAppointment
989 } else if (amSettings.value.general.redirectUrlAfterAppointment) {
990 url = amSettings.value.general.redirectUrlAfterAppointment
991 }
992
993 return url
994 })
995 let customerPanelUrl = computed(() => {
996 let entitySettings = response.event.settings
997 ? JSON.parse(response.event.settings)
998 : amSettings.value
999 let url = ''
1000 if (
1001 'roles' in entitySettings &&
1002 'customerCabinet' in entitySettings.roles &&
1003 'pageUrl' in entitySettings.roles.customerCabinet &&
1004 entitySettings.roles.customerCabinet.pageUrl
1005 ) {
1006 url = entitySettings.roles.customerCabinet.pageUrl
1007 } else if (amSettings.value.roles.customerCabinet.pageUrl) {
1008 url = amSettings.value.roles.customerCabinet.pageUrl
1009 }
1010
1011 return url
1012 })
1013
1014 runAction(store, response)
1015
1016 let event = response.event
1017
1018 let price = 0
1019
1020 if (event.customPricing) {
1021 response.booking.ticketsData.forEach((t) => {
1022 if (t.persons) {
1023 price += event.aggregatedPrice ? t.price * t.persons : t.price
1024 }
1025 })
1026 } else {
1027 price = event.aggregatedPrice ? event.price * response.booking.persons : event.price
1028 }
1029
1030 let tax = useEntityTax(store, store.getters['eventBooking/getSelectedEventId'], 'event')
1031
1032 let amountData = useAmount(
1033 event,
1034 store.getters['coupon/getCoupon'],
1035 settings.payments.taxes.enabled
1036 ? Object.assign({}, tax, { excluded: settings.payments.taxes.excluded })
1037 : null,
1038 price,
1039 false,
1040 )
1041
1042 return {
1043 type: response.type,
1044 active: settings.general.addToCalendar,
1045 data: data,
1046 event: response.event,
1047 bringingAnyone: response.event.bringingAnyone,
1048 customPricing: response.event.customPricing,
1049 ticketsData: ticketsData,
1050 address: locationAddress,
1051 token: response.booking.token,
1052 persons: response.booking.persons,
1053 payments: [response.payment],
1054 paymentAmount: response.payment.amount,
1055 price:
1056 amountData.deposit && response.payment.gateway !== 'onSite'
1057 ? amountData.deposit
1058 : amountData.price - amountData.discount + amountData.tax,
1059 redirectAfterBookingUrl: afterBookingUrl.value,
1060 customerCabinetUrl: customerPanelUrl.value,
1061 }
1062 }
1063
1064 function useCreateBookingSuccess(store, response, callback) {
1065 if (response.data.data) {
1066 let bookableType = store.getters['bookableType/getType']
1067 ? store.getters['bookableType/getType']
1068 : store.getters['booking/getBookableType']
1069 store.commit(bookableType === 'event' ? 'setLoading' : 'booking/setLoading', false)
1070
1071 switch (bookableType) {
1072 case 'appointment':
1073 store.commit('booking/setBooked', useAppointmentCalendarData(store, response.data.data))
1074
1075 break
1076
1077 case 'package':
1078 store.commit('booking/setBooked', usePackageCalendarData(store, response.data.data))
1079
1080 break
1081
1082 case 'event':
1083 store.commit('eventBooking/setBooked', useEventCalendarData(store, response.data.data))
1084
1085 break
1086 }
1087
1088 useNotify(
1089 store,
1090 response.data.data,
1091 () => {},
1092 () => {},
1093 )
1094 }
1095
1096 if (typeof callback !== 'undefined') {
1097 callback()
1098 }
1099 }
1100
1101 function useCreateBookingError(store, response, callback) {
1102 const bookingErrorResponse =
1103 response &&
1104 typeof response === 'object' &&
1105 'data' in response &&
1106 response.data &&
1107 typeof response.data === 'object' &&
1108 'data' in response.data
1109 ? response.data
1110 : response
1111
1112 if (bookingErrorResponse && typeof bookingErrorResponse === 'object') {
1113 const normalizedBookingErrorResponse =
1114 'data' in bookingErrorResponse ? bookingErrorResponse : { data: bookingErrorResponse }
1115
1116 errorMessage.value = useBookingError(normalizedBookingErrorResponse, store)
1117 }
1118
1119 if (store.getters['bookableType/getType'] === 'event') {
1120 store.commit('setLoading', false)
1121 } else {
1122 store.commit('booking/setLoading', false)
1123 }
1124
1125 if (typeof callback !== 'undefined') {
1126 callback()
1127 }
1128 }
1129
1130 function getBookingErrorResponse(error) {
1131 if (error?.response?.data) {
1132 return error.response.data
1133 }
1134
1135 if (error?.data) {
1136 return error
1137 }
1138
1139 return {
1140 data: {
1141 message: error?.message || globalLabels['payment_error'],
1142 },
1143 }
1144 }
1145
1146 function getErrorMessage() {
1147 return errorMessage.value
1148 }
1149
1150 export {
1151 usePaymentError,
1152 useBookingData,
1153 useCreateBooking,
1154 useCreateBookingSuccess,
1155 useCreateBookingError,
1156 getBookingErrorResponse,
1157 useAppointmentCalendarData,
1158 usePackageCalendarData,
1159 useEventCalendarData,
1160 getErrorMessage,
1161 useBookingError,
1162 useNotify,
1163 useAppointmentBookingData,
1164 usePackageBookingData,
1165 }
1166