Uname: Linux webm012.cluster130.gra.hosting.ovh.net 5.15.167-ovh-vps-grsec-zfs-classid #1 SMP Tue Sep 17 08:14:20 UTC 2024 x86_64
Software: Apache
PHP version: 8.0.30 [ PHP INFO ] PHP os: Linux
Server Ip: 145.239.37.162
Your Ip: 216.73.216.190
User: dreampi (1009562) | Group: users (100)
Safe Mode: OFF
Disable Function:
_dyuweyrj4,_dyuweyrj4r,dl

name : Packages.vue
<template>
  <div class="am-wrap">
    <div id="am-appointments" class="am-body">

      <!-- Spinner -->
      <div class="am-spinner am-section" v-show="!fetched || !options.fetched">
        <img :src="$root.getUrl+'public/img/spinner.svg'"/>
      </div>

      <!-- Empty State -->
      <div class="am-empty-state am-section"
           v-if="fetched && packageCustomers.length === 0 && !filterApplied && fetchedFiltered && options.fetched">
        <img :src="$root.getUrl+'public/img/emptystate.svg'">
        <h2>{{ $root.labels.no_appointments_yet }}</h2>
        <p>{{ $root.labels.click_add_appointments }}</p>
      </div>


      <!-- Appointments -->
      <div
        v-show="fetched && options.fetched && (packageCustomers.length !== 0 || (packageCustomers.length === 0 && filterApplied) || !fetchedFiltered)"
      >

        <!-- Global Search & Filters -->
        <div class="am-appointments-filter am-section">

          <div style="margin-bottom: 16px">
            <h1 @click="goBack" style="cursor: pointer;display: inline-block"><img :src="$root.getUrl+'public/img/arrow-back.svg'"> {{ purchasedPackage.name }}</h1>
          </div>

          <el-form :model="params" class="demo-form-inline" :action="exportAction" method="POST">

            <!-- Filters -->
            <transition name="fade">
              <div class="am-filter-fields" v-show="filterFields">
                <el-row :gutter="16" class="am-package-appointment-filters">

                  <!-- Dates Filter -->
                  <el-col :sm="24" :md="12" :lg="8" class="v-calendar-column">
                    <el-form-item prop="dates">
                      <v-date-picker
                          @input="changeRange"
                          v-model="params.dates"
                          :is-double-paned="false"
                          mode='range'
                          popover-visibility="focus"
                          popover-direction="bottom"
                          tint-color='#1A84EE'
                          :show-day-popover=false
                          :input-props='{class: "el-input__inner"}'
                          :is-expanded=false
                          :is-required=true
                          input-class="el-input__inner"
                          :formats="vCalendarFormats"
                      >
                      </v-date-picker>
                      <span
                          v-if="params.dates"
                          class="am-v-date-picker-suffix el-input__suffix-inner"
                          @click="clearDateFilter"
                      >
                        <i class="el-select__caret el-input__icon el-icon-circle-close"></i>
                      </span>
                    </el-form-item>
                  </el-col>

                  <!-- Customer Filter -->
                  <el-col :sm="24" :md="12" :lg="8">
                    <el-form-item>
                      <el-select
                          v-model="params.customerId"
                          filterable
                          clearable
                          :placeholder="$root.labels.customer"
                          @change="changeFilter"
                          remote
                          :remote-method="searchCustomers"
                          :loading="customersLoading"
                      >
                        <el-option
                            v-for="(item, key) in searchedCustomers.length ? searchedCustomers : options.entities.customers"
                            :key="key"
                            :label="(!item.firstName.trim() && !item.lastName.trim()) ? $root.labels.customer + ' ' + item.id : item.firstName + ' ' + item.lastName"
                            :value="item.id"
                        >
                        </el-option>
                      </el-select>
                    </el-form-item>
                  </el-col>

                  <!-- Status Filter -->
                  <el-col :sm="24" :md="12" :lg="8">
                    <el-form-item>
                      <el-select
                          v-model="params.packageStatus"
                          clearable
                          :placeholder="$root.labels.package_status"
                          @change="changeFilter"
                      >
                        <el-option
                            v-for="(item, key) in packageStatuses"
                            :key="key"
                            :label="item.label"
                            :value="item.value"
                        >
                        </el-option>
                      </el-select>
                    </el-form-item>
                  </el-col>

                </el-row>
              </div>
            </transition>

            <!-- Dialog Export -->
            <transition name="slide">
              <el-dialog
                  :close-on-click-modal="false"
                  class="am-side-dialog am-dialog-export"
                  :visible.sync="dialogExport"
                  :show-close="false"
                  v-if="dialogExport"
              >
                <dialog-export
                    :data="Object.assign(params, exportParams)"
                    :action="$root.getAjaxUrl + '/report/appointments'"
                    @updateAction="(action) => {this.exportAction = action}"
                    @closeDialogExport="dialogExport = false"
                >
                </dialog-export>
              </el-dialog>
            </transition>

          </el-form>
        </div>

        <!-- No Results -->
        <div class="am-empty-state am-section"
             v-show="fetched && Object.keys(packageCustomers).length === 0 && filterApplied && fetchedFiltered && options.fetched">
          <img :src="$root.getUrl + 'public/img/emptystate.svg'">
          <h2>{{ $root.labels.no_results }}</h2>
        </div>

        <!-- Content Spinner -->
        <div class="am-spinner am-section" v-show="!fetchedFiltered">
          <img :src="$root.getUrl+'public/img/spinner.svg'"/>
        </div>

        <!-- Appointment List -->
        <div
          class="am-appointments am-section"
          v-show="fetchedFiltered && options.fetched && Object.keys(packageCustomers).length !== 0"
        >

          <!-- Appointment List Header -->
          <div class="am-appointments-list-head">
            <el-row>
              <el-col :lg="12">
                <el-row :gutter="10" type="flex" justify="space-around" align="middle">

                  <!-- Checkbox -->
                  <el-col :lg="2" :sm="2">
                  </el-col>

                  <!-- Appointment List Customer Label -->
                  <el-col :lg="8" :md="8" :sm="12">
                    <p>{{ $root.labels.customer }}:</p>
                  </el-col>

                  <!-- Appointment List Spots Label -->
                  <el-col :lg="7" :md="7" :sm="12">
                    <p>{{ $root.labels.appointments }}:</p>
                  </el-col>

                  <!-- Appointment Date Booked Label -->
                  <el-col :lg="7" :md="7" :sm="12">
                    <p>{{ $root.labels.package_date_purchased }}:</p>
                  </el-col>


                </el-row>
              </el-col>

              <el-col :lg="12">
                <el-row :gutter="10" type="flex" justify="space-around" align="middle">

                  <!-- Package Employees Label -->
                  <el-col :lg="6" :md="6">
                    <p>{{ $root.labels.employees }}:</p>
                  </el-col>

                  <!-- Package Price Label -->
                  <el-col :lg="5" :sm="5">
                    <p>{{ $root.labels.price }}:</p>
                  </el-col>

                  <!-- Package Payment Status Label -->
                  <el-col :lg="6" :md="7">
                    <p>{{ $root.labels.package_payment_status }}:</p>
                  </el-col>

                  <!-- Package Status Label -->
                  <el-col :lg="7" :sm="3" :xs="8">

                  </el-col>

                </el-row>
              </el-col>

            </el-row>
          </div>

          <!-- Appointment List Content -->
          <div>

            <!-- Appointments -->
            <div class="am-appointments-list">
              <el-collapse>
                <el-collapse-item
                  v-for="(packageCustomer, packageCustomerId) in packageCustomers"
                  :key="packageCustomerId"
                  :name="packageCustomerId"
                  class="am-appointment"
                  :class="appointmentCameFrom(packageCustomer)"
                >
                  <template slot="title">
                    <div class="am-appointment-data">
                      <el-row>

                        <el-col :lg="12">
                          <el-row :gutter="15" class="am-appointments-flex-row-middle-align">

                            <!-- Checkbox -->
                            <el-col :lg="2" :sm="2">
                              <span class="am-appointment-checkbox" @click.stop>
                                <el-checkbox
                                  v-if="$root.settings.capabilities.canDelete === true"
                                  @change="handleCheckPackageAppointments(packageCustomerId)"
                                  v-model="packageCustomer.checked"
                                  :value="packageCustomer.checked"
                                  :label="packageCustomer.checked"
                                >
                                </el-checkbox>
                              </span>
                            </el-col>


                            <!-- Customer -->
                            <el-col :lg="8" :sm="8" :xs="24">
                              <p class="am-col-title">{{ $root.labels.customer }}:</p>
                              <template>
                                <h3>
                                  <span :class="getNoShowClass(packageCustomer.appointments[0].booking.customer.id, customersNoShowCount, null, packageCustomer.appointments[0].booking.customer.status)">
                                    {{ (user = getCustomerInfo(packageCustomer.appointments[0].booking)) !== null ? (!user.firstName.trim() && !user.lastName.trim() ? $root.labels.customer + ' ' + user.id : user.firstName + ' ' + user.lastName) : '' }}
                                  </span>
                                </h3>
                                <span>{{ packageCustomer.appointments[0].booking.customer.email }}</span>

                              </template>
                            </el-col>

                            <!-- Appointments Count -->
                            <el-col :lg="7" :sm="7">
                              <p class="am-col-title">{{ $root.labels.appointments }}:</p>
                              <h3>{{ packageCustomer.total }} {{ $root.labels.package_total }}</h3>
                              <span> {{ packageCustomer.count }} {{ $root.labels.package_to_be_booked }}</span>
                            </el-col>

                            <!-- Date purchased -->
                            <el-col :lg="7" :sm="7" :xs="12">
                              <p class="am-col-title">{{ $root.labels.package_date_purchased }}:</p>
                              <h3>{{ getFrontedFormattedDateFromDateTimeString(packageCustomer.appointments[0].booking.packageCustomerService.packageCustomer.purchased) }}</h3>
                              <span>{{ getFrontedFormattedTimeFromDateTimeString(packageCustomer.appointments[0].booking.packageCustomerService.packageCustomer.purchased) }}</span>
                              <span :class="'am-appointment-package-purchased-status am-appointment-package-purchased-status-' + getPackagePurchasedExpiration(packageCustomer.appointments[0].booking.packageCustomerService.packageCustomer.end).class">
                                {{ getPackagePurchasedExpiration(packageCustomer.appointments[0].booking.packageCustomerService.packageCustomer.end).label }}
                              </span>
                            </el-col>

                          </el-row>
                        </el-col>

                        <el-col :lg="12">
                          <el-row :gutter="15" class="am-appointments-flex-row-middle-align">
                            <el-col :lg="0" :md="2" :sm="2"></el-col>

                            <!-- Employees -->
                            <el-col :lg="6" :sm="6">
                              <p class="am-col-title">{{ $root.labels.employees }}:</p>
                              <div class="am-category-services-thumbs">
                                <img
                                  v-for="(providerId, index) in getPurchasedPackageProviders(packageCustomer.appointments)"
                                  v-if="index < 3"
                                  :key="providerId"
                                  :src="pictureLoad(getProviderById(providerId), true)"
                                  @error="imageLoadError(getProviderById(providerId), true)"
                                >
                              </div>
                            </el-col>

                            <!-- Price -->
                            <el-col class="am-appointment-payment" :lg="5" :sm="5" :xs="13">
                              <p class="am-col-title">{{ $root.labels.price }}:</p>
                              <h3>{{ getFormattedPrice(getPackageDiscountedPrice(packageCustomer.appointments[0].booking.packageCustomerService.packageCustomer)) }}</h3>
                            </el-col>

                            <!-- Payment Status -->
                            <el-col :lg="6" :sm="6" :xs="16" class="am-finance-payment-status">
                              <p class="am-col-title">{{ $root.labels.package_payment_status }}:</p>
                              <div class="am-payment-status">
                                <span :class="'am-payment-status-symbol am-payment-status-symbol-' + getPackagePaymentStatus(packageCustomer.appointments)"></span>
                                <h3>
                                  {{ getPaymentStatusNiceName(getPackagePaymentStatus(packageCustomer.appointments)) }}
                                </h3>
                              </div>
                            </el-col>

                            <el-col :lg="7" :sm="3" :xs="8">
                              <div @click.stop>
                                <el-button
                                  v-if="packageCustomer.status !== 'canceled'"
                                  @click="updatePackageCustomerStatus(packageCustomerId, 'canceled')"
                                  type="danger"
                                  plain
                                  :loading="packageCustomer.updating"
                                >
                                  {{ $root.labels.cancel }}
                                </el-button>
                                <el-button
                                  v-else
                                  @click="updatePackageCustomerStatus(packageCustomerId, 'approved')"
                                  type="primary"
                                  plain
                                  :loading="packageCustomer.updating"
                                >
                                  {{ $root.labels.open }}
                                </el-button>
                              </div>
                            </el-col>

                          </el-row>
                        </el-col>

                      </el-row>
                    </div>
                  </template>

                  <packages-list-collapsed
                    :packageCustomer="packageCustomer"
                    :packageCustomerId="packageCustomerId"
                    :options="options"
                    :purchasedPackage="purchasedPackage"
                    @showDialogEditPackageAppointment="showDialogEditPackageAppointment"
                    @updatePackageAppointmentBookingStatus="updatePackageAppointmentBookingStatusCallback"
                    @showDialogNewPackageAppointment="showDialogNewPackageAppointment"
                    @handleCheckPackageAppointment="handleToaster"
                  >
                  </packages-list-collapsed>
                </el-collapse-item>
              </el-collapse>
            </div>

          </div>

        </div>

        <!-- Selected Popover -->
        <transition name="slide-vertical">
          <div class="am-bottom-popover" v-if="toaster">
            <transition name="fade">
              <el-button
                class="am-button-icon"
                @click="switchShowDeleteConfirmation(true)"
                v-show="showDeleteButton"
              >
                <img class="svg-amelia" :alt="$root.labels.delete" :src="$root.getUrl+'public/img/delete.svg'"/>
              </el-button>
            </transition>
            <transition name="slide-vertical">
              <div class="am-bottom-popover-confirmation" v-show="showDeleteConfirmation">
                <el-row type="flex" justify="start" align="middle">
                  <h3>{{ confirmationText() }}</h3>
                  <div class="align-left">
                    <el-button size="small" @click="switchShowDeleteConfirmation(false)">
                      {{ $root.labels.cancel }}
                    </el-button>
                    <el-button
                      size="small" @click="deleteSelected"
                      type="primary"
                      :loading="deleteLoading"
                    >
                      {{ $root.labels.delete }}
                    </el-button>
                  </div>
                </el-row>

              </div>
            </transition>

          </div>
        </transition>

        <!-- Pagination -->
        <pagination-block
          :params="paginationParams"
          :show="paginationParams.show"
          :count="paginationParams.total"
          :label="$root.labels.purchased_packages_pagination"
          :visible="paginationParams.total > paginationParams.show"
          @change="getAppointments"
        >
        </pagination-block>
      </div>

      <!-- Dialog New Appointment -->
      <transition name="slide">
        <el-dialog
          :close-on-click-modal="false"
          class="am-side-dialog"
          :visible.sync="dialogAppointment"
          :show-close="false"
          v-if="dialogAppointment"
        >
          <dialog-appointment
            :appointment="appointment"
            :recurringAppointments="recurringAppointments"
            :savedAppointment="savedAppointment"
            :totalBookings="totalBookings"
            :bookings="bookings"
            :options="options"
            :customerCreatedCount="customerCreatedCount"
            :customersNoShowCount="customersNoShowCount"
            :packageServices="packageServices"
            :packageCustomer="packageCustomer"
            :have-duplicate="packageCustomer.count > 0 && appointment && packageServices.map(packageService => packageService.id).indexOf(appointment.serviceId) !== -1"
            @sortBookings="sortBookings"
            @saveCallback="(response) => {appointment.id ? saveAppointmentCallback(response, true) : saveAppointmentCallback(response)}"
            @duplicateCallback="duplicateAppointmentCallback(appointment)"
            @closeDialog="closeDialogAppointment"
            @showDialogNewCustomer="showDialogNewCustomer()"
            @editPayment="editPayment"
            @openRecurringAppointment="openRecurringAppointment"
          >
          </dialog-appointment>
        </el-dialog>
      </transition>

      <!-- Dialog New Package Booking -->
      <transition name="slide">
        <el-dialog
          :close-on-click-modal="false"
          class="am-side-dialog"
          :visible.sync="dialogPackageBooking"
          :show-close="false"
          v-if="dialogPackageBooking"
        >
          <dialog-package-booking
            :options="options"
            :passed-package="purchasedPackage"
            :customerCreatedCount="customerCreatedCount"
            :new-user="newUser"
            @saveCallback="packageBookingCallback"
            @closeDialog="closePackageBooking"
            @showDialogNewCustomer="showDialogNewCustomer()"
            @editPayment="editPayment"
          >
          </dialog-package-booking>
        </el-dialog>
      </transition>

      <!-- Dialog New Customer -->
      <transition name="slide">
        <el-dialog
            :close-on-click-modal="false"
            class="am-side-dialog"
            :visible.sync="dialogCustomer"
            :show-close="false"
            v-if="dialogCustomer">
          <dialog-customer
              :customer="customer"
              @closeDialog="dialogCustomer=false"
              @saveCallback="saveCustomerCallback"
          >
          </dialog-customer>
        </el-dialog>
      </transition>

      <!-- Dialog Payment -->
      <transition name="slide">
        <el-dialog
            :close-on-click-modal="false"
            class="am-side-dialog am-dialog-coupon"
            :visible.sync="dialogPayment"
            :show-close="false"
            v-if="dialogPayment"
        >
          <dialog-payment
              :modalData="selectedPaymentModalData"
              :bookingFetched=true
              @closeDialogPayment="dialogPayment = false"
              @updatePaymentCallback="updatePaymentCallback"
              @deletePaymentCallback="deletePaymentCallback"
          >
          </dialog-payment>
        </el-dialog>
      </transition>

    </div>
  </div>
</template>

<script>
  import PackagesListCollapsed from './../services/PackagesListCollapsed.vue'
  import appointmentMixin from '../../../js/backend/mixins/appointmentMixin'
  import appointmentPriceMixin from '../../../js/backend/mixins/appointmentPriceMixin'
  import customerMixin from '../../../js/backend/mixins/customerMixin'
  import dateMixin from '../../../js/common/mixins/dateMixin'
  import DialogAppointment from './../appointments/DialogAppointment.vue'
  import DialogPackageBooking from './../services/DialogPackageBooking.vue'
  import DialogCustomer from '../customers/DialogCustomer.vue'
  import DialogExport from '../parts/DialogExport.vue'
  import DialogPayment from '../finance/DialogFinancePayment.vue'
  import durationMixin from '../../../js/common/mixins/durationMixin'
  import entitiesMixin from '../../../js/common/mixins/entitiesMixin'
  import Form from 'form-object'
  import helperMixin from '../../../js/backend/mixins/helperMixin'
  import imageMixin from '../../../js/common/mixins/imageMixin'
  import moment from 'moment'
  import notifyMixin from '../../../js/backend/mixins/notifyMixin'
  import PageHeader from '../parts/PageHeader.vue'
  import paymentMixin from '../../../js/backend/mixins/paymentMixin'
  import priceMixin from '../../../js/common/mixins/priceMixin'
  import customFieldMixin from '../../../js/common/mixins/customFieldMixin'
  import PaginationBlock from '../parts/PaginationBlock.vue'
  import packageMixin from '../../../js/frontend/mixins/packageMixin'
  import taxMixin from '../../../js/common/mixins/taxesMixin'

export default {
    mixins: [
      customFieldMixin,
      paymentMixin,
      entitiesMixin,
      appointmentMixin,
      imageMixin,
      dateMixin,
      durationMixin,
      notifyMixin,
      customerMixin,
      priceMixin,
      helperMixin,
      appointmentPriceMixin,
      packageMixin,
      taxMixin
    ],

    props: [
      'purchasedPackage',
      'dialogPackageBooking'
    ],

    data () {
      return {
        customersNoShowCount: [],
        customersLoading: false,
        changedRange: false,
        packageServices: [],
        packageCustomer: null,
        showDeleteButton: true,
        appointment: null,
        customer: null,
        deleteLoading: false,
        dialogAppointment: false,
        dialogPayment: false,
        dialogExport: false,
        fetched: false,
        fetchedFiltered: false,
        filterFields: true,
        form: new Form(),
        params: {
          page: 1,
          dates: this.getDatePickerInitRange(),
          providers: [],
          search: '',
          status: '',
          services: [],
          customerId: '',
          packageStatus: ''
        },
        selectedPaymentModalData: {
          paymentId: null,
          bookingStart: null,
          bookings: null,
          service: null,
          providers: null,
          customer: null
        },
        showDeleteConfirmation: false,
        timer: null,
        toaster: false,
        updateBookingStatusId: 0,
        updateBookingStatusLoading: false,
        appointmentsDeleteCount: {
          success: 0,
          error: 0
        },
        packageCustomersDeleteCount: {
          success: 0,
          error: 0
        },
        packageCustomers: [],
        newUser: null,
        packageStatuses: [
          { value: 'approved', label: this.$root.labels.active },
          { value: 'expired', label: this.$root.labels.expired },
          { value: 'canceled', label: this.$root.labels.canceled }
        ],
        bookingCount: [
          { value: 1, label: this.$root.labels.available },
          { value: 0, label: this.$root.labels.fully_booked }
        ],
        paginationParams: {
          page: 1,
          show: this.$root.settings.general.itemsPerPageBackEnd,
          total: 0
        }
      }
    },

    created () {
      Form.defaults.axios = this.$http

      // Set filter params based on URL GET fields
      let urlParams = this.getUrlQueryParams(window.location.href)

      if (!('dateFrom' in urlParams) || !('dateTo' in urlParams)) {
        this.params.dates = this.getDatePickerInitRange()
      } else {
        this.params.dates = {
          start: moment(urlParams['dateFrom']).toDate(),
          end: moment(urlParams['dateTo']).toDate()
        }
      }

      if (urlParams['status']) { this.params.status = urlParams['status'] }

      if (urlParams['bookingId']) { this.params.bookingId = urlParams['bookingId'] }

      this.getAppointmentOptions(true, true)
    },

    mounted () {
      if (this.$root.settings.payments.wc && this.$root.settings.payments.wc.enabled) {
        this.exportParams.fields.push({label: this.$root.labels.wc_order_id, value: 'wcOrderId', checked: true})
      }
    },

    updated () {
      if (this.fetched) this.inlineSVG()
    },

    methods: {
      getPackagePurchasedExpiration (end) {
        if (end === null) {
          return { label: this.$root.labels.unlimited, class: 'unlimited' }
        } else if (this.getDateTime(end) < this.getNowDate()) {
          return { label: this.$root.labels.expired, class: 'expired' }
        } else {
          return { label: (this.$root.labels.expires_on + ' ' + this.getFrontedFormattedDateFromDateTimeString(end)), class: 'active' }
        }
      },

      clearDateFilter () {
        this.params.dates = null

        this.paginationParams.page = 1

        this.getAppointments()
      },

      getPackageDiscountedPrice (packCustomer) {
        let amountData = this.getAmountData(
          packCustomer.tax && packCustomer.tax.length ? packCustomer.tax[0] : null,
          packCustomer.price,
          this.options.entities.coupons.length && packCustomer.couponId
            ? this.options.entities.coupons.find(c => c.id === packCustomer.couponId)
            : null
        )

        return amountData.total - amountData.discount + amountData.tax
      },

      saveCustomerCallback (response) {
        this.options.entities.customers.push(response.user)
        this.newUser = response.user
        this.customerCreatedCount++
      },

      getAvailableServicesForPurchase (packageCustomer) {
        let packageCustomerId = packageCustomer.appointments[0].packageCustomerId

        let availableServicesIds = []

        packageCustomer.data.forEach((purchases) => {
          if (purchases.bookings.filter(item => item.count > 0 && item.packageCustomerId === packageCustomerId).length) {
            availableServicesIds.push(purchases.serviceId)
          }
        })

        return this.options.entities.services.filter(item => availableServicesIds.indexOf(item.id) !== -1)
      },

      showDialogNewPackageAppointment (packageCustomer) {
        this.packageServices = this.getAvailableServicesForPurchase(packageCustomer)

        this.packageCustomer = packageCustomer

        this.showDialogNewAppointment(
          {
            customerId: packageCustomer.appointments[0].booking.packageCustomerService.packageCustomer.customerId,
            id: packageCustomer.appointments[0].packageCustomerId
          }
        )
      },

      showDialogEditPackageAppointment (id, packageCustomer) {
        this.packageServices = this.getAvailableServicesForPurchase(packageCustomer)

        this.packageCustomer = packageCustomer

        this.showDialogEditAppointment(id, packageCustomer.appointments[0].booking.customerId)
      },

      getPurchasedPackageProviders (app) {
        return _.uniq(app.map(a => a.provider ? a.provider.id : 0)).filter(a => a !== 0)
      },

      goBack () {
        this.$emit('closePurchasedPackages')
      },

      switchShowDeleteConfirmation (bool) {
        this.showDeleteConfirmation = bool
        this.showDeleteButton = !bool
      },

      confirmationText () {
        let message = this.checkedAppointmentsCount() < 2 ? this.$root.labels.confirm_delete_appointment : this.$root.labels.confirm_delete_appointment_plural

        Object.keys(this.packageCustomers).forEach((packageCustomerId) => {
          if (this.packageCustomers[packageCustomerId].checked) {
            message = this.$root.labels.confirm_delete_package_purchase
          }
        })

        return message
      },

      checkedAppointmentsCount () {
        let cnt = 0

        Object.keys(this.packageCustomers).forEach((packageCustomerId) => {
          cnt += this.packageCustomers[packageCustomerId].appointments.filter(app => app.checked).length
        })

        return cnt
      },

      deleteSelected () {
        this.deleteLoading = true

        let selectedAppointments = []

        let selectedPackageCustomers = []

        Object.keys(this.packageCustomers).forEach((packageCustomerId) => {
          this.packageCustomers[packageCustomerId].appointments.forEach((appointment) => {
            if (appointment.checked && !this.packageCustomers[packageCustomerId].checked) {
              selectedAppointments.push(appointment.id)
            }
          })

          if (this.packageCustomers[packageCustomerId].checked) {
            selectedPackageCustomers.push(packageCustomerId)
          }
        })

        Object.keys(this.packageCustomers).forEach((packageCustomerId) => {
          this.packageCustomers[packageCustomerId].appointments.forEach((appointment) => {
            if (appointment.checked && !this.packageCustomers[packageCustomerId].checked) {
              this.form.post(`${this.$root.getAjaxUrl}/bookings/delete/` + appointment.booking.id)
                .then(() => {
                  this.deleteSelectedCallback(selectedAppointments, selectedPackageCustomers, 'appointment', true)
                })
                .catch(() => {
                  this.deleteSelectedCallback(selectedAppointments, selectedPackageCustomers, 'appointment', false)
                })
            }
          })
        })

        Object.keys(this.packageCustomers).forEach((packageCustomerId) => {
          if (this.packageCustomers[packageCustomerId].checked) {
            this.form.post(`${this.$root.getAjaxUrl}/packages/customers/delete/` + packageCustomerId)
              .then(() => {
                this.deleteSelectedCallback(selectedAppointments, selectedPackageCustomers, 'packageCustomer', true)
              })
              .catch(() => {
                this.deleteSelectedCallback(selectedAppointments, selectedPackageCustomers, 'packageCustomer', false)
              })
          }
        })
      },

      deleteSelectedCallback (selectedAppointments, selectedPackageCustomers, type, result) {
        if (type === 'appointment') {
          selectedAppointments.pop()

          if (result) {
            this.appointmentsDeleteCount.success++
          } else {
            this.appointmentsDeleteCount.error++
          }
        }

        if (type === 'packageCustomer') {
          selectedPackageCustomers.pop()

          if (result) {
            this.packageCustomersDeleteCount.success++
          } else {
            this.packageCustomersDeleteCount.error++
          }
        }

        if (selectedAppointments.length === 0 && selectedPackageCustomers.length === 0) {
          if (this.appointmentsDeleteCount.success) {
            this.notify(
              this.$root.labels.success,
              this.appointmentsDeleteCount.success + ' ' + (this.appointmentsDeleteCount.success > 1 ? this.$root.labels.appointments_deleted : this.$root.labels.appointment_deleted),
              'success')
          }

          if (this.appointmentsDeleteCount.error) {
            this.notify(
              this.$root.labels.error,
              this.appointmentsDeleteCount.error + ' ' + (this.appointmentsDeleteCount.error > 1 ? this.$root.labels.appointments_not_deleted : this.$root.labels.appointment_not_deleted),
              'error')
          }

          if (this.packageCustomersDeleteCount.success) {
            this.notify(
              this.$root.labels.success,
              this.packageCustomersDeleteCount.success + ' ' + (this.packageCustomersDeleteCount.success > 1 ? this.$root.labels.package_customers_deleted : this.$root.labels.package_customer_deleted),
              'success')
          }

          if (this.packageCustomersDeleteCount.error) {
            this.notify(
              this.$root.labels.error,
              this.packageCustomersDeleteCount.error + ' ' + (this.packageCustomersDeleteCount.error > 1 ? this.$root.labels.package_customers_not_deleted : this.$root.labels.package_customer_not_deleted),
              'error')
          }

          this.appointmentsDeleteCount.success = 0
          this.appointmentsDeleteCount.error = 0

          this.packageCustomersDeleteCount.success = 0
          this.packageCustomersDeleteCount.error = 0

          this.getAppointmentOptions(true)

          this.toaster = false
          this.deleteLoading = false
          this.showDeleteConfirmation = false
          this.showDeleteButton = true
        }
      },

      packageBookingCallback () {
        this.$emit('savePackageBookingCallback')
        this.getAppointments()
      },

      getAppointmentOptions (fetchAppointments, first = false) {
        this.options.fetched = false
        this.customersLoading = true

        this.fetchEntities((success) => {
          if (success && fetchAppointments) {
            this.customersLoading = false

            this.getAppointments(first)
          }

          this.fetched = true
          this.options.fetched = true
        }, {
          types: ['locations', 'employees', 'categories', 'custom_fields', 'packages', 'coupons', 'resources', 'coupons'],
          lite: true,
          page: 'appointments',
          isFrontEnd: false,
          isPanel: false
        })
      },

      getAppointments (first = false) {
        this.fetchedFiltered = false

        let params = JSON.parse(JSON.stringify(this.params))
        let dates = []

        if (params.dates) {
          if (params.dates.start) {
            dates.push(moment(params.dates.start).format('YYYY-MM-DD'))
          }

          if (params.dates.end) {
            dates.push(moment(params.dates.end).format('YYYY-MM-DD'))
          }

          params.dates = dates
        }

        params.page = this.paginationParams.page

        params.packageId = this.purchasedPackage.id

        Object.keys(params).forEach((key) => (!params[key] && params[key] !== 0) && delete params[key])

        this.$http.get(`${this.$root.getAjaxUrl}/package/appointments`, {
          params: this.getAppropriateUrlParams(params)
        })
          .then(response => {
            this.toaster = false

            let customersIds = this.options.entities.customers.map(customer => parseInt(customer.id))

            let customers = this.options.entities.customers

            let packageCustomers = {}

            this.useSortedDateStrings(Object.keys(response.data.data.appointments)).forEach((dateKey) => {
              response.data.data.appointments[dateKey].appointments.forEach((app) => {
                app.checked = false

                app.bookings.forEach((booking) => {
                  if (customersIds.indexOf(parseInt(booking.customer.id)) === -1) {
                    customersIds.push(booking.customer.id)
                    customers.push(booking.customer)
                  }

                  let packageCustomerId = parseInt(booking.packageCustomerService.packageCustomer.id)

                  if (!(packageCustomerId in packageCustomers)) {
                    packageCustomers[packageCustomerId] = {
                      status: booking.packageCustomerService.packageCustomer.status,
                      checked: false,
                      updating: false,
                      appointments: [],
                      count: 0,
                      total: 0,
                      data: []
                    }
                  }

                  let newPackageBooking =
                    {
                      status: booking.status === 'canceled' || booking.status === 'rejected' ? booking.status : app.status,
                      packageCustomerId: packageCustomerId,
                      canceled: false,
                      checked: false,
                      bookingStarts: app.bookingStart,
                      bookingEnd: app.bookingEnd,
                      provider: app.provider,
                      service: app.service,
                      id: app.id,
                      booking: booking
                    }

                  packageCustomers[packageCustomerId].appointments.push(newPackageBooking)
                })
              })
            })

            let emptyPackages = this.groupBy(response.data.data.emptyPackageBookings, 'packageCustomer')

            if (emptyPackages) {
              for (let pack in emptyPackages) {
                if (customersIds.indexOf(parseInt(emptyPackages[pack][0].packageCustomer.customer.id)) === -1) {
                  customersIds.push(parseInt(emptyPackages[pack][0].packageCustomer.customer.id))
                  customers.push(emptyPackages[pack][0].packageCustomer.customer)
                }

                let packageCustomerId = parseInt(pack)

                if (emptyPackages.hasOwnProperty(packageCustomerId)) {
                  if (!(pack in packageCustomers)) {
                    packageCustomers[packageCustomerId] = {
                      status: emptyPackages[pack][0].packageCustomer.status,
                      checked: false,
                      updating: false,
                      appointments: [],
                      count: 0,
                      total: 0,
                      data: []
                    }
                  }

                  let newPackageBooking =
                      {
                        status: null,
                        packageCustomerId: packageCustomerId,
                        canceled: false,
                        checked: false,
                        bookingStarts: null,
                        provider: null,
                        service: null,
                        id: null,
                        booking: {
                          customer: emptyPackages[pack][0].packageCustomer.customer,
                          packageCustomerService: emptyPackages[pack][0],
                          id: packageCustomerId,
                          payments: emptyPackages[pack][0].packageCustomer.payments
                        }
                      }

                  packageCustomers[packageCustomerId].appointments.push(newPackageBooking)
                }
              }
            }

            response.data.data.availablePackageBookings.forEach((customerData) => {
              customerData.packages.forEach((packageData) => {
                packageData.services.forEach((purchasePackageData) => {
                  purchasePackageData.bookings.forEach((purchaseData) => {
                    if (purchaseData.packageCustomerId in packageCustomers) {
                      if (purchaseData.sharedCapacity) {
                        if (packageCustomers[purchaseData.packageCustomerId].total === 0) {
                          packageCustomers[purchaseData.packageCustomerId].count = purchaseData.total - packageCustomers[purchaseData.packageCustomerId].appointments.filter(a => a.id && a.status !== 'canceled').length
                          packageCustomers[purchaseData.packageCustomerId].total = purchaseData.total
                        }
                      } else {
                        packageCustomers[purchaseData.packageCustomerId].count += purchaseData.count
                        packageCustomers[purchaseData.packageCustomerId].total += purchaseData.total
                      }

                      packageCustomers[purchaseData.packageCustomerId].data.push(purchasePackageData)
                    }
                  })
                })
              })
            })

            this.options.entities.customers = Object.values(customers.sort((a, b) => (a.firstName.toLowerCase() > b.firstName.toLowerCase()) ? 1 : -1))

            this.packageCustomers = Object.keys(packageCustomers).length === 0 ? [] : packageCustomers

            this.paginationParams.total = response.data.data.totalPackagePurchases

            this.changedRange = false

            this.fetched = true
            this.fetchedFiltered = true
          })
          .catch(e => {
            console.log(e.message)
            this.fetched = true
            this.fetchedFiltered = true
          })
      },

      appointmentCameFrom (cust) {
        let arr = []
        cust.appointments.forEach(app => {
          if (app.booking.info) {
            arr.push('front')
          } else {
            arr.push('back')
          }
        })

        if (arr.indexOf('front') !== -1 && arr.indexOf('back') !== -1) {
          return 'am-mixed-appointment'
        }

        if (arr.indexOf('front') !== -1 && arr.indexOf('back') < 0) {
          return 'am-front-appointment'
        }

        if (arr.indexOf('front') < 0 && arr.indexOf('back') !== -1) {
          return 'am-back-appointment'
        }

        return ''
      },

      changeRange () {
        this.setDatePickerSelectedDaysCount(this.params.dates.start, this.params.dates.end)
        this.changedRange = true
        this.changeFilter()
      },

      changeFilter () {
        if (!this.params.customerId) {
          this.searchedCustomers = []
        }

        this.paginationParams.page = 1

        this.getAppointments()
      },

      handleResize () {
        this.filterFields = window.innerWidth >= 992
      },

      handleCheckPackageAppointments (packageCustomerId) {
        if (this.packageCustomers[packageCustomerId].checked) {
          this.packageCustomers[packageCustomerId].appointments.filter(appointment => appointment.id !== null).forEach((appointment) => {
            appointment.checked = this.packageCustomers[packageCustomerId].checked
          })
        }

        Object.keys(this.packageCustomers).forEach((key) => {
          if (key !== packageCustomerId) {
            this.packageCustomers[key].checked = false

            this.packageCustomers[key].appointments.filter(appointment => appointment.id !== null).forEach((appointment) => {
              appointment.checked = false
            })
          }
        })

        this.handleToaster(packageCustomerId)
      },

      handleToaster (packageCustomerId) {
        let hasAnyAppointmentChecked = false

        let hasAnyPackageCustomerChecked = false

        Object.keys(this.packageCustomers).forEach((key) => {
          if (packageCustomerId !== key) {
            this.packageCustomers[key].appointments.filter(appointment => appointment.id !== null).forEach((appointment) => {
              appointment.checked = false
            })
          }

          this.packageCustomers[key].appointments.filter(appointment => appointment.id !== null).forEach((appointment) => {
            if (appointment.checked) {
              hasAnyAppointmentChecked = true
            }
          })

          if (this.packageCustomers[key].appointments.filter(appointment => appointment.id !== null).length !== 0 &&
              (
                this.packageCustomers[key].appointments.filter(appointment => appointment.checked && appointment.id !== null).length <
                this.packageCustomers[key].appointments.filter(appointment => appointment.id !== null).length
              )
          ) {
            this.packageCustomers[key].checked = false
          }

          if (this.packageCustomers[key].checked) {
            hasAnyPackageCustomerChecked = true
          }
        })

        this.toaster = hasAnyAppointmentChecked || hasAnyPackageCustomerChecked
      },

      showDialogNewCustomer () {
        this.customer = this.getInitCustomerObject()
        this.dialogCustomer = true
      },

      closePackageBooking () {
        this.$emit('closePackageBooking')
      },

      openRecurringAppointment (id) {
        this.dialogAppointment = false

        setTimeout(() => {
          this.showDialogEditAppointment(id)
        }, 200)
      },

      getPackagePaymentStatus (app) {
        let allPayments = app[0].booking.payments
        if (allPayments.every(p => p.status === 'refunded')) {
          return 'refunded'
        }

        let bookingPrice = this.getPackageDiscountedPrice(app[0].booking.packageCustomerService.packageCustomer)
        bookingPrice -= allPayments.filter(p => p.wcOrderId && p.wcItemCouponValue).reduce((partialSum, a) => partialSum + a.wcItemCouponValue, 0)
        bookingPrice += allPayments.filter(p => p.wcOrderId && p.wcItemTaxValue).reduce((partialSum, a) => partialSum + a.wcItemTaxValue, 0)

        let paidAmount = allPayments.filter(p => p.status !== 'pending' && p.status !== 'refunded').reduce((partialSum, a) => partialSum + a.amount, 0)

        if (Math.round(paidAmount * 100) / 100 >= Math.round(bookingPrice * 100) / 100) {
          return 'paid'
        } else if (paidAmount > 0) {
          return 'partiallyPaid'
        }
        return 'pending'
      },

      updatePackageAppointmentBookingStatusCallback (booking, packageCustomer, originalStatus) {
        this.updateAppointmentBookingStatus(booking, booking.status, packageCustomer, (success) => {
          if ((originalStatus === 'approved' || originalStatus === 'pending') && (booking.status === 'canceled' || booking.status === 'rejected')) {
            packageCustomer.count++
          } else if ((originalStatus === 'canceled' || originalStatus === 'rejected') && (booking.status === 'approved' || booking.status === 'pending')) {
            packageCustomer.count--
          }
        })
      },

      updatePackageCustomerStatus (packageCustomerId, status) {
        this.packageCustomers[packageCustomerId].updating = true

        this.form.post(this.$root.getAjaxUrl + '/packages/customers/' + packageCustomerId, {
          'status': status
        })
          .then(response => {
            this.packageCustomers[packageCustomerId].updating = false

            this.packageCustomers[packageCustomerId].status = status

            this.notify(this.$root.labels.success, response.message, 'success')
          })
          .catch(error => {
            this.packageCustomers[packageCustomerId].updating = false

            if (error.response) {
              this.notify(this.$root.labels.error, error.response.data.message, 'error')
            }
          })
      },

      groupBy (xs, key) {
        return xs.reduce(function (rv, x) {
          (rv[x[key].id] = rv[x[key].id] || []).push(x)
          return rv
        }, {})
      }
    },

    computed: {
      filterApplied () {
        return !!this.params.services.length || !!this.params.providers.length || !!this.params.customerId ||
            (this.params.dates && (!!this.params.dates.start || !!this.params.dates.end)) || !!this.params.status ||
            !!this.params.packageStatus
      }
    },

    watch: {
    },

    components: {
      PaginationBlock,
      PageHeader,
      DialogExport,
      DialogAppointment,
      DialogPackageBooking,
      DialogCustomer,
      DialogPayment,
      PackagesListCollapsed
    }
  }
</script>
© 2026 GrazzMean-Shell