HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux spn-python 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64
User: arjun (1000)
PHP: 8.1.2-1ubuntu2.20
Disabled: NONE
Upload Files
File: /var/www/html/appointmentbook.me/wp-content/plugins/booknetic/app/Backend/Appointments/Ajax.php
<?php

namespace BookneticApp\Backend\Appointments;

use BookneticApp\Backend\Appointments\Helpers\AppointmentRequests;
use BookneticApp\Config;
use BookneticApp\Models\AppointmentPrice;
use BookneticApp\Models\Service;
use BookneticApp\Providers\Common\PaymentGatewayService;
use BookneticApp\Providers\Core\CapabilitiesException;
use BookneticApp\Providers\UI\TabUI;
use BookneticApp\Backend\Appointments\Helpers\AppointmentSmartObject;
use BookneticApp\Backend\Appointments\Helpers\AppointmentService;
use BookneticApp\Backend\Appointments\Helpers\CalendarService;
use BookneticApp\Models\Appointment;
use BookneticApp\Models\AppointmentExtra;
use BookneticApp\Models\Customer;
use BookneticApp\Models\Location;
use BookneticApp\Models\ServiceCategory;
use BookneticApp\Models\ServiceExtra;
use BookneticApp\Models\ServiceStaff;
use BookneticApp\Models\Staff;
use BookneticApp\Providers\Core\Capabilities;
use BookneticApp\Providers\Helpers\Date;
use BookneticApp\Providers\DB\DB;
use BookneticApp\Providers\Helpers\Helper;
use BookneticApp\Providers\Core\Permission;

class Ajax extends \BookneticApp\Providers\Core\Controller
{

	public function add_new()
	{
		Capabilities::must( 'appointments_add' );

		$date           = Helper::_post('date', '', 'string');
		$locations      = Location::where('is_active', 1)->fetchAll();
		$locationInf    = count( $locations ) == 1 ? $locations[0] : false;

        TabUI::get( 'appointments_add_new' )
             ->item( 'details' )
             ->setTitle( bkntc__( 'Appointment details' ) )
             ->addView( __DIR__ . '/view/tab/details.php' )
             ->setPriority( 1 );

        TabUI::get( 'appointments_add_new' )
             ->item( 'extras' )
             ->setTitle( bkntc__( 'Extras' ) )
             ->addView( __DIR__ . '/view/tab/extras.php' )
             ->setPriority( 2 );

        $data = [
            'location'  => $locationInf,
            'date'      => $date,
        ];

		return $this->modalView( 'add_new', [
            'data' => $data
        ] );
	}

    /**
     * @throws CapabilitiesException
     * @throws \Exception
     */
    public function create_appointment()
	{
		Capabilities::must( 'appointments_add' );

        $run_workflows = Helper::_post('run_workflows', 1, 'num');
        Config::getWorkflowEventsManager()->setEnabled($run_workflows === 1);
        $appointmentRequests = AppointmentRequests::load( true );

        if( ! $appointmentRequests->validate() )
        {
            return $this->response(false,$appointmentRequests->getFirstError());
        }

        $appointmentData = $appointmentRequests->currentRequest();

		if( $appointmentData->isRecurring() && empty( $appointmentData->recurringAppointmentsList ) )
		{
			return $this->response(true, [ 'dates' => AppointmentService::getRecurringDates( $appointmentData ) ]);
		}

        AppointmentService::createAppointment();

        do_action( 'bkntc_backend_appointment_created', $appointmentData );

        PaymentGatewayService::find('local')->doPayment($appointmentRequests);

		return $this->response(true );
	}

	public function edit ()
	{
		Capabilities::must( 'appointments_edit' );

		$id = Helper::_post( 'id', '0', 'integer' );

		$appointmentSO = AppointmentSmartObject::load( $id );

		$appointmentInfo = Appointment::leftJoin( 'location',   [ 'name' ] )
                                      ->leftJoin( 'service',    [ 'name' ] )
                                      ->leftJoin( 'staff',      [ 'name' ] )
                                      ->where( Appointment::getField( 'id' ), $id )
                                      ->fetch();

		if( ! $appointmentSO->validate() )
		{
            return $this->response( false, bkntc__( 'Selected appointment not found!' ) );
		}

		// get service categories...
		$serviceInfo = $appointmentSO->getServiceInf();

		$categories = [];

		$categoryId = $serviceInfo['category_id'];
		$deep = 15;
		while( true )
		{
			$categoryInf = ServiceCategory::get( $categoryId );
			$categories[] = $categoryInf;

			$categoryId = (int)$categoryInf['parent_id'];

			if( ($deep--) < 0 || $categoryId <= 0 )
			{
				break;
			}
		}

        TabUI::get( 'appointments_edit' )
             ->item( 'details' )
             ->setTitle( bkntc__( 'Appointment details' ) )
             ->addView( __DIR__ . '/view/tab/edit_details.php', [
                 'id'            => $id,
                 'appointment'   => $appointmentSO,
                 'categories'    => array_reverse( $categories )
             ] )
             ->setPriority( 1 );

        TabUI::get( 'appointments_edit' )
             ->item( 'extras' )
             ->setTitle( bkntc__( 'Extras' ) )
             ->addView( __DIR__ . '/view/tab/edit_extras.php' )
             ->setPriority( 2 );

		return $this->modalView( 'edit', [
			'id'				=> $id,
			'service_capacity'	=> $serviceInfo['max_capacity'],
            'priceUpdated'      => Appointment::getData( $appointmentSO->getId(), 'price_updated', 0 ),
		] );
	}

	public function save_edited_appointment()
	{
		Capabilities::must( 'appointments_edit' );

        $run_workflows = Helper::_post('run_workflows', 1, 'num');
        Config::getWorkflowEventsManager()->setEnabled($run_workflows === 1);

        $appointmentRequests = AppointmentRequests::load( true );

        if( ! $appointmentRequests->validate() )
        {
            return $this->response(false, $appointmentRequests->getFirstError());
        }

        $appointmentObj = $appointmentRequests->currentRequest();

		do_action( 'bkntc_appointment_before_edit', $appointmentObj );
        do_action( 'bkntc_appointment_before_mutation', $appointmentObj->appointmentId );

		AppointmentService::editAppointment( $appointmentObj );

        do_action( 'bkntc_appointment_after_edit', $appointmentObj );
        do_action( 'bkntc_appointment_after_mutation', $appointmentObj->appointmentId );

		return $this->response(true, ['id' => $appointmentObj->appointmentId]);
	}

	public function info()
	{
		Capabilities::must( 'appointments' );

		$id = Helper::_post('id', '0', 'integer');

		$appointmentInfo = Appointment::leftJoin( 'customer', ['first_name', 'last_name', 'phone_number', 'email', 'profile_image'])
            ->leftJoin( 'location', ['name'] )
            ->leftJoin( 'service', ['name'] )
            ->leftJoin( 'staff', ['name', 'profile_image', 'email', 'phone_number'])
            ->where( Appointment::getField('id'), $id )->fetch();

		if( !$appointmentInfo )
		{
			return $this->response(false, bkntc__('Appointment not found!'));
		}

		$extrasArr = AppointmentExtra::where('appointment_id', $id)
            ->leftJoin(ServiceExtra::class, ['name', 'image'], ServiceExtra::getField('id'), AppointmentExtra::getField('extra_id'))
            ->fetchAll();


        $paymentGatewayList = [];
        $appointmentPrice = AppointmentPrice::where('appointment_id',  $appointmentInfo->id)
            ->select('sum(price * negative_or_positive) as total_amount', true)->fetch();

        if( $appointmentPrice->total_amount != $appointmentInfo->paid_amount )
        {
            $paymentGatewayList = PaymentGatewayService::getInstalledGatewayNames();
            $paymentGatewayList = array_filter($paymentGatewayList , function ($paymentGateway){
                return property_exists( PaymentGatewayService::find($paymentGateway) , 'createPaymentLink');
            });
        }

        TabUI::get( 'appointments_info' )
             ->item( 'details' )
             ->setTitle( bkntc__( 'Appointment details' ) )
             ->addView( __DIR__ . '/view/tab/info_details.php', [
                 'info' => $appointmentInfo,
                 'paymentGateways'=>$paymentGatewayList
             ] )
             ->setPriority( 1 );

        TabUI::get( 'appointments_info' )
             ->item( 'extras' )
             ->setTitle( bkntc__( 'Extras' ) )
             ->addView( __DIR__ . '/view/tab/info_extras.php', [
	             'info'     => $appointmentInfo,
                 'extras'   => $extrasArr
             ] )
             ->setPriority( 2 );

		return $this->modalView( 'info', [
            'id'            => $id,
        ] );
	}

    public function change_status()
    {
        Capabilities::must( 'appointments_change_status' );

        $ids = Helper::_post('ids', [], 'array');
        $statuses = Helper::getAppointmentStatuses();
        $selectedStatus = '';

        if( count( $ids ) === 1 )
        {
            $appointmentInf = Appointment::get( $ids[0] );
            $appointmentStatus = $appointmentInf->status;

            if( array_key_exists( $appointmentStatus, $statuses ) )
                $selectedStatus = $appointmentStatus;
        }


        return $this->modalView( 'change_status', [
            'ids'               => $ids,
            'statuses'          =>  $statuses,
            'selected_status'   =>  [
                'slug'  =>  $selectedStatus,
                'icon'  =>  empty($selectedStatus) ? '' : $statuses[$selectedStatus]['icon'],
                'color'  =>  empty($selectedStatus) ? '#FFF' : $statuses[$selectedStatus]['color'],
                'title'  =>  empty($selectedStatus) ? bkntc__('Select status...') : $statuses[$selectedStatus]['title']
            ]
        ] );
    }

    public function change_status_save()
    {
        Capabilities::must( 'appointments_change_status' );

        $run_workflows = Helper::_post('run_workflows', 1, 'num');
        Config::getWorkflowEventsManager()->setEnabled($run_workflows === 1);

        $ids = Helper::_post('ids', [], 'array');
        $status = Helper::_post('status', '', 'string');

        $availableStatuses = Helper::getAppointmentStatuses();

        if( ! array_key_exists( $status, $availableStatuses ) )
        {
            return $this->response( false, bkntc__('Selected status not found') );
        }

        foreach ( $ids AS $appointmentId )
        {
            if( ! ( is_numeric( $appointmentId ) && $appointmentId > 0 ) )
                continue;

            AppointmentService::setStatus( $appointmentId, $status );
        }

        return $this->response(true );
    }

	public function get_services()
	{
        $search		= Helper::_post( 'q', '', 'string' );
        $category	= Helper::_post( 'category', '', 'int' );

        $allowedStafIDs = array_column( Staff::fetchAll(), 'id' );

        $services = Service::where( 'is_active', 1 );

        if( ! empty( $category ) )
        {
            $services = $services->where( 'category_id', $category );
        }

        if ( ! empty( $search ) )
        {
            $services = $services->like('name', $search );
        }

        $data = [];

        foreach ( $services->fetchAll() as $service )
        {
            $isAllowedServiceForStaff = ServiceStaff::where('staff_id', $allowedStafIDs)->where('service_id', $service->id)->count();

            if( $isAllowedServiceForStaff == 0 )
                continue;

            $data[] = [
                'id'				=>	(int)$service['id'],
                'text'				=>	htmlspecialchars($service['name']),
                'repeatable'		=>	(int)$service['is_recurring'],
                'repeat_type'		=>	htmlspecialchars( $service['repeat_type'] ),
                'repeat_frequency'	=>	htmlspecialchars( $service['repeat_frequency'] ),
                'full_period_type'	=>	htmlspecialchars( $service['full_period_type'] ),
                'full_period_value'	=>	(int)$service['full_period_value'],
                'max_capacity'		=>	(int)$service['max_capacity'],
                'date_based'		=>	$service['duration'] >= 1440
            ];
        }

        return $this->response(true, [ 'results' => $data ]);
	}

	public function get_locations()
	{
		$search		= Helper::_post('q', '', 'string');

        // Permission olan Stafflarin ID-lerini chekib, hemen stafflarin assign olundugu Locationlari gotururuk.
        $allowedLocations = [];
        $myStaffList = Staff::fetchAll();
        foreach( $myStaffList as $staffLocations )
        {
            if( ! empty( $staffLocations->locations ) )
            {
                $allowedLocations = array_merge( $allowedLocations, explode(',', $staffLocations->locations) );
            }
        }

        $locations  = Location::where('is_active', 1)
                            ->where('id', $allowedLocations ?: 0)
                            ->where('name', 'LIKE', '%' . $search . '%')
                            ->fetchAll();

		$data = [];

		foreach ( $locations AS $location )
		{
			$data[] = [
				'id'	=> (int)$location['id'],
				'text'	=> htmlspecialchars($location['name'])
			];
		}

		return $this->response(true, [ 'results' => $data ]);
	}

	public function get_service_categories()
	{
		$search		= Helper::_post('q', '', 'string');
		$category	= Helper::_post('category', 0, 'int');

        $allowedStafIDs = array_column( Staff::fetchAll(), 'id' );

        $serviceCategories = ServiceCategory::where('name', 'LIKE', '%' . $search . '%')
            ->where('parent_id', $category)
            ->fetchAll();

		$data = [];

        foreach ( $serviceCategories AS $category )
        {
            $subcategories = Helper::getAllSubCategories( $category['id'] );

            $isAllowedServiceForStaff = ServiceStaff::where('staff_id', $allowedStafIDs)->where('service_id', Service::where('category_id', $subcategories)->select('id'))->count();

            if( $isAllowedServiceForStaff == 0 )
                continue;

            $data[] = [
                'id'                => (int)$category['id'],
                'text'              => htmlspecialchars($category['name']),
                'have_sub_categ'    => count( $subcategories ) - 1 // Helper::getAllSubCategories() methodu arrayin ichinde sechilen categoriyanida elave edir deye, neticenin countundan -1 edirik.
            ];
        }

		return $this->response(true, [ 'results' => $data ]);
	}

	public function get_staff()
	{
		$search		= Helper::_post('q', '', 'string');
		$location	= Helper::_post('location', 0, 'int');
		$service	= Helper::_post('service', 0, 'int');

		$staff = Staff::where('is_active', 1)
		                 ->where('name', 'like', "%$search%");

		if( !empty( $location ) )
		{
			$staff->whereFindInSet( 'locations', $location );
		}

		if( !empty( $service ) )
		{
			$serviceStaffSubQuery = ServiceStaff::where( 'service_id', $service )->select('staff_id');
			$staff->where( 'id', 'IN', $serviceStaffSubQuery );
		}

		$staff = $staff->fetchAll();

		$data = [];
		foreach ( $staff AS $staffInf )
		{
			$data[] = [
				'id'	=> (int)$staffInf['id'],
				'text'	=> htmlspecialchars($staffInf['name'])
			];
		}

		return $this->response(true, [ 'results' => $data ]);
	}

	public function get_customers()
	{
		$search = Helper::_post( 'q', '', 'string' );

		$customers = Customer::my();

        if( ! empty( $search ) )
        {
            $customers = $customers->where( 'CONCAT(`first_name`, \' \', `last_name`)', 'like', "%{$search}%" )
                ->orWhere( 'email', 'like', "%{$search}%" )
                ->orWhere( 'phone_number', 'like', "%{$search}%" );
        }

        $customers = $customers->select( [ 'id', 'first_name', 'last_name' ] )->limit( 100 )->fetchAll();

        $data = array_map( fn( $elem ) => [
            'id'	=> (int) $elem[ 'id' ],
            'text'	=> htmlspecialchars($elem[ 'first_name' ] . ' ' . $elem[ 'last_name' ] )
        ], $customers );

		return $this->response( true, [ 'results' => $data ] );
	}

	public function get_available_times( $calledFromBackend = true )
	{
        $id				= Helper::_post('id', -1, 'int');
        $search			= Helper::_post('q', '', 'string');
        $date			= Helper::_post('date', '', 'string');

        $date           = Date::reformatDateFromCustomFormat( $date );
        $calendarData   = new CalendarService( $date );

        if( $calledFromBackend )
        {
            $location		= Helper::_post('location', 0, 'int');
            $service		= Helper::_post('service', 0, 'int');
            $staff			= Helper::_post('staff', 0, 'int');
            $service_extras	= Helper::_post('service_extras', '[]', 'string');

            $calendarData->initServiceInf( $service );
        }
        else
        {
            $appointmentRequestData = AppointmentRequests::load()->currentRequest();
            $location		= $appointmentRequestData->getData('location', 0, 'int');
            $service		= $appointmentRequestData->getData('service', 0, 'int');
            $staff			= $appointmentRequestData->getData('staff', 0, 'int');
            $service_extras	= $appointmentRequestData->getData('service_extras', '[]', 'string');

            $calendarData->setServiceInf( $appointmentRequestData->serviceInf );
        }

        $service_extras	= json_decode($service_extras, true);

		$extras_arr	= [];
		foreach ( $service_extras AS $extraInf )
		{
			if( !( is_array( $extraInf )
			       && isset($extraInf['extra']) && is_numeric( $extraInf['extra'] ) && $extraInf['extra'] > 0
			       && isset($extraInf['quantity']) && is_numeric($extraInf['quantity']) && $extraInf['quantity'] > 0)
			)
			{
				continue;
			}

			$extra_inf = ServiceExtra::where('service_id', $service)->where('id', $extraInf['extra'])->fetch();

			if( $extra_inf && $extra_inf['max_quantity'] >= $extraInf['quantity'] )
			{
				$extra_inf['quantity'] = $extraInf['quantity'];

				$extras_arr[] = $extra_inf;
			}
		}

		$dataForReturn = [];

		$calendarData->setStaffId( $staff )
		             ->setLocationId( $location )
		             ->setServiceExtras( $extras_arr )
		             ->setExcludeAppointmentId( $id )
		             ->setShowExistingTimeSlots( false )
		             ->setCalledFromBackEnd( $calledFromBackend );

		$calendarData = $calendarData->getCalendar();
		$data = $calendarData['dates'];

		if( isset( $data[ $date ] ) )
		{
			foreach ( $data[ $date ] AS $dataInf )
			{
				$startTime = $dataInf['start_time_format'];

				// search...
				if( !empty( $search ) && strpos( $startTime, $search ) === false )
				{
					continue;
				}

				$result = [
					'id'					=>	$dataInf['start_time'],
					'text'					=>	$startTime,
					'max_capacity'			=>	$dataInf['max_capacity'],
					'weight'                =>	$dataInf['weight']
				];
                $dataForReturn[] = apply_filters('bkntc_backend_appointment_date_time' , $result , $dataInf );
			}
		}

		return $this->response(true, [ 'results' => $dataForReturn ]);
	}

	public function get_available_times_all()
	{
		$search		= Helper::_post('q', '', 'string');
		$service	= Helper::_post('service', 0, 'int');
		$location	= Helper::_post('location', 0, 'int');
		$staff		= Helper::_post('staff', 0, 'int');
		$dayOfWeek	= Helper::_post('day_number', 1, 'int');

		if( $dayOfWeek != -1 )
		{
			$dayOfWeek -= 1;
		}

		$calendarServ = new CalendarService();

		$calendarServ->setStaffId( $staff )
		             ->setServiceId( $service )
		             ->setLocationId( $location );

		return $this->response(true, [
			'results' => $calendarServ->getCalendarByDayOfWeek( $dayOfWeek, $search )
		]);
	}

	public function get_day_offs()
	{
        $appointmentRequests = AppointmentRequests::load();
        $appointmentObj = $appointmentRequests->currentRequest();

		if(
			! Date::isValid( $appointmentObj->recurringStartDate )
			|| ! Date::isValid( $appointmentObj->recurringEndDate )
			|| $appointmentObj->serviceId <= 0
		)
		{
			return $this->response(false, bkntc__('Please fill in all required fields correctly!'));
		}

		$calendarService = new CalendarService( $appointmentObj->recurringStartDate, $appointmentObj->recurringEndDate );
		$calendarService->setDefaultsFrom( $appointmentObj );

		return $this->response( true, $calendarService->getDayOffs() );
	}

	public function get_service_extras()
	{
		$appointment_id			= Helper::_post('appointment_id', 0, 'integer');
		$service_id	            = Helper::_post('service_id', 0, 'integer');

		$extras = ServiceExtra::where('service_id', $service_id)->fetchAll();

        $appointmentExtras = AppointmentExtra::where('appointment_id', $appointment_id)->fetchAll();
        $appointmentExtras = Helper::assocByKey($appointmentExtras, 'extra_id');

        foreach ($extras as $extra)
        {
            $extra->quantity = array_key_exists($extra->id, $appointmentExtras) ? $appointmentExtras[$extra->id]->quantity : 0;
        }

		return $this->modalView( 'service_extras', [
			'extras'    => $extras,
		] );
	}

    public function create_payment_link()
    {
        $paymentGateway = Helper::_post('payment_gateway','','str');
        $id = Helper::_post('id',0,'int');

        $totalAmountQuery = AppointmentPrice::where('appointment_id', DB::field( Appointment::getField('id') ))
            ->select('sum(price * negative_or_positive)', true);

        $appointments = Appointment::leftJoin('customer', ['first_name', 'last_name', 'email', 'profile_image', 'phone_number'])
            ->leftJoin('staff', ['name', 'profile_image'])
            ->leftJoin('location', ['name'])
            ->leftJoin('service', ['name'])
            ->where(Appointment::getField('id') , $id )
            ->selectSubQuery( $totalAmountQuery, 'total_price' );

        $appointment = $appointments->fetch();
        if( empty($appointments) )
        {
            return $this->response(false);
        }

        $paymentGatewayService = PaymentGatewayService::find( $paymentGateway );

        if(! property_exists( $paymentGatewayService  ,'createPaymentLink'))
        {
            return $this->response(false);
        }

        $data = $paymentGatewayService->createPaymentLink([$appointment]);

        return $this->response(true ,['url'=>$data->data['url']]);
    }


}