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 / common / appointments.js
ameliabooking / v3 / src / assets / js / common Last commit date
appointments.js 1 day ago attendees.js 1 month ago colorManipulation.js 1 month ago customer.js 1 month ago date.js 1 month ago defaultCustomize.js 1 month ago employee.js 1 week ago events.js 1 month ago formFieldsTemplates.js 1 month ago formatting.js 1 month ago helper.js 1 week ago image.js 1 month ago integrationApple.js 1 month ago integrationGoogle.js 1 month ago integrationOutlook.js 1 month ago integrationRazorpay.js 1 day ago integrationStripe.js 1 month ago integrationZoom.js 1 month ago licence.js 1 month ago liveAnnouncer.js 1 month ago phone.js 1 month ago pricing.js 1 month ago recurring.js 1 week ago responsive.js 1 month ago scrollElements.js 1 month ago settings.js 1 month ago translationsElementPlus.js 1 day ago utilHeaderHeight.js 1 month ago whiteLabel.js 1 day ago
appointments.js
1597 lines
1 import { useTimeInSeconds } from './date'
2 import { useCart, useCartItem } from '../public/cart'
3 import { useSortedDateStrings } from './helper'
4 import { settings } from '../../../plugins/settings'
5 import { checkLimitPerEmployee, sortForEmployeeSelection } from './employee'
6 import {
7 useEntityTax,
8 usePercentageAmount,
9 useRoundAmount,
10 useTaxAmount,
11 useTaxedAmount,
12 } from './pricing'
13 import { getServicePrices } from '../public/catalog'
14 import { useFormattedPrice } from './formatting'
15
16 const globalSettings = reactive(window.wpAmeliaSettings)
17
18 function useEmployeeService(store, serviceId, employeeId) {
19 let entitiesRelations = store.getters['entities/getEntitiesRelations']
20
21 let service = store.getters['entities/getService'](serviceId)
22
23 let employeeService = employeeId
24 ? store.getters['entities/getEmployeeService'](employeeId, serviceId)
25 : service
26
27 let employees = store.getters['entities/filteredEmployees'](store.getters['booking/getSelection'])
28
29 let arrPrice = []
30
31 if (service) {
32 arrPrice.push(...getServicePrices(service))
33 }
34
35 if (employeeId) {
36 if (employeeService) {
37 arrPrice.push(...getServicePrices(employeeService))
38 }
39 } else {
40 employees.forEach((employee) => {
41 if (employee.id in entitiesRelations && serviceId in entitiesRelations[employee.id]) {
42 let eService = employee.serviceList.find((service) => service.id === serviceId)
43 if (eService) {
44 arrPrice.push(...getServicePrices(eService))
45 }
46 }
47 })
48 }
49
50 let serviceMinPrice = arrPrice.reduce((prev, curr) => {
51 return curr < prev ? curr : prev
52 }, arrPrice[0])
53
54 let serviceMaxPrice = arrPrice.reduce((prev, curr) => {
55 return curr > prev ? curr : prev
56 }, arrPrice[0])
57
58 let price =
59 serviceMinPrice !== serviceMaxPrice
60 ? `${useFormattedPrice(serviceMinPrice, !globalSettings.payments.hideCurrencySymbolFrontend)} - ${useFormattedPrice(serviceMaxPrice, !globalSettings.payments.hideCurrencySymbolFrontend)}`
61 : useFormattedPrice(serviceMinPrice, !globalSettings.payments.hideCurrencySymbolFrontend)
62
63 return Object.assign({}, service, {
64 price: employeeService.price,
65 formatedPrice: price,
66 minCapacity: employeeService.minCapacity,
67 maxCapacity: employeeService.maxCapacity,
68 customPricing: employeeService.customPricing,
69 isServiceFree: serviceMinPrice === 0 && serviceMaxPrice === 0,
70 })
71 }
72
73 function useChangedBookingPrice(appointment, savedAppointment, booking, service) {
74 let isChangedBookingService =
75 savedAppointment && savedAppointment.serviceId !== appointment.serviceId
76
77 let savedBooking =
78 savedAppointment && booking.id
79 ? savedAppointment.bookings.find((i) => i.id === booking.id)
80 : null
81
82 let isChangedBookingPersons =
83 savedBooking &&
84 savedBooking.persons !== booking.persons &&
85 service.customPricing.enabled === 'person'
86
87 let isChangedBookingDuration =
88 savedBooking &&
89 (booking.duration === null ? service.duration : booking.duration) !== savedBooking.duration &&
90 service.customPricing.enabled === 'duration'
91
92 return isChangedBookingService || isChangedBookingPersons || isChangedBookingDuration
93 }
94
95 function useAppointmentServicePrice(service, persons, duration, price = null) {
96 if (service.customPricing.enabled === 'duration' && duration in service.customPricing.durations) {
97 return service.customPricing.durations[duration].price
98 } else if (service.customPricing.enabled === 'person') {
99 let lastRange = Object.keys(service.customPricing.persons)[
100 Object.keys(service.customPricing.persons).length - 1
101 ]
102
103 for (let range in service.customPricing.persons) {
104 if (
105 persons >= service.customPricing.persons[range].from &&
106 (range !== lastRange ? persons <= parseInt(range) : true)
107 ) {
108 return service.customPricing.persons[range].price
109 }
110 }
111 } else if (price !== null && service.customPricing.enabled === 'period') {
112 return price
113 }
114
115 return service.price
116 }
117
118 function useAppointmentServiceAmount(employeeService, persons, duration, price) {
119 return (
120 useAppointmentServicePrice(employeeService, persons, duration, price) *
121 (employeeService.aggregatedPrice ? persons : 1)
122 )
123 }
124
125 function useAppointmentAmountData(store, item, coupon, couponLimit) {
126 let instantTotalAmount = 0
127
128 let instantTotalServiceAmount = 0
129
130 let instantTotalExtrasAmount = 0
131
132 let instantDiscountAmount = 0
133
134 let instantTaxAmount = 0
135
136 let instantDepositAmount = 0
137
138 let totalAmount = 0
139
140 let totalServiceAmount = 0
141
142 let totalExtrasAmount = 0
143
144 let discountAmount = 0
145
146 let taxAmount = 0
147
148 let depositAmount = 0
149
150 let useFullAmount = store.getters['booking/getPaymentDeposit']
151
152 let service = store.getters['entities/getService'](item.serviceId)
153
154 let applyCoupon = couponLimit > 0 && coupon.servicesIds.indexOf(service.id) !== -1
155
156 let instantPaymentCount = 1
157
158 if (service.recurringPayment) {
159 instantPaymentCount =
160 service.recurringPayment > item.services[item.serviceId].list.length
161 ? item.services[item.serviceId].list.length
162 : service.recurringPayment
163 }
164
165 let appliedCoupon = false
166
167 let servicesPrices = {}
168
169 let prepaidCount = 0
170
171 let totalCount = 0
172
173 item.services[item.serviceId].list.forEach((appointment, index) => {
174 let employeeService = useEmployeeService(store, item.serviceId, appointment.providerId)
175
176 let amountData = useAppointmentBookingAmountData(
177 store,
178 {
179 price: useAppointmentServicePrice(
180 employeeService,
181 appointment.persons,
182 appointment.duration,
183 appointment.price,
184 ),
185 persons: appointment.persons,
186 aggregatedPrice: service.aggregatedPrice,
187 extras: appointment.extras,
188 serviceId: item.serviceId,
189 coupon: applyCoupon && couponLimit > 0 ? coupon : null,
190 },
191 true,
192 )
193
194 if (applyCoupon && couponLimit > 0) {
195 appliedCoupon = true
196
197 couponLimit--
198 }
199
200 let appointmentTotalAmount = amountData.total
201
202 let appointmentDiscountAmount = amountData.discount
203
204 let appointmentTaxAmount = amountData.tax
205
206 totalExtrasAmount += amountData.total - amountData.bookable
207
208 totalServiceAmount += amountData.bookable
209
210 let appointmentDepositAmount = 0
211
212 let servicePrice = useAppointmentServicePrice(
213 employeeService,
214 appointment.persons,
215 appointment.duration,
216 appointment.price,
217 )
218
219 servicesPrices[servicePrice] = !(servicePrice in servicesPrices)
220 ? 1
221 : servicesPrices[servicePrice] + 1
222
223 if (service.depositPayment !== 'disabled' && (useFullAmount ? !service.fullPayment : true)) {
224 switch (service.depositPayment) {
225 case 'fixed':
226 appointmentDepositAmount =
227 (service.depositPerPerson && service.aggregatedPrice ? appointment.persons : 1) *
228 service.deposit
229
230 break
231
232 case 'percentage':
233 appointmentDepositAmount = useRoundAmount(
234 usePercentageAmount(
235 appointmentTotalAmount - appointmentDiscountAmount + appointmentTaxAmount,
236 service.deposit,
237 ),
238 )
239
240 break
241 }
242 }
243
244 totalAmount += appointmentTotalAmount
245
246 discountAmount += appointmentDiscountAmount
247
248 taxAmount += appointmentTaxAmount
249
250 depositAmount += appointmentDepositAmount
251
252 totalCount++
253
254 if (index < instantPaymentCount) {
255 instantTotalAmount = totalAmount
256
257 instantTotalServiceAmount = totalServiceAmount
258
259 instantTotalExtrasAmount = totalExtrasAmount
260
261 instantDiscountAmount = discountAmount
262
263 instantTaxAmount = taxAmount
264
265 instantDepositAmount = depositAmount
266
267 prepaidCount++
268 }
269 })
270
271 return {
272 serviceId: service.id,
273 postpaid: {
274 totalAmount: totalAmount - instantTotalAmount,
275 totalServiceAmount: totalServiceAmount - instantTotalServiceAmount,
276 totalExtrasAmount: totalExtrasAmount - instantTotalExtrasAmount,
277 discountAmount: discountAmount - instantDiscountAmount,
278 taxAmount: taxAmount - instantTaxAmount,
279 depositAmount: 0,
280 count: totalCount - prepaidCount,
281 },
282 prepaid: {
283 totalAmount: instantTotalAmount,
284 totalServiceAmount: instantTotalServiceAmount,
285 totalExtrasAmount: instantTotalExtrasAmount,
286 discountAmount: instantDiscountAmount,
287 taxAmount: instantTaxAmount,
288 depositAmount: instantDepositAmount,
289 count: prepaidCount,
290 },
291 appliedCoupon: appliedCoupon,
292 couponLimit: couponLimit,
293 servicesPrices: servicesPrices,
294 }
295 }
296
297 function useAppointmentBookingAmountData(store, appointment, includedTaxInTotal) {
298 let serviceTax = null
299
300 let excludedServiceTax = settings.payments.taxes.excluded
301
302 let enabledServiceTax = settings.payments.taxes.enabled
303
304 if ('tax' in appointment) {
305 serviceTax = appointment.tax && appointment.tax.length ? appointment.tax[0] : null
306
307 excludedServiceTax = serviceTax ? serviceTax.excluded : excludedServiceTax
308
309 enabledServiceTax = serviceTax !== null
310 } else if (enabledServiceTax) {
311 serviceTax = useEntityTax(store, appointment.serviceId, 'service')
312 }
313
314 let serviceAmount = (appointment.aggregatedPrice ? appointment.persons : 1) * appointment.price
315
316 let appointmentServiceAmount = 0
317
318 let appointmentTotalAmount = 0
319
320 let appointmentDiscountAmount = 0
321
322 let appointmentTaxAmount = 0
323
324 if (appointment.coupon) {
325 appointmentTotalAmount = serviceAmount
326
327 appointmentServiceAmount = serviceAmount
328
329 if (enabledServiceTax && serviceTax && !excludedServiceTax) {
330 serviceAmount = useTaxedAmount(serviceAmount, serviceTax)
331 }
332
333 let serviceDiscountAmount = appointment.coupon.discount
334 ? usePercentageAmount(serviceAmount, appointment.coupon.discount)
335 : 0
336
337 let serviceDiscountedAmount = serviceAmount - serviceDiscountAmount
338
339 serviceAmount = serviceDiscountedAmount
340
341 let deduction = appointment.coupon.deduction
342
343 let serviceDeductionAmount = 0
344
345 if (serviceDiscountedAmount > 0 && deduction > 0) {
346 serviceDeductionAmount =
347 serviceDiscountedAmount >= deduction ? deduction : serviceDiscountedAmount
348
349 serviceAmount = serviceDiscountedAmount - serviceDeductionAmount
350
351 deduction = serviceDiscountedAmount >= deduction ? 0 : deduction - serviceDiscountedAmount
352 }
353
354 if (enabledServiceTax && serviceTax && excludedServiceTax) {
355 appointmentTaxAmount = useTaxAmount(serviceTax, serviceAmount)
356 } else if (enabledServiceTax && serviceTax && !excludedServiceTax) {
357 serviceAmount = useTaxedAmount(
358 (appointment.aggregatedPrice ? appointment.persons : 1) * appointment.price,
359 serviceTax,
360 )
361
362 let serviceTaxAmount = useTaxAmount(
363 serviceTax,
364 serviceAmount - serviceDiscountAmount - serviceDeductionAmount,
365 )
366
367 if (includedTaxInTotal) {
368 appointmentTotalAmount = serviceAmount + serviceTaxAmount
369
370 appointmentServiceAmount = serviceAmount + serviceTaxAmount
371 } else {
372 appointmentTotalAmount = serviceAmount
373
374 appointmentServiceAmount = serviceAmount
375
376 appointmentTaxAmount = serviceTaxAmount
377 }
378 }
379
380 appointmentDiscountAmount = serviceDiscountAmount + serviceDeductionAmount
381
382 appointment.extras.forEach((selectedExtra) => {
383 let extraTax = null
384
385 let excludedExtraTax = settings.payments.taxes.excluded
386
387 let enabledExtraTax = settings.payments.taxes.enabled
388
389 if ('tax' in selectedExtra) {
390 extraTax = selectedExtra.tax && selectedExtra.tax.length ? selectedExtra.tax[0] : null
391
392 excludedExtraTax = extraTax ? extraTax.excluded : excludedExtraTax
393
394 enabledExtraTax = extraTax !== null
395 } else if (enabledExtraTax) {
396 extraTax = useEntityTax(store, selectedExtra.extraId, 'extra')
397 }
398
399 let extraAggregatedPrice =
400 selectedExtra.aggregatedPrice === null
401 ? appointment.aggregatedPrice
402 : selectedExtra.aggregatedPrice
403
404 let extraAmount = useExtraAmount(selectedExtra, extraAggregatedPrice, appointment.persons)
405
406 let extraTotalAmount = extraAmount
407
408 if (enabledExtraTax && extraTax && !excludedExtraTax) {
409 extraAmount = useTaxedAmount(extraAmount, extraTax)
410 }
411
412 let extraDiscountAmount = appointment.coupon.discount
413 ? usePercentageAmount(extraAmount, appointment.coupon.discount)
414 : 0
415
416 let extraDiscountedAmount = extraAmount - extraDiscountAmount
417
418 extraAmount = extraDiscountedAmount
419
420 let extraDeductionAmount = 0
421
422 if (extraDiscountedAmount > 0 && deduction > 0) {
423 extraDeductionAmount =
424 extraDiscountedAmount >= deduction ? deduction : extraDiscountedAmount
425
426 extraAmount = extraDiscountedAmount - extraDeductionAmount
427
428 deduction = extraDiscountedAmount >= deduction ? 0 : deduction - extraDiscountedAmount
429 }
430
431 if (enabledExtraTax && extraTax && excludedExtraTax) {
432 appointmentTaxAmount += useTaxAmount(extraTax, extraAmount)
433 } else if (enabledExtraTax && extraTax && !excludedExtraTax) {
434 extraAmount = useTaxedAmount(
435 useExtraAmount(selectedExtra, extraAggregatedPrice, appointment.persons),
436 extraTax,
437 )
438
439 let extraTaxAmount = useTaxAmount(
440 extraTax,
441 extraAmount - extraDiscountAmount - extraDeductionAmount,
442 )
443
444 if (includedTaxInTotal) {
445 extraTotalAmount = extraAmount + extraTaxAmount
446 } else {
447 extraTotalAmount = extraAmount
448
449 appointmentTaxAmount += extraTaxAmount
450 }
451 } else if (enabledExtraTax && !extraTax && !excludedExtraTax) {
452 extraTotalAmount = useExtraAmount(selectedExtra, extraAggregatedPrice, appointment.persons)
453 }
454
455 appointmentTotalAmount += extraTotalAmount
456
457 appointmentDiscountAmount += extraDiscountAmount + extraDeductionAmount
458 })
459 } else {
460 if (enabledServiceTax && serviceTax && excludedServiceTax) {
461 appointmentTaxAmount = useTaxAmount(serviceTax, serviceAmount)
462 } else if (enabledServiceTax && serviceTax && !excludedServiceTax && !includedTaxInTotal) {
463 serviceAmount = useTaxedAmount(
464 (appointment.aggregatedPrice ? appointment.persons : 1) * appointment.price,
465 serviceTax,
466 )
467
468 appointmentTaxAmount = useTaxAmount(serviceTax, serviceAmount)
469 }
470
471 appointmentTotalAmount = serviceAmount
472
473 appointmentServiceAmount = serviceAmount
474
475 appointment.extras.forEach((selectedExtra) => {
476 let extraAggregatedPrice =
477 selectedExtra.aggregatedPrice === null
478 ? appointment.aggregatedPrice
479 : selectedExtra.aggregatedPrice
480
481 let extraAmount = useExtraAmount(selectedExtra, extraAggregatedPrice, appointment.persons)
482
483 let extraTax = null
484
485 let excludedExtraTax = settings.payments.taxes.excluded
486
487 let enabledExtraTax = settings.payments.taxes.enabled
488
489 if ('tax' in selectedExtra) {
490 extraTax = selectedExtra.tax && selectedExtra.tax.length ? selectedExtra.tax[0] : null
491
492 excludedExtraTax = extraTax ? extraTax.excluded : excludedExtraTax
493
494 enabledExtraTax = extraTax !== null
495 } else if (enabledExtraTax) {
496 extraTax = useEntityTax(store, selectedExtra.extraId, 'extra')
497 }
498
499 if (enabledExtraTax && extraTax && excludedExtraTax) {
500 appointmentTaxAmount += useTaxAmount(extraTax, extraAmount)
501 } else if (enabledExtraTax && extraTax && !excludedExtraTax && !includedTaxInTotal) {
502 extraAmount = useTaxedAmount(
503 useExtraAmount(selectedExtra, extraAggregatedPrice, appointment.persons),
504 extraTax,
505 )
506
507 appointmentTaxAmount += useTaxAmount(extraTax, extraAmount)
508 }
509
510 appointmentTotalAmount += extraAmount
511 })
512 }
513
514 return {
515 total: appointmentTotalAmount,
516 bookable: appointmentServiceAmount,
517 discount: appointmentDiscountAmount,
518 tax: appointmentTaxAmount,
519 wcTax: appointment.wcTax,
520 }
521 }
522
523 function useAppointmentsAmountInfo(store) {
524 let amountsInfo = []
525
526 let coupon = store.getters['booking/getCoupon']
527
528 let couponLimit = coupon && coupon.limit ? coupon.limit : 0
529
530 useCart(store).forEach((item) => {
531 let amountData = useAppointmentAmountData(store, item, coupon, couponLimit)
532
533 couponLimit = amountData.couponLimit
534
535 delete amountData.couponLimit
536
537 amountsInfo.push(amountData)
538 })
539
540 return amountsInfo
541 }
542
543 function useCapacity(employeeServices) {
544 let options = {
545 availability: false,
546 min: 0,
547 max: 0,
548 }
549
550 let serviceMinCapacity = 0
551
552 if (employeeServices.length && employeeServices.length > 1) {
553 employeeServices.forEach((service) => {
554 serviceMinCapacity = service.minCapacity
555
556 options.availability =
557 service.bringingAnyone &&
558 service.maxCapacity > 1 &&
559 (service.maxExtraPeople === null || service.maxExtraPeople > 0)
560
561 if (service.maxCapacity > options.max || options.max === 0) {
562 options.max =
563 service.maxExtraPeople !== null ? service.maxExtraPeople + 1 : service.maxCapacity
564 }
565
566 if (options.min < service.minCapacity) {
567 options.min = settings.appointments.allowBookingIfNotMin ? 1 : service.minCapacity
568 }
569 })
570 } else if (employeeServices.length && employeeServices.length === 1) {
571 let service = employeeServices[0]
572
573 serviceMinCapacity = service.minCapacity
574
575 options.availability =
576 service.bringingAnyone &&
577 service.maxCapacity > 1 &&
578 (service.maxExtraPeople === null || service.maxExtraPeople > 0)
579 options.min = settings.appointments.allowBookingIfNotMin ? 1 : service.minCapacity
580 options.max =
581 service.maxExtraPeople !== null && service.maxExtraPeople < service.maxCapacity
582 ? service.maxExtraPeople + 1
583 : service.maxCapacity
584 }
585
586 if (settings.appointments.openedBookingAfterMin) {
587 options.min = serviceMinCapacity
588 }
589
590 let additionalPeople = settings.appointments.bringingAnyoneLogic === 'additional'
591
592 options.max = options.max > 1 ? options.max - (additionalPeople ? 1 : 0) : options.max
593 options.min = options.min > 0 ? options.min - (additionalPeople ? 1 : 0) : options.min
594
595 return options
596 }
597
598 function usePaymentError(store, message) {
599 store.commit('booking/setError', message)
600 }
601
602 function usePrepaidPrice(store) {
603 let type = store.getters['bookableType/getType']
604 ? store.getters['bookableType/getType']
605 : store.getters['booking/getBookableType']
606
607 let entity = null
608
609 switch (type) {
610 case 'appointment':
611 let appointmentPrice = 0
612
613 useCart(store).forEach((item) => {
614 if ((item.serviceId && item.serviceId in item.services) || item.packageId) {
615 entity = store.getters['entities/getService'](item.serviceId)
616
617 appointmentPrice += useAppointmentsTotalAmount(
618 store,
619 entity,
620 useAppointmentsPayments(store, item.serviceId, item.services[item.serviceId].list)
621 .prepaid,
622 )
623 }
624 })
625
626 return appointmentPrice
627 case 'package':
628 let price = 0
629
630 if (useCart(store)[0].packageId) {
631 entity = store.getters['entities/getPackage'](useCart(store)[0].packageId)
632
633 price = entity.price
634 }
635
636 return price
637 case 'event':
638 entity = store.getters['eventEntities/getEvent'](
639 store.getters['eventBooking/getSelectedEventId'],
640 )
641
642 let eventPrice = 0
643
644 if (entity.customPricing) {
645 let tickets = store.getters['tickets/getTicketsData']
646
647 tickets.forEach((t) => {
648 eventPrice += t.price * t.persons
649 })
650
651 return eventPrice
652 }
653
654 let persons = store.getters['persons/getPersons']
655
656 return entity.price * persons
657 }
658 }
659
660 function useDuration(serviceDuration, extras) {
661 let duration = serviceDuration
662
663 extras.forEach((extra) => {
664 duration += extra.duration * extra.quantity
665 })
666
667 return duration
668 }
669
670 function useCalendarEvents(slots, waitingSlots) {
671 let calendarSlotsValues = []
672
673 let dates = Object.keys(slots)
674
675 if (waitingSlots) {
676 dates = [...new Set([...dates, ...Object.keys(waitingSlots)])]
677 }
678
679 useSortedDateStrings(dates).forEach((date) => {
680 calendarSlotsValues.push({
681 title: 'e',
682 start: date,
683 display: 'background',
684 extendedProps: {
685 slotsTotal: 100,
686 slotsAvailable: 1,
687 slots: date in slots ? slots[date] : {},
688 },
689 })
690 })
691
692 return calendarSlotsValues
693 }
694
695 function useServices(store) {
696 let type = store.getters['booking/getBookableType']
697
698 let services = null
699
700 switch (type) {
701 case 'appointment': {
702 services = store.getters['entities/getServices']
703
704 break
705 }
706
707 case 'package': {
708 let packages = store.getters['entities/getPackages']
709
710 let packagesServices = {}
711
712 packages.forEach((pack) => {
713 pack.bookable.forEach((book) => {
714 packagesServices[book.service.id] = book.service
715 })
716 })
717
718 services = Object.values(packagesServices)
719
720 break
721 }
722 }
723
724 return services
725 }
726
727 function useBusySlots(store) {
728 let cartItem = useCartItem(store)
729
730 let activeAppointment = cartItem.services[cartItem.serviceId].list[cartItem.index]
731
732 let busySlots = store.getters['booking/getMultipleAppointmentsOccupied']
733
734 return busySlots[activeAppointment.date] ? Object.keys(busySlots[activeAppointment.date]) : []
735 }
736
737 function isSlotOccupiedBySelection(inspectedStart, inspectedEnd, occupiedStart, occupiedEnd) {
738 return !(inspectedEnd <= occupiedStart || inspectedStart >= occupiedEnd)
739 }
740
741 function useAvailableSlots(store) {
742 let cart = store.getters['booking/getAllMultipleAppointments']
743
744 let cartIndex = store.getters['booking/getCartItemIndex']
745
746 let activeAppointment =
747 cart[cartIndex].services[cart[cartIndex].serviceId].list[cart[cartIndex].index]
748
749 let services = useServices(store)
750
751 if (activeAppointment.date) {
752 let selectedSlots = []
753
754 cart.forEach((cartItem, selectedCartIndex) => {
755 for (let serviceId in cartItem.services) {
756 let service = services.find((i) => i.id === parseInt(serviceId))
757
758 cartItem.services[serviceId].list.forEach((i, selectedListIndex) => {
759 let isCurrentAppointment =
760 selectedCartIndex === parseInt(cartIndex) &&
761 selectedListIndex === parseInt(cart[cartIndex].index) &&
762 (cartItem.packageId ? parseInt(cartItem.serviceId) === parseInt(serviceId) : true)
763
764 if (i.date && i.date === activeAppointment.date && i.time && !isCurrentAppointment) {
765 selectedSlots.push({
766 providerId: i.providerId,
767 time: i.time,
768 duration:
769 i.duration +
770 i.extras
771 .filter((e) => e.quantity && e.duration)
772 .map((e) => e.duration)
773 .reduce((fullDuration, duration) => fullDuration + duration, 0),
774 timeAfter: service.timeAfter,
775 timeBefore: service.timeBefore,
776 })
777 }
778 })
779 }
780 })
781
782 let targetService = services.find((i) => i.id === cart[cartIndex].serviceId)
783
784 let targetDuration =
785 activeAppointment.duration +
786 activeAppointment.extras
787 .filter((e) => e.quantity && e.duration)
788 .map((e) => e.duration)
789 .reduce((fullDuration, duration) => fullDuration + duration, 0)
790
791 // Date may only exist in waiting list slots (not in regular slots) — guard against undefined
792 if (!(activeAppointment.date in cart[cartIndex].services[cart[cartIndex].serviceId].slots)) {
793 return []
794 }
795
796 let defaultSlots = Object.keys(
797 cart[cartIndex].services[cart[cartIndex].serviceId].slots[activeAppointment.date],
798 )
799
800 let availableSlots = {}
801
802 for (let i = 0; i < defaultSlots.length; i++) {
803 let slotEmployeesIds = cart[cartIndex].services[cart[cartIndex].serviceId].slots[
804 activeAppointment.date
805 ][defaultSlots[i]].map((j) => parseInt(j.e))
806
807 let targetSlotSeconds = useTimeInSeconds(defaultSlots[i])
808
809 let occupiedSlotsCounter = 0
810
811 let occupiedSlotsEmployeesIds = []
812
813 for (let j = 0; j < selectedSlots.length; j++) {
814 let selectedSlotSeconds = useTimeInSeconds(selectedSlots[j].time)
815
816 if (
817 isSlotOccupiedBySelection(
818 targetSlotSeconds - targetService.timeBefore,
819 targetSlotSeconds + targetDuration + targetService.timeAfter,
820 selectedSlotSeconds - selectedSlots[j].timeBefore,
821 selectedSlotSeconds + selectedSlots[j].duration + selectedSlots[j].timeAfter,
822 )
823 ) {
824 occupiedSlotsCounter++
825
826 if (selectedSlots[j].providerId) {
827 occupiedSlotsEmployeesIds.push(selectedSlots[j].providerId)
828 }
829 }
830 }
831
832 if (
833 occupiedSlotsEmployeesIds.length
834 ? slotEmployeesIds.some((value) => !occupiedSlotsEmployeesIds.includes(value))
835 : occupiedSlotsCounter <
836 cart[cartIndex].services[cart[cartIndex].serviceId].slots[activeAppointment.date][
837 defaultSlots[i]
838 ].length
839 ) {
840 availableSlots[defaultSlots[i]] = targetSlotSeconds
841 }
842 }
843
844 return useSortedDateStrings(Object.keys(availableSlots))
845 }
846
847 return 'slots' in activeAppointment ? activeAppointment.slots : []
848 }
849
850 function useFillAppointments(store) {
851 let cartItem = useCartItem(store)
852
853 if (
854 !cartItem.packageId &&
855 Object.keys(cartItem.services).length === 1 &&
856 cartItem.services[cartItem.serviceId].list.length === 1
857 ) {
858 let booking = cartItem.services[cartItem.serviceId].list[0]
859
860 if (!booking.providerId && booking.date && booking.time) {
861 // Check if waiting list slot with pre-selected provider
862 let isWaitingListSlot = store.getters['appointmentWaitingListOptions/getIsWaitingListSlot']
863 let waitingListOptions = store.getters['appointmentWaitingListOptions/getWaitingListOptions']
864
865 if (isWaitingListSlot && waitingListOptions.selectedProviderId) {
866 // Use the provider ID that was stored when the waiting list slot was selected
867 booking.providerId = waitingListOptions.selectedProviderId
868 } else {
869 // Derive employeesIds. If a waiting list slot is selected (not present in regular slots),
870 // pull provider data from occupied structure instead of slots.
871 let slotDataArray = null
872
873 if (
874 isWaitingListSlot &&
875 booking.date &&
876 booking.time &&
877 (!(booking.date in cartItem.services[cartItem.serviceId].slots) ||
878 !(booking.time in cartItem.services[cartItem.serviceId].slots[booking.date])) &&
879 booking.date in cartItem.services[cartItem.serviceId].occupied &&
880 booking.time in cartItem.services[cartItem.serviceId].occupied[booking.date]
881 ) {
882 // Waiting list slot: use occupied data (fully booked original appointment rows)
883 const serviceId = parseInt(cartItem.serviceId)
884 slotDataArray = cartItem.services[cartItem.serviceId].occupied[booking.date][
885 booking.time
886 ].filter((p) => p.s === serviceId)
887 } else if (
888 booking.date &&
889 booking.time &&
890 booking.date in cartItem.services[cartItem.serviceId].slots &&
891 booking.time in cartItem.services[cartItem.serviceId].slots[booking.date]
892 ) {
893 // Regular slot
894 slotDataArray = cartItem.services[cartItem.serviceId].slots[booking.date][booking.time]
895 } else {
896 slotDataArray = []
897 }
898
899 let occupiedSlotEmployeesIds = !isWaitingListSlot
900 ? getOccupiedSlotEmployeesIds(store, cartItem.serviceId, booking)
901 : []
902
903 let employeesIds = slotDataArray
904 .map((i) => i.e)
905 .filter((v, i, a) => a.indexOf(v) === i && !occupiedSlotEmployeesIds.includes(v))
906
907 if (!employeesIds.length) {
908 employeesIds = slotDataArray.map((i) => i.e).filter((v, i, a) => a.indexOf(v) === i)
909 }
910
911 if (settings.roles.limitPerEmployee.enabled) {
912 let chosenEmployees = store.getters['booking/getAllMultipleAppointments'].map(
913 (a) => Object.values(a.services)[0].list[0],
914 )
915 let appCount = store.getters['booking/getMultipleAppointmentsAppCount'](
916 cartItem.serviceId,
917 )
918 let result = checkLimitPerEmployee(
919 employeesIds,
920 0,
921 [],
922 booking,
923 appCount,
924 chosenEmployees,
925 cartItem.serviceId,
926 )
927 if (result.bookingFailed !== null) {
928 return { booking: result.bookingFailed, serviceId: parseInt(cartItem.serviceId) }
929 }
930 employeesIds = result.employeeIds
931 }
932
933 if (settings.appointments.employeeSelection === 'random') {
934 booking.providerId = employeesIds[Math.floor(Math.random() * employeesIds.length + 1) - 1]
935 } else {
936 employeesIds = sortForEmployeeSelection(store, employeesIds, cartItem.serviceId)
937 booking.providerId = employeesIds[0]
938 }
939 }
940 }
941
942 if (!booking.locationId && booking.date && booking.time) {
943 let isWaitingListSlot = store.getters['appointmentWaitingListOptions/getIsWaitingListSlot']
944
945 let slotRows = null
946 if (
947 isWaitingListSlot &&
948 (!(booking.date in cartItem.services[cartItem.serviceId].slots) ||
949 !(booking.time in cartItem.services[cartItem.serviceId].slots[booking.date])) &&
950 booking.date in cartItem.services[cartItem.serviceId].occupied &&
951 booking.time in cartItem.services[cartItem.serviceId].occupied[booking.date]
952 ) {
953 // Waiting list slot: derive possible locations from occupied data
954 const serviceId = parseInt(cartItem.serviceId)
955 slotRows = cartItem.services[cartItem.serviceId].occupied[booking.date][
956 booking.time
957 ].filter((p) => p.s === serviceId)
958 } else if (
959 booking.date in cartItem.services[cartItem.serviceId].slots &&
960 booking.time in cartItem.services[cartItem.serviceId].slots[booking.date]
961 ) {
962 slotRows = cartItem.services[cartItem.serviceId].slots[booking.date][booking.time]
963 } else {
964 slotRows = []
965 }
966
967 let locationsIds = slotRows.filter((r) => r.e === booking.providerId).map((r) => r.l)
968
969 booking.locationId = locationsIds.length
970 ? getPreferredEntityId(
971 cartItem.services[cartItem.serviceId].slots[booking.date] || {},
972 booking.date in cartItem.services[cartItem.serviceId].occupied
973 ? cartItem.services[cartItem.serviceId].occupied[booking.date]
974 : {},
975 booking.time,
976 booking.providerId,
977 locationsIds,
978 'l',
979 )
980 : null
981 }
982
983 let slots = store.getters['booking/getMultipleAppointmentsSlots']
984
985 let existingApp =
986 booking.date in slots &&
987 booking.time in slots[booking.date] &&
988 slots[booking.date][booking.time].length > 0
989 ? slots[booking.date][booking.time].find((s) => s.e === booking.providerId)
990 : null
991
992 store.commit(
993 'booking/setMultipleAppointmentsExistingApp',
994 existingApp && existingApp.c && existingApp.c > 0,
995 )
996
997 booking.price = existingApp && 'p' in existingApp ? existingApp.p : null
998 } else {
999 let chosenEmployees = []
1000
1001 for (let serviceId of Object.keys(cartItem.services)) {
1002 if (
1003 cartItem.services[serviceId].list.length &&
1004 cartItem.services[serviceId].list.filter((i) => i.date && i.time).length
1005 ) {
1006 let bookingFailed = setPreferredEntitiesData(
1007 cartItem.services[serviceId],
1008 store,
1009 serviceId,
1010 chosenEmployees,
1011 )
1012
1013 if (bookingFailed !== null) {
1014 return { booking: bookingFailed, serviceId: parseInt(serviceId) }
1015 }
1016 chosenEmployees = chosenEmployees.concat(
1017 cartItem.services[serviceId].list.map((l) => {
1018 return {
1019 date: l.date,
1020 providerId: l.providerId,
1021 serviceId: serviceId,
1022 existingApp: 'existingApp' in l && l.existingApp,
1023 price: l.price,
1024 }
1025 }),
1026 )
1027 }
1028 }
1029 }
1030
1031 let activeItemServices = cartItem.services
1032
1033 Object.keys(activeItemServices).forEach((serviceId) => {
1034 if (activeItemServices[serviceId].list.filter((i) => i.date && i.time).length) {
1035 activeItemServices[serviceId].list.forEach((booking) => {
1036 if (booking.date && booking.time) {
1037 setProviderServicePrice(store, booking.providerId, serviceId, booking.persons)
1038 }
1039 })
1040 }
1041 })
1042
1043 return null
1044 }
1045
1046 function getOccupiedSlotEmployeesIds(store, serviceId, booking) {
1047 let cart = useCart(store)
1048
1049 let services = useServices(store)
1050
1051 let targetService = services.find((i) => i.id === parseInt(serviceId))
1052
1053 let targetSlotSeconds = useTimeInSeconds(booking.time)
1054
1055 let targetDuration =
1056 booking.duration +
1057 booking.extras
1058 .filter((e) => e.quantity && e.duration)
1059 .map((e) => e.duration)
1060 .reduce((fullDuration, duration) => fullDuration + duration, 0)
1061
1062 let occupiedSlotEmployeesIds = []
1063
1064 cart.forEach((item) => {
1065 item.services[item.serviceId].list.forEach((slot) => {
1066 if (slot.providerId && slot.date && slot.time && booking.date === slot.date) {
1067 let selectedService = services.find((i) => i.id === item.serviceId)
1068
1069 let selectedSlotSeconds = useTimeInSeconds(slot.time)
1070
1071 let selectedDuration =
1072 slot.duration +
1073 slot.extras
1074 .filter((e) => e.quantity && e.duration)
1075 .map((e) => e.duration)
1076 .reduce((fullDuration, duration) => fullDuration + duration, 0)
1077
1078 if (
1079 isSlotOccupiedBySelection(
1080 targetSlotSeconds - targetService.timeBefore,
1081 targetSlotSeconds + targetDuration + targetService.timeAfter,
1082 selectedSlotSeconds - selectedService.timeBefore,
1083 selectedSlotSeconds + selectedDuration + selectedService.timeAfter,
1084 )
1085 ) {
1086 occupiedSlotEmployeesIds.push(slot.providerId)
1087 }
1088 }
1089 })
1090 })
1091
1092 return [...new Set(occupiedSlotEmployeesIds)].map((i) => parseInt(i))
1093 }
1094
1095 function setProviderServicePrice(store, employeeId, serviceId, persons) {
1096 let employee = store.getters['entities/getUnfilteredEmployee'](employeeId)
1097
1098 let service = employee.serviceList.find((i) => i.id === parseInt(serviceId))
1099
1100 service.duration = store.getters['booking/getDuration']
1101
1102 service.price = useAppointmentServicePrice(service, persons, store.getters['booking/getDuration'])
1103 }
1104
1105 function useAppointmentsAmount(store, service, appointments) {
1106 let amount = 0
1107
1108 appointments.forEach((appointment) => {
1109 amount += useAppointmentServiceAmount(
1110 useEmployeeService(store, service.id, appointment.providerId),
1111 appointment.persons,
1112 appointment.duration,
1113 appointment.price,
1114 )
1115 })
1116
1117 return amount
1118 }
1119
1120 function useExtraAmount(extra, serviceAggregatedPrice, persons) {
1121 let extraAggregatedPrice =
1122 extra.aggregatedPrice === null ? serviceAggregatedPrice : extra.aggregatedPrice
1123
1124 return extra.price * extra.quantity * (extraAggregatedPrice ? persons : 1)
1125 }
1126
1127 function useAppointmentExtraAmount(service, selectedExtra, persons) {
1128 let extra = service.extras.find((item) => item.id === parseInt(selectedExtra.extraId))
1129
1130 if (extra) {
1131 let extraAggregatedPrice =
1132 extra.aggregatedPrice === null ? service.aggregatedPrice : extra.aggregatedPrice
1133
1134 return extra.price * selectedExtra.quantity * (extraAggregatedPrice ? persons : 1)
1135 }
1136
1137 return 0
1138 }
1139
1140 function useAppointmentExtrasAmount(service, appointments) {
1141 let amount = 0
1142
1143 appointments.forEach((appointment) => {
1144 if (appointment.extras) {
1145 appointment.extras.forEach((selectedExtra) => {
1146 amount += useAppointmentExtraAmount(service, selectedExtra, appointment.persons)
1147 })
1148 }
1149 })
1150
1151 return amount
1152 }
1153
1154 function useAppointmentsTotalAmount(store, service, appointments) {
1155 return (
1156 useAppointmentsAmount(store, service, appointments) +
1157 useAppointmentExtrasAmount(service, appointments)
1158 )
1159 }
1160
1161 function useAppointmentsPayments(store, serviceId, appointments) {
1162 let service = store.getters['entities/getService'](serviceId)
1163
1164 let prepaidCount = 1
1165
1166 if (service.recurringPayment) {
1167 prepaidCount =
1168 service.recurringPayment > appointments.length
1169 ? appointments.length
1170 : service.recurringPayment
1171 }
1172
1173 return {
1174 prepaid: appointments.slice(0, prepaidCount),
1175 postpaid: appointments.slice(prepaidCount),
1176 }
1177 }
1178
1179 function setPreferredEntitiesData(bookings, store, serviceId, chosenEmployees) {
1180 let allEmployeesIds = getAllEntitiesIds(bookings, 'e')
1181
1182 let locationsIds = getAllEntitiesIds(bookings, 'l')
1183
1184 let isSingleEmployee = allEmployeesIds.length === 1
1185
1186 let isSingleLocation = locationsIds.length === 1
1187
1188 let appCount = store.getters['booking/getMultipleAppointmentsAppCount'](serviceId)
1189
1190 for (let bookingIndex = 0; bookingIndex < bookings.list.length; bookingIndex++) {
1191 let booking = bookings.list[bookingIndex]
1192
1193 if (booking.date && booking.time) {
1194 let occupiedSlotEmployeesIds = !booking.providerId
1195 ? getOccupiedSlotEmployeesIds(store, serviceId, booking)
1196 : []
1197
1198 let employeesIds = sortForEmployeeSelection(
1199 store,
1200 allEmployeesIds.filter((x) => !occupiedSlotEmployeesIds.includes(x)),
1201 serviceId,
1202 )
1203
1204 if (!employeesIds.length) {
1205 employeesIds = sortForEmployeeSelection(store, allEmployeesIds, serviceId)
1206 }
1207
1208 if (settings.roles.limitPerEmployee.enabled) {
1209 let result = checkLimitPerEmployee(
1210 employeesIds,
1211 bookingIndex,
1212 bookings.list,
1213 booking,
1214 appCount,
1215 chosenEmployees,
1216 serviceId,
1217 )
1218 if (result.bookingFailed !== null) {
1219 return result.bookingFailed
1220 }
1221 employeesIds = result.employeeIds
1222 }
1223
1224 if (!locationsIds.length && isSingleEmployee) {
1225 booking.providerId = employeesIds[0]
1226
1227 booking.locationId = null
1228 } else if (!locationsIds.length && !isSingleEmployee) {
1229 booking.locationId = null
1230
1231 for (let i = 0; i < employeesIds.length; i++) {
1232 for (let j = 0; j < bookings.slots[booking.date][booking.time].length; j++) {
1233 if (bookings.slots[booking.date][booking.time][j].e === employeesIds[i]) {
1234 booking.providerId = employeesIds[i]
1235
1236 break
1237 }
1238 }
1239 }
1240 } else if (isSingleLocation && isSingleEmployee) {
1241 booking.providerId = employeesIds[0]
1242
1243 booking.locationId = locationsIds[0]
1244 } else if (!isSingleLocation && isSingleEmployee) {
1245 booking.providerId = employeesIds[0]
1246
1247 booking.locationId = getPreferredEntityId(
1248 bookings.slots[booking.date],
1249 booking.date in bookings.occupied ? bookings.occupied[booking.date] : {},
1250 booking.time,
1251 booking.providerId,
1252 locationsIds,
1253 'l',
1254 )
1255 } else if (isSingleLocation && !isSingleEmployee) {
1256 booking.locationId = locationsIds[0]
1257
1258 const preferredProviderId = getPreferredEntityId(
1259 bookings.slots[booking.date],
1260 booking.date in bookings.occupied ? bookings.occupied[booking.date] : {},
1261 booking.time,
1262 booking.locationId,
1263 employeesIds,
1264 'e',
1265 )
1266
1267 booking.providerId = employeesIds.includes(preferredProviderId)
1268 ? preferredProviderId
1269 : employeesIds[0]
1270 } else {
1271 let setEntities = false
1272 outsideLoop: for (let j = 0; j < employeesIds.length; j++) {
1273 for (let i = 0; i < locationsIds.length; i++) {
1274 let isPreferred = isPreferredLocationAndEmployee(
1275 bookings.slots[booking.date],
1276 booking.date in bookings.occupied ? bookings.occupied[booking.date] : {},
1277 booking.time,
1278 locationsIds[i],
1279 employeesIds[j],
1280 )
1281
1282 if (isPreferred) {
1283 booking.providerId = employeesIds[j]
1284
1285 booking.locationId = locationsIds[i]
1286
1287 setEntities = true
1288
1289 break outsideLoop
1290 }
1291 }
1292 }
1293
1294 if (!setEntities) {
1295 outsideLoop2: for (let j = 0; j < employeesIds.length; j++) {
1296 for (let i = 0; i < locationsIds.length; i++) {
1297 for (let k = 0; k < bookings.slots[booking.date][booking.time].length; k++) {
1298 if (
1299 bookings.slots[booking.date][booking.time][k].e === employeesIds[j] &&
1300 bookings.slots[booking.date][booking.time][k].l === locationsIds[i]
1301 ) {
1302 booking.providerId = employeesIds[j]
1303
1304 booking.locationId = locationsIds[i]
1305
1306 break outsideLoop2
1307 }
1308 }
1309 }
1310 }
1311 }
1312 }
1313
1314 store.commit('booking/setMultipleAppointmentsServiceProvider', booking.providerId)
1315
1316 let slots = store.getters['booking/getMultipleAppointmentsSlots']
1317
1318 let existingApp =
1319 booking.date in slots &&
1320 booking.time in slots[booking.date] &&
1321 slots[booking.date][booking.time].length > 0
1322 ? slots[booking.date][booking.time].find((s) => s.e === booking.providerId)
1323 : null
1324
1325 bookings.list[bookingIndex].existingApp = existingApp && existingApp.c && existingApp.c > 0
1326
1327 store.commit('booking/setLastBookedProviderId', {
1328 providerId: booking.providerId,
1329 fromBackend: false,
1330 })
1331
1332 bookings.list[bookingIndex].price = existingApp && 'p' in existingApp ? existingApp.p : null
1333 }
1334 }
1335
1336 return null
1337 }
1338
1339 function getAllEntitiesIds(bookings, index) {
1340 let ids = {}
1341
1342 for (let i = 0; i < bookings.list.length; i++) {
1343 if (bookings.list[i].date && bookings.list[i].time) {
1344 bookings.slots[bookings.list[i].date][bookings.list[i].time].forEach((slotData) => {
1345 if (slotData[index]) {
1346 if (!(slotData[index] in ids)) {
1347 ids[slotData[index]] = 0
1348 }
1349
1350 ids[slotData[index]]++
1351 }
1352 })
1353 }
1354 }
1355
1356 let sortedEntitiesIds = []
1357
1358 Object.keys(ids).forEach((id) => {
1359 sortedEntitiesIds.push({ id: parseInt(id), quantity: ids[id] })
1360 })
1361
1362 sortedEntitiesIds.sort((a, b) => b.quantity - a.quantity)
1363
1364 return sortedEntitiesIds.map((entity) => entity.id)
1365 }
1366
1367 function getPreferredEntityId(
1368 availableSlots,
1369 occupiedSlots,
1370 timeString,
1371 selectedId,
1372 allIds,
1373 targetIndex,
1374 ) {
1375 let searchIndex = targetIndex === 'e' ? 'l' : 'e'
1376
1377 let appointmentsStarts = {}
1378
1379 Object.keys(occupiedSlots).forEach((time) => {
1380 occupiedSlots[time].forEach((slotData) => {
1381 if (slotData[searchIndex] === selectedId) {
1382 appointmentsStarts[useTimeInSeconds(time)] = slotData[targetIndex]
1383 }
1384 })
1385 })
1386
1387 Object.keys(availableSlots).forEach((time) => {
1388 availableSlots[time].forEach((slotData) => {
1389 if (Object.keys(slotData).length >= 3 && slotData[searchIndex] === selectedId) {
1390 appointmentsStarts[useTimeInSeconds(time)] = slotData[targetIndex]
1391 }
1392 })
1393 })
1394
1395 let availableIds = []
1396
1397 // Collect ids from available slots for the specific time; if time not in availableSlots (e.g., waiting list slot),
1398 // fall back to occupied slots data to derive potential ids.
1399 let timeRows =
1400 timeString in availableSlots && Array.isArray(availableSlots[timeString])
1401 ? availableSlots[timeString]
1402 : []
1403
1404 if (!timeRows.length && timeString in occupiedSlots && Array.isArray(occupiedSlots[timeString])) {
1405 timeRows = occupiedSlots[timeString]
1406 }
1407
1408 timeRows.forEach((slotData) => {
1409 if (slotData[searchIndex] === selectedId) {
1410 availableIds.push(slotData[targetIndex])
1411 }
1412 })
1413
1414 if (Object.keys(appointmentsStarts).length) {
1415 let timeInSeconds = useTimeInSeconds(timeString)
1416
1417 let closestSlot = Object.keys(appointmentsStarts).reduce((a, b) => {
1418 return Math.abs(b - timeInSeconds) < Math.abs(a - timeInSeconds) ? b : a
1419 })
1420
1421 if (availableIds.indexOf(appointmentsStarts[closestSlot]) !== -1) {
1422 return appointmentsStarts[closestSlot]
1423 }
1424 }
1425
1426 for (let i = 0; i < allIds.length; i++) {
1427 for (let j = 0; j < timeRows.length; j++) {
1428 if (timeRows[j][searchIndex] === selectedId && allIds[i] === timeRows[j][targetIndex]) {
1429 return timeRows[j][targetIndex]
1430 }
1431 }
1432 }
1433
1434 return targetIndex === 'e' ? availableSlots[timeString][0][targetIndex] : null
1435 }
1436
1437 function isPreferredLocationAndEmployee(
1438 slotsData,
1439 occupiedData,
1440 timeString,
1441 locationId,
1442 employeeId,
1443 ) {
1444 let isEmployeeLocation = false
1445
1446 slotsData[timeString].forEach((slotData) => {
1447 if (slotData.e === employeeId && slotData.l === locationId) {
1448 isEmployeeLocation = true
1449 }
1450 })
1451
1452 // inspect if employee is available on proposed location
1453 if (!isEmployeeLocation) {
1454 return false
1455 }
1456
1457 let appointmentStarts = {
1458 onLocation: {},
1459 offLocation: {},
1460 }
1461
1462 Object.keys(occupiedData).forEach((time) => {
1463 occupiedData[time].forEach((slotData) => {
1464 if (slotData.e === employeeId && slotData.l === locationId) {
1465 appointmentStarts.onLocation[useTimeInSeconds(time)] = slotData.l
1466 } else if (slotData.e === employeeId) {
1467 appointmentStarts.offLocation[useTimeInSeconds(time)] = slotData.l
1468 }
1469 })
1470 })
1471
1472 Object.keys(slotsData).forEach((time) => {
1473 slotsData[time].forEach((slotData) => {
1474 if ('p' in slotData && slotData.e === employeeId && slotData.l === locationId) {
1475 appointmentStarts.onLocation[useTimeInSeconds(time)] = slotData.l
1476 } else if ('p' in slotData && slotData.e === employeeId) {
1477 appointmentStarts.offLocation[useTimeInSeconds(time)] = slotData.l
1478 }
1479 })
1480 })
1481
1482 // inspect if employee has appointments only on proposed location, or has no appointments in that day
1483 if (
1484 (!Object.keys(appointmentStarts.onLocation).length &&
1485 !Object.keys(appointmentStarts.offLocation).length) ||
1486 (Object.keys(appointmentStarts.onLocation).length &&
1487 !Object.keys(appointmentStarts.offLocation).length)
1488 ) {
1489 return true
1490 }
1491
1492 let timeInSeconds = useTimeInSeconds(timeString)
1493
1494 appointmentStarts = Object.assign(appointmentStarts.onLocation, appointmentStarts.offLocation)
1495
1496 let closestTime = Object.keys(appointmentStarts).reduce((a, b) => {
1497 return Math.abs(b - timeInSeconds) < Math.abs(a - timeInSeconds) ? b : a
1498 })
1499
1500 return locationId === appointmentStarts[closestTime]
1501 }
1502
1503 function useCheckingIfAllNotFree(store) {
1504 let preselected = store.getters['entities/getPreselected']
1505
1506 if (preselected.show === 'packages') {
1507 let packages = store.getters['booking/getPackageId']
1508 ? [store.getters['entities/getPackage'](store.getters['booking/getPackageId'])]
1509 : store.getters['entities/getPackages']
1510
1511 return packages.length > 0 && packages.filter((p) => p.price > 0).length === packages.length
1512 }
1513
1514 if (!store.getters['booking/getPackageId']) {
1515 let services = store.getters['booking/getServiceId']
1516 ? [store.getters['entities/getService'](store.getters['booking/getServiceId'])]
1517 : store.getters['entities/getServices']
1518
1519 let nonFreeServices = 0
1520 for (let service of services) {
1521 let employees = store.getters['booking/getEmployeeId']
1522 ? store.getters['entities/getEmployee'](store.getters['booking/getEmployeeId'])
1523 ? [store.getters['entities/getEmployee'](store.getters['booking/getEmployeeId'])]
1524 : []
1525 : store.getters['entities/getEmployees']
1526
1527 let duration = store.getters['booking/getBookingDuration']
1528
1529 let persons = store.getters['booking/getBookingPersons']
1530
1531 let providers = employees.filter((eS) =>
1532 eS.serviceList.find(
1533 (s) =>
1534 s.id === service.id &&
1535 (s.price > 0 ||
1536 (s.customPricing &&
1537 ((s.customPricing.enabled === 'duration' &&
1538 (Object.values(s.customPricing.durations).length ===
1539 Object.values(s.customPricing.durations).filter((cp) => cp.price > 0).length ||
1540 (duration && s.customPricing.durations[duration].price > 0))) ||
1541 (s.customPricing.enabled === 'person' &&
1542 (s.customPricing.persons.length ===
1543 s.customPricing.persons.filter((cp) => cp.price > 0).length ||
1544 (persons &&
1545 (s.customPricing.persons.filter((cp) => cp.range >= persons).length
1546 ? s.customPricing.persons.filter((cp) => cp.range >= persons)[0].price > 0
1547 : s.customPricing.persons[s.customPricing.persons.length - 1].price >
1548 0))))))),
1549 ),
1550 )
1551
1552 if (
1553 providers.length ===
1554 employees.filter((eS) => eS.serviceList.find((s) => s.id === service.id)).length
1555 ) {
1556 nonFreeServices++
1557 } else {
1558 let extras = store.getters['booking/getSelectedExtras'].length
1559 ? store.getters['booking/getSelectedExtras']
1560 : []
1561
1562 if (extras.length > 0 && extras.reduce((partialSum, a) => partialSum + a.price, 0) > 0) {
1563 nonFreeServices++
1564 }
1565 }
1566 }
1567
1568 return services.length > 0 && nonFreeServices === services.length
1569 }
1570
1571 return store.getters['entities/getPackage'](store.getters['booking/getPackageId']).price > 0
1572 }
1573
1574 export {
1575 useCapacity,
1576 useAvailableSlots,
1577 useBusySlots,
1578 useFillAppointments,
1579 useCalendarEvents,
1580 useAppointmentsPayments,
1581 useAppointmentsAmountInfo,
1582 useAppointmentServiceAmount,
1583 useAppointmentExtrasAmount,
1584 useAppointmentExtraAmount,
1585 useAppointmentsAmount,
1586 useAppointmentsTotalAmount,
1587 useDuration,
1588 usePrepaidPrice,
1589 usePaymentError,
1590 useServices,
1591 useEmployeeService,
1592 useAppointmentBookingAmountData,
1593 useCheckingIfAllNotFree,
1594 useAppointmentServicePrice,
1595 useChangedBookingPrice,
1596 }
1597