PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.4
Booking for Appointments and Events Calendar – Amelia v2.4.4
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 6 days ago booking.js 6 days ago cabinet.js 6 days ago cart.js 6 days ago catalog.js 6 days ago coupon.js 6 days ago customFields.js 6 days ago events.js 6 days ago facebookPixel.js 6 days ago ivy.js 6 days ago package.js 6 days ago panel.js 6 days ago public.js 6 days ago renderActions.js 6 days ago restore.js 6 days ago slots.js 6 days ago translation.js 6 days ago user.js 6 days ago
booking.js
1142 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') {
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 }
389 }
390
391 function useEventsStoreData(store) {
392 return {
393 eventId: store.getters['eventBooking/getSelectedEventId'],
394 bookableType: store.getters['bookableType/getType'],
395 coupon: store.getters['coupon/getCoupon'],
396 customerInfo: store.getters['customerInfo/getAllData'],
397 customFields: store.getters['customFields/getAllData'],
398 payment: store.getters['payment/getAllData'],
399 tickets: store.getters['tickets/getAllData'],
400 persons: store.getters['persons/getAllData'],
401 pagination: store.getters['pagination/getAllData'],
402 params: store.getters['params/getAllData'],
403 eventsDisplay: store.getters['eventEntities/getEventsDisplay'],
404 }
405 }
406
407 function useAppointmentBookingData(store) {
408 let appointments = []
409
410 useCart(store).forEach((item) => {
411 if (item.serviceId && item.serviceId in item.services) {
412 item.services[item.serviceId].list.forEach((appointment) => {
413 let bookingStart = appointment.date ? appointment.date + ' ' + appointment.time : null
414
415 appointments.push({
416 serviceId: item.serviceId,
417 providerId: appointment.providerId,
418 locationId: appointment.locationId,
419 bookingStart: settings.general.showClientTimeZone
420 ? useUtcValue(bookingStart)
421 : bookingStart,
422 utcOffset: settings.general.showClientTimeZone ? useUtcValueOffset(bookingStart) : null,
423 extras: appointment.extras,
424 persons: appointment.persons,
425 duration: appointment.duration,
426 })
427 })
428 }
429 })
430
431 return appointments
432 }
433
434 function usePackageBookingData(store) {
435 let appointments = []
436
437 let rules = []
438
439 let cartItem = useCartItem(store)
440
441 let activeItemServices = cartItem.services
442
443 for (let serviceId in activeItemServices) {
444 activeItemServices[serviceId].list.forEach((appointment) => {
445 if (appointment.date && appointment.time) {
446 let utcOffset = null
447
448 let bookingStart = appointment.date + ' ' + appointment.time
449
450 if (settings.general.showClientTimeZone) {
451 bookingStart = useUtcValue(bookingStart)
452
453 utcOffset = useUtcValueOffset(bookingStart)
454 }
455
456 appointments.push({
457 bookingStart: bookingStart,
458 serviceId: parseInt(serviceId),
459 providerId: appointment.providerId,
460 locationId: appointment.locationId,
461 utcOffset: utcOffset,
462 notifyParticipants: 1,
463 })
464 }
465 })
466
467 rules.push({
468 serviceId: parseInt(serviceId),
469 providerId: activeItemServices[serviceId].providerId
470 ? activeItemServices[serviceId].providerId
471 : null,
472 locationId: activeItemServices[serviceId].locationId
473 ? activeItemServices[serviceId].locationId
474 : null,
475 })
476 }
477
478 let coupon = store.getters['booking/getCoupon']
479
480 return {
481 package: appointments,
482 packageId: store.getters['booking/getPackageId'],
483 packageRules: rules,
484 utcOffset: useUtcValueOffset(null),
485 deposit: !store.getters['booking/getPaymentDeposit'],
486 couponCode:
487 coupon && (coupon.required || coupon.discount || coupon.deduction) ? coupon.code : null,
488 }
489 }
490
491 function createBooking(store, requestData, successCallback, errorCallback) {
492 httpClient
493 .post('/bookings', requestData.data, requestData.options)
494 .then(successCallback)
495 .catch(errorCallback)
496 }
497
498 function useCreateBooking(store, requestData, successCallback, errorCallback) {
499 let bookableType = store.getters['bookableType/getType']
500 ? store.getters['bookableType/getType']
501 : store.getters['booking/getBookableType']
502 useAction(
503 store,
504 useBookingData(store, null, true, {}, null).data,
505 'beforeBooking',
506 bookableType,
507 () => {
508 createBooking(store, requestData, successCallback, errorCallback)
509 },
510 (message) => {
511 errorCallback({ response: { data: { data: { message: message } } } })
512 },
513 )
514 }
515
516 function useBookingError(response, store) {
517 let bookableType = store.getters['bookableType/getType']
518 ? store.getters['bookableType/getType']
519 : store.getters['booking/getBookableType']
520 let message = globalLabels['payment_error_default']
521
522 if ('onSitePayment' in response.data && response.data.onSitePayment === true) {
523 store.commit('payment/setOnSitePayment', true)
524 }
525
526 if ('data' in response) {
527 if ('customerAlreadyBooked' in response.data && response.data.customerAlreadyBooked === true) {
528 message =
529 bookableType === 'event'
530 ? globalLabels['customer_already_booked_ev']
531 : globalLabels['customer_already_booked_app']
532 } else if (
533 'timeSlotUnavailable' in response.data &&
534 response.data.timeSlotUnavailable === true
535 ) {
536 message =
537 bookableType === 'event'
538 ? globalLabels['maximum_capacity_reached']
539 : globalLabels['time_slot_unavailable']
540 } else if ('customerBlocked' in response.data && response.data.customerBlocked === true) {
541 message = globalLabels['customer_blocked']
542 } else if (
543 'bookingsLimitReached' in response.data &&
544 response.data.bookingsLimitReached === true
545 ) {
546 message = globalLabels['bookings_limit_reached']
547 } else if (
548 'eventBookingUnavailable' in response.data &&
549 response.data.eventBookingUnavailable === true
550 ) {
551 message = globalLabels['event_booking_unavailable']
552 } else if ('emailError' in response.data && response.data.emailError === true) {
553 message = globalLabels['email_exist_error']
554 } else if ('phoneError' in response.data && response.data.phoneError === true) {
555 message = globalLabels['phone_exist_error']
556 } else if ('couponUnknown' in response.data && response.data.couponUnknown === true) {
557 message = globalLabels['coupon_unknown']
558 } else if ('couponInvalid' in response.data && response.data.couponInvalid === true) {
559 message = globalLabels['coupon_invalid']
560 } else if ('couponExpired' in response.data && response.data.couponExpired === true) {
561 message = globalLabels['coupon_expired']
562 } else if ('couponMissing' in response.data && response.data.couponMissing === true) {
563 message = globalLabels['coupon_missing']
564 } else if ('emailRequired' in response.data && response.data.emailRequired === true) {
565 message = globalLabels['email_required']
566 } else if ('paymentSuccessful' in response.data && response.data.paymentSuccessful === false) {
567 if (response.data.message) {
568 message = response.data.message
569 } else {
570 message = globalLabels['payment_error']
571 }
572 } else if (
573 'bookingAlreadyInWcCart' in response.data &&
574 response.data.bookingAlreadyInWcCart === true
575 ) {
576 message = globalLabels['booking_already_in_wc_cart']
577 } else if ('wcError' in response.data && response.data.wcError === true) {
578 message = globalLabels['wc_error']
579 } else if ('recaptchaError' in response.data && response.data.recaptchaError === true) {
580 message = globalLabels['recaptcha_invalid_error']
581 } else if (
582 'packageBookingUnavailable' in response.data &&
583 response.data.packageBookingUnavailable === true
584 ) {
585 message = globalLabels['package_booking_unavailable']
586 } else if ('message' in response.data && response.data.message) {
587 message = response.data.message
588 }
589 }
590
591 return message
592 }
593
594 function useNotify(store, response, success, error) {
595 if (!settings.general.runInstantPostBookingActions) {
596 let request = {}
597
598 switch (response.type) {
599 case 'appointment':
600 request = getAppointmentNotifyData(store, response)
601
602 break
603
604 case 'package':
605 request = getPackageNotifyData(response)
606
607 break
608
609 case 'event':
610 request = getEventNotifyData(response)
611
612 break
613 }
614
615 httpClient
616 .post(
617 '/bookings/success/' + request.id + '&nocache=' + new Date().getTime(),
618 request.data,
619 {},
620 )
621 .then(success)
622 .catch(error)
623 }
624 }
625
626 function getAppointmentNotifyData(store, response) {
627 let recurringData = []
628
629 response.recurring.forEach((recurring) => {
630 recurringData.push({
631 type: 'appointment',
632 id: recurring.booking.id,
633 appointmentStatusChanged: recurring.appointmentStatusChanged,
634 })
635 })
636
637 return {
638 id: response.booking.id,
639 data: {
640 type: useCart(store).length > 1 ? 'cart' : 'appointment',
641 appointmentStatusChanged: response.appointmentStatusChanged,
642 recurring: recurringData,
643 packageId: null,
644 customer: response.customer,
645 paymentId: 'paymentId' in response && response.paymentId ? response.paymentId : null,
646 packageCustomerId: null,
647 isPackageAppointment: response.isPackageAppointment,
648 },
649 }
650 }
651
652 function getPackageNotifyData(response) {
653 let bookings = []
654 let bookingId = 0
655 response.package.forEach((packData, index) => {
656 if (index > 0) {
657 bookings.push({
658 type: 'appointment',
659 id: packData.booking.id,
660 appointmentStatusChanged: packData.appointmentStatusChanged,
661 })
662 } else {
663 bookingId = packData.booking.id
664 }
665 })
666
667 return {
668 id: bookingId,
669 data: {
670 type: 'package',
671 appointmentStatusChanged: response.appointmentStatusChanged,
672 recurring: bookings,
673 packageId: response.packageId,
674 customer: response.customer,
675 paymentId: 'paymentId' in response && response.paymentId ? response.paymentId : null,
676 packageCustomerId: response.packageCustomerId,
677 isPackageAppointment: true,
678 },
679 }
680 }
681
682 function getEventNotifyData(response) {
683 return {
684 id: response.booking.id,
685 data: {
686 type: 'event',
687 appointmentStatusChanged: response.appointmentStatusChanged,
688 paymentId: 'paymentId' in response && response.paymentId ? response.paymentId : null,
689 },
690 }
691 }
692
693 function getSingleAppointmentData(store, date, data) {
694 let services = useServices(store)
695
696 let bookable = services.find((i) => i.id === data.appointment.serviceId)
697
698 return {
699 appointmentId: data.appointment ? data.appointment.id : '',
700 bookings: [data.booking],
701 bookingId: data.booking.id,
702 serviceId: data.appointment.serviceId,
703 providerId: data.appointment.providerId,
704 locationId: data.appointment.locationId,
705 title: bookable.name,
706 description: bookable.description,
707 start: settings.general.showClientTimeZone
708 ? useLocalValue(date.start)
709 : data.appointment.bookingStart,
710 end: settings.general.showClientTimeZone
711 ? useLocalValue(date.end)
712 : data.appointment.bookingEnd,
713 utcStart: moment.utc(date.start.replace(/ /g, 'T')).toDate(),
714 utcEnd: moment.utc(date.end.replace(/ /g, 'T')).toDate(),
715 cfAddress: getAddressFromCF(store),
716 }
717 }
718
719 function runAction(store, response) {
720 useAction(
721 store,
722 {
723 appointmentId: response.appointment ? response.appointment.id : null,
724 payment: Object.assign(response.payment, { currency: settings.payments.currencyCode }),
725 ...(response.type === 'event' && {
726 event: response.event || null,
727 number_of_persons: response.booking ? response.booking.persons : null,
728 }),
729 ...(response.isCart && {
730 providerId: response.appointment ? response.appointment.providerId : null,
731 locationId: response.appointment ? response.appointment.locationId : null,
732 serviceId: response.appointment ? response.appointment.serviceId : null,
733 isCartAppointment: response.isCart,
734 }),
735 },
736 response.payment.gateway === 'onSite' ? 'Schedule' : 'Purchase',
737 response.type,
738 null,
739 null,
740 )
741
742 if (response.recurring.length) {
743 response.recurring.forEach((item) => {
744 useAction(
745 store,
746 {
747 appointmentId: item.appointment ? item.appointment.id : null,
748 payment: Object.assign(item.booking.payments[0], {
749 currency: settings.payments.currencyCode,
750 }),
751 ...(response.isCart && {
752 providerId: item.appointment ? item.appointment.providerId : null,
753 locationId: item.appointment ? item.appointment.locationId : null,
754 serviceId: item.appointment ? item.appointment.serviceId : null,
755 isCartAppointment: true,
756 }),
757 },
758 item.booking.payments[0].gateway === 'onSite' ? 'Schedule' : 'Purchase',
759 item.type,
760 null,
761 null,
762 )
763 })
764 }
765 }
766
767 function getAddressFromCF(store) {
768 let bookingCustomFields = store.getters['booking/getAvailableCustomFields']
769 if (bookingCustomFields) {
770 for (let i = 0; i < Object.values(bookingCustomFields).length; i++) {
771 let cf = Object.values(bookingCustomFields)[i]
772 let cfId = Object.keys(bookingCustomFields)[i]
773 let customField = store.getters['entities/getCustomField'](cfId)
774 if (cf.value && customField && customField.type === 'address' && customField.useAsLocation) {
775 return cf.value
776 }
777 }
778 }
779 return null
780 }
781
782 function useAppointmentCalendarData(store, response) {
783 let appointments = []
784
785 let payments = []
786 let paymentAmount = 0
787
788 response.utcTime.forEach((date) => {
789 appointments.push(getSingleAppointmentData(store, date, response))
790
791 payments.push(response.payment)
792
793 paymentAmount = response.booking.payments[0].amount
794 })
795
796 response.recurring.forEach((data) => {
797 data.utcTime.forEach((date) => {
798 appointments.push(getSingleAppointmentData(store, date, data))
799 })
800
801 if (useCart(store).length > 1 || response.bookable.recurringPayment > 0) {
802 payments.push(data.booking.payments[0])
803
804 paymentAmount += data.booking.payments[0].amount
805 }
806 })
807
808 runAction(store, response)
809
810 let amountInfo = useAppointmentsAmountInfo(store)
811
812 let totalAmount = 0
813
814 amountInfo.forEach((item) => {
815 totalAmount +=
816 item.prepaid.totalAmount +
817 item.postpaid.totalAmount -
818 item.prepaid.discountAmount -
819 item.postpaid.discountAmount +
820 item.prepaid.taxAmount +
821 item.postpaid.taxAmount
822 })
823
824 return {
825 type: 'appointment',
826 data: appointments,
827 token: response.booking.token,
828 payments: payments,
829 paymentAmount: paymentAmount,
830 price: totalAmount,
831 customerCabinetUrl: response.customerCabinetUrl,
832 }
833 }
834
835 function usePackageCalendarData(store, response) {
836 let token = ''
837
838 let appointments = []
839
840 response.package.forEach((data, index) => {
841 if (index === 0) {
842 token = data.booking.token
843 }
844
845 data.utcTime.forEach((date) => {
846 appointments.push(getSingleAppointmentData(store, date, data))
847 })
848 })
849
850 runAction(store, response)
851
852 let pack = store.getters['entities/getPackage'](store.getters['booking/getPackageId'])
853
854 let tax = useEntityTax(store, store.getters['booking/getPackageId'], 'package')
855
856 let amountData = useAmount(
857 pack,
858 store.getters['booking/getCoupon'],
859 tax && settings.payments.taxes.enabled
860 ? Object.assign({}, tax, { excluded: settings.payments.taxes.excluded })
861 : null,
862 usePackageAmount(pack),
863 false,
864 )
865
866 return {
867 type: 'package',
868 data: appointments,
869 token: token,
870 payments: [response.payment],
871 paymentAmount: response.payment.amount,
872 payment: response.payment,
873 price:
874 amountData.deposit && response.payment.gateway !== 'onSite'
875 ? amountData.deposit
876 : amountData.price - amountData.discount + amountData.tax,
877 customerCabinetUrl: response.customerCabinetUrl,
878 }
879 }
880
881 function useEventCalendarData(store, response) {
882 let locationAddress = ''
883
884 if (response.event.location) {
885 locationAddress = response.event.location.address
886 } else if (response.event.customLocation) {
887 locationAddress = response.event.customLocation
888 }
889
890 let data = []
891
892 let responsePeriods = settings.general.showClientTimeZone
893 ? response.utcTime
894 : response.event.periods
895
896 let periodsArr = []
897 responsePeriods.forEach(function (date) {
898 let keyStart = settings.general.showClientTimeZone ? 'start' : 'periodStart'
899 let keyEnd = settings.general.showClientTimeZone ? 'end' : 'periodEnd'
900 let periodStartDate = moment(date[keyStart].split(' ')[0], 'YYYY-MM-DD')
901 let periodEndDate = moment(date[keyEnd].split(' ')[0], 'YYYY-MM-DD')
902 let periodStartTime = moment(date[keyStart].split(' ')[1], 'HH:mm:ss').format('HH:mm:ss')
903 let periodEndTime = moment(date[keyEnd].split(' ')[1], 'HH:mm:ss').format('HH:mm:ss')
904
905 if (periodEndTime === '00:00:00') {
906 periodEndTime = '24:00:00'
907 periodEndDate.subtract(1, 'days')
908 }
909
910 /** if the period in the event lasts for several days */
911 if (periodStartDate.diff(periodEndDate, 'days') < 0) {
912 let periodDates = []
913
914 while (periodStartDate.isSameOrBefore(periodEndDate)) {
915 periodDates.push(periodStartDate.format('YYYY-MM-DD'))
916 periodStartDate.add(1, 'days')
917 }
918
919 periodDates.forEach((dayPeriod) => {
920 periodsArr.push({
921 start: settings.general.showClientTimeZone
922 ? useLocalValue(dayPeriod + ' ' + periodStartTime)
923 : dayPeriod + ' ' + periodStartTime,
924 end: settings.general.showClientTimeZone
925 ? useLocalValue(dayPeriod + ' ' + periodEndTime)
926 : dayPeriod + ' ' + periodEndTime,
927 })
928 })
929 } else {
930 periodsArr.push({
931 start: settings.general.showClientTimeZone
932 ? useLocalValue(periodStartDate.format('YYYY-MM-DD') + ' ' + periodStartTime)
933 : periodStartDate.format('YYYY-MM-DD') + ' ' + periodStartTime,
934 end: settings.general.showClientTimeZone
935 ? useLocalValue(periodEndDate.format('YYYY-MM-DD') + ' ' + periodEndTime)
936 : periodEndDate.format('YYYY-MM-DD') + ' ' + periodEndTime,
937 })
938 }
939 })
940
941 periodsArr
942 .sort((a, b) => moment(a.start, 'YYYY-MM-DD HH:mm:ss') - moment(b.start, 'YYYY-MM-DD HH:mm:ss'))
943 .forEach((item) => {
944 data.push({
945 eventId: response.event.id,
946 bookingId: response.booking.id,
947 locationId: response.event.locationId,
948 organizerId: response.event.organizerId,
949 title: response.event.name,
950 description: response.event.description,
951 start: item.start,
952 end: item.end,
953 utcStart: moment.utc(item.start.replace(/ /g, 'T')).toDate(),
954 utcEnd: moment.utc(item.end.replace(/ /g, 'T')).toDate(),
955 cfAddress: getAddressFromCF(store),
956 })
957 })
958
959 let ticketsData = []
960
961 response.booking.ticketsData.forEach((ticket) => {
962 if (ticket.persons) {
963 let ticketInEvent = response.event.customTickets.find((t) => t.id === ticket.eventTicketId)
964 ticket.name = ticketInEvent.name
965
966 ticketsData.push(ticket)
967 }
968 })
969
970 let amSettings = computed(() => store.getters['getSettings'])
971 let afterBookingUrl = computed(() => {
972 let entitySettings = response.event.settings
973 ? JSON.parse(response.event.settings)
974 : amSettings.value
975 let url = ''
976 if (
977 'general' in entitySettings &&
978 'redirectUrlAfterAppointment' in entitySettings.general &&
979 entitySettings.general.redirectUrlAfterAppointment
980 ) {
981 url = entitySettings.general.redirectUrlAfterAppointment
982 } else if (amSettings.value.general.redirectUrlAfterAppointment) {
983 url = amSettings.value.general.redirectUrlAfterAppointment
984 }
985
986 return url
987 })
988 let customerPanelUrl = computed(() => {
989 let entitySettings = response.event.settings
990 ? JSON.parse(response.event.settings)
991 : amSettings.value
992 let url = ''
993 if (
994 'roles' in entitySettings &&
995 'customerCabinet' in entitySettings.roles &&
996 'pageUrl' in entitySettings.roles.customerCabinet &&
997 entitySettings.roles.customerCabinet.pageUrl
998 ) {
999 url = entitySettings.roles.customerCabinet.pageUrl
1000 } else if (amSettings.value.roles.customerCabinet.pageUrl) {
1001 url = amSettings.value.roles.customerCabinet.pageUrl
1002 }
1003
1004 return url
1005 })
1006
1007 runAction(store, response)
1008
1009 let event = response.event
1010
1011 let price = 0
1012
1013 if (event.customPricing) {
1014 response.booking.ticketsData.forEach((t) => {
1015 if (t.persons) {
1016 price += event.aggregatedPrice ? t.price * t.persons : t.price
1017 }
1018 })
1019 } else {
1020 price = event.aggregatedPrice ? event.price * response.booking.persons : event.price
1021 }
1022
1023 let tax = useEntityTax(store, store.getters['eventBooking/getSelectedEventId'], 'event')
1024
1025 let amountData = useAmount(
1026 event,
1027 store.getters['coupon/getCoupon'],
1028 settings.payments.taxes.enabled
1029 ? Object.assign({}, tax, { excluded: settings.payments.taxes.excluded })
1030 : null,
1031 price,
1032 false,
1033 )
1034
1035 return {
1036 type: response.type,
1037 active: settings.general.addToCalendar,
1038 data: data,
1039 event: response.event,
1040 bringingAnyone: response.event.bringingAnyone,
1041 customPricing: response.event.customPricing,
1042 ticketsData: ticketsData,
1043 address: locationAddress,
1044 token: response.booking.token,
1045 persons: response.booking.persons,
1046 payments: [response.payment],
1047 paymentAmount: response.payment.amount,
1048 price:
1049 amountData.deposit && response.payment.gateway !== 'onSite'
1050 ? amountData.deposit
1051 : amountData.price - amountData.discount + amountData.tax,
1052 redirectAfterBookingUrl: afterBookingUrl.value,
1053 customerCabinetUrl: customerPanelUrl.value,
1054 }
1055 }
1056
1057 function useCreateBookingSuccess(store, response, callback) {
1058 if (response.data.data) {
1059 let bookableType = store.getters['bookableType/getType']
1060 ? store.getters['bookableType/getType']
1061 : store.getters['booking/getBookableType']
1062 store.commit(bookableType === 'event' ? 'setLoading' : 'booking/setLoading', false)
1063
1064 switch (bookableType) {
1065 case 'appointment':
1066 store.commit('booking/setBooked', useAppointmentCalendarData(store, response.data.data))
1067
1068 break
1069
1070 case 'package':
1071 store.commit('booking/setBooked', usePackageCalendarData(store, response.data.data))
1072
1073 break
1074
1075 case 'event':
1076 store.commit('eventBooking/setBooked', useEventCalendarData(store, response.data.data))
1077
1078 break
1079 }
1080
1081 useNotify(
1082 store,
1083 response.data.data,
1084 () => {},
1085 () => {},
1086 )
1087 }
1088
1089 if (typeof callback !== 'undefined') {
1090 callback()
1091 }
1092 }
1093
1094 function useCreateBookingError(store, response, callback) {
1095 const bookingErrorResponse =
1096 response &&
1097 typeof response === 'object' &&
1098 'data' in response &&
1099 response.data &&
1100 typeof response.data === 'object' &&
1101 'data' in response.data
1102 ? response.data
1103 : response
1104
1105 if (bookingErrorResponse && typeof bookingErrorResponse === 'object') {
1106 const normalizedBookingErrorResponse =
1107 'data' in bookingErrorResponse ? bookingErrorResponse : { data: bookingErrorResponse }
1108
1109 errorMessage.value = useBookingError(normalizedBookingErrorResponse, store)
1110 }
1111
1112 if (store.getters['bookableType/getType'] === 'event') {
1113 store.commit('setLoading', false)
1114 } else {
1115 store.commit('booking/setLoading', false)
1116 }
1117
1118 if (typeof callback !== 'undefined') {
1119 callback()
1120 }
1121 }
1122
1123 function getErrorMessage() {
1124 return errorMessage.value
1125 }
1126
1127 export {
1128 usePaymentError,
1129 useBookingData,
1130 useCreateBooking,
1131 useCreateBookingSuccess,
1132 useCreateBookingError,
1133 useAppointmentCalendarData,
1134 usePackageCalendarData,
1135 useEventCalendarData,
1136 getErrorMessage,
1137 useBookingError,
1138 useNotify,
1139 useAppointmentBookingData,
1140 usePackageBookingData,
1141 }
1142