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