HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/netwerkbrabant/netwerkbrabant.nl/app/KommaApp/Events/EventService.php
<?php


namespace App\KommaApp\Events;

use App\KommaApp\Base\Service;
use App\KommaApp\Events\Models\Event;
use App\KommaApp\EventSignUps\EventSignUpService;
use App\KommaApp\Orders\OrderService;
use App\KommaApp\Regions\Models\Region;
use App\KommaApp\WeFact\WeFactAPI;
use App\KommaApp\WeFact\WeFactService;
use App\Mail\EventSignUpMail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Spatie\CalendarLinks\Link;

class EventService extends Service
{
    const EVENTS_PER_PAGE = 6;
    const EVENTS_PER_REGION_PAGE = 9;
    const HIGHLIGHTED_EVENTS = 3;
    private $today;

    public function __construct()
    {
        $this->today = Carbon::now()->startOfDay();
        $this->today = $this->today->format('Y-m-d H:i:s');

        parent::__construct();
    }

    /**
     * The main query for getting events
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder
     */
    private function eventQuery()
    {
        return $this->site
            ->events()
            ->where('active', 1)
            ->whereNotNull('region_id')
            ->where('date', '>=', $this->today)
            ->orderBy('date', 'asc')
            ->orderBy('created_at', 'asc');
    }

    /**
     * Load the relations after or during query building
     *
     * @param $events
     * @param bool $queryBuilder
     */
    private function loadRelations(&$events, $queryBuilder = false)
    {
        if ($queryBuilder) $events->with('translation', 'images', 'type', 'type.translation');
        else $events->load('translation', 'images', 'type', 'type.translation');
    }

    /**
     * Get a specific event by id
     *
     * @param $eventId
     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder|null|object
     */
    public function getEvent($eventId)
    {
        $event = $this->eventQuery();
        $event = $event->where('id', $eventId);
        $this->loadRelations($event, true);
        return $event->first();
    }

    /**
     * Get the events which are active and bound to a region and are newer then today ordered descending by date
     * with optional parameter to paginate, load the relations, exclude given ids and get a given amount
     *
     * @param bool $pagination
     * @param bool $loadRelations
     * @param array|null $excludeIds
     * @param int|null $amount
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
     */
    public function getEvents(bool $pagination = false, bool $loadRelations = false, array $excludeIds = null, int $amount = null)
    {
        $events = $this->eventQuery();

        if ($loadRelations) $this->loadRelations($events, true);

        if ($excludeIds) $events = $events->whereNotIn('id', $excludeIds);

        // Get the events paginated or by get with optional a given amount
        if ($pagination) {
            $events = $events->paginate(self::EVENTS_PER_PAGE);
        } else {
            if ($amount) $events = $events->take($amount);
            $events = $events->get();
        }

        return $events;
    }

    /**
     * Get the events which are active and bound to a region and are newer then today ordered descending by date
     * with optional parameter to paginate, load the relations, exclude given ids and get a given amount
     *
     * @param bool $pagination
     * @param bool $loadRelations
     * @param array|null $excludeIds
     * @param int|null $amount
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
     */
    public function getEventsByType(string $eventType, bool $pagination = false, bool $loadRelations = false, array $excludeIds = null, int $amount = null)
    {
        $events = $this->eventQuery();

        if ($loadRelations) $this->loadRelations($events, true);

        if ($excludeIds) $events = $events->whereNotIn('id', $excludeIds);

        // Get the events paginated or by get with optional a given amount
        if ($pagination) {
            $events = $events->where('event_type_id', $eventType)->paginate(self::EVENTS_PER_PAGE);
        } else {
            if ($amount) $events = $events->where('event_type_id', $eventType)->take($amount);
            $events = $events->get();
        }


        return $events;
    }

    /**
     * Get the events which are active and where the region is $regionId and are newer then today ordered descending by date
     * with optional parameter to paginate, exclude given ids and get a given amount
     *
     * @param int $regionId
     * @param bool $pagination
     * @param array|null $excludeIds
     * @param int|null $amount
     * @param int|null $eventType
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
     */
    public function getEventsByRegion(int $regionId, bool $pagination = true, array $excludeIds = null, int $amount = null, $eventType = null)
    {

        $events = $this->eventQuery()
            ->where('region_id', $regionId);

        if($eventType !== null) $events = $events->where('event_type_id', $eventType);

        // Exclude the following events
        if ($excludeIds) $events = $events->whereNotIn('id', $excludeIds);

        $this->loadRelations($events, true);

        // Get the events paginated or by get with optional a given amount
        if ($pagination) {
            $events = $events->paginate(self::EVENTS_PER_REGION_PAGE);
        } else {
            if ($amount) $events = $events->take($amount);
            $events = $events->get();
        }

        return $events;
    }


    public function getBreakfastEventsByUserGroupId($userGroupId)
    {
        $events = $this->eventQuery()->where('user_group', $userGroupId);
        return $events->paginate();
    }

    public function getHighlightedBreakfastEventsByUserGroupId($userGroupId)
    {
        $events = $this->eventQuery()->where('user_group', $userGroupId)->take(self::HIGHLIGHTED_EVENTS)->get();
//        dd($events);
        return $events;
    }


    /**
     * Get the events which are active and where the region is $regionId and are newer then today and where the event_type_id is $eventType ordered descending by date
     * with optional parameter to paginate, exclude given ids and get a given amount
     *
     * @param int $regionId
     * @param bool $pagination
     * @param array|null $excludeIds
     * @param int|null $amount
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
     */
    public function getEventsByRegionAndType(int $regionId, string $eventType, bool $pagination = true, array $excludeIds = null, int $amount = null)
    {

        $events = $this->eventQuery()
            ->where('region_id', $regionId);

        // Exclude the following events
        if ($excludeIds) $events = $events->whereNotIn('id', $excludeIds);

        // Only get events with matching event type
        $events = $events->where('event_type_id', $eventType);

        $this->loadRelations($events, true);

        // Get the events paginated or by get with optional a given amount
        if ($pagination) {
            $events = $events->paginate(self::EVENTS_PER_REGION_PAGE);
        } else {
            if ($amount) $events = $events->take($amount);
            $events = $events->get();
        }

        return $events;
    }



    /**
     * Get the region of the logged in user
     *
     * @return Region|null
     */
    private function getRegionThroughUser(): ?Region
    {
        if (!\Auth::guard('siteUser')->check()) return null;

        if (!$userCompany = \Auth::guard('siteUser')->user()->getCompany()) return null;

        if (!$region = $userCompany->region) return null;

        return $region;

    }

    /**
     * Get the amount of needed highlighted events by region
     * and fill them out by the latest events if there aren't enough
     *
     * @param $regionId
     * @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
     */
    private function getHighlightedEventsForRegionId($regionId)
    {

        $events = $this->eventQuery()
            ->where('region_id', $regionId)
            ->take(self::HIGHLIGHTED_EVENTS)
            ->get();

        // If there are 3 events return them
        if ($events->count() == self::HIGHLIGHTED_EVENTS) return $events;

        // Fill out the events with the latest events (without region filter)

        // Get id's from the found event(s)
        $eventIds = $events->pluck('id')->all();

        // Determine the amount of events will still need
        $amountMoreNeeded = self::HIGHLIGHTED_EVENTS - $events->count();

        // Get the events and merge them with the current highlighted
        $otherRegionsUpcomingEvents = $this->getEvents(false, false, $eventIds, $amountMoreNeeded);
        $events = $events->merge($otherRegionsUpcomingEvents);

        return $events;
    }

    /**
     * Get the highlighted events
     * This can be based upon the bind company region of the user / or as guest the most upcoming events
     *
     * @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
     */
    public function getHighlightedEvents()
    {

        if ($region = $this->getRegionThroughUser()) $highlightedEvents = $this->getHighlightedEventsForRegionId($region->id);
        else $highlightedEvents = $this->getEvents(false, false, null, 3);

        $this->loadRelations($highlightedEvents);

        return $highlightedEvents;

    }


    /**
     * Get the highlighted events
     * This can be based upon the bind company region of the user / or as guest the most upcoming events
     *
     * @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
     */
    public function getHighlightedEventsByType($eventType)
    {
        // TODO user region
        if ($region = $this->getRegionThroughUser()) $highlightedEvents = $this->getHighlightedEventsForRegionId($region->id);

        else $highlightedEvents = $this->getEventsByType($eventType, false, false, null, 3);

        $this->loadRelations($highlightedEvents);

        return $highlightedEvents;

    }


    /**
     * Get the next event after the given one
     * first try to get the next event in region else just the next one
     * and if there are non a event that hasn't happend yet...
     *
     * @param Event $event
     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder|null|object
     */
    public function getNextEvent(Event $event)
    {

        // First next event in region
        if ($nextRegionEvent = $this->eventQuery()
            ->where('date', '>=', $event->date)
            ->where('id', '!=', $event->id)
            ->where('region_id', $event->region_id)
            ->first()) return $nextRegionEvent;

        // Else first next event
        if ($nextEvent = $this->eventQuery()
            ->where('date', '>=', $event->date)
            ->where('id', '!=', $event->id)
            ->first()) return $nextEvent;

        // And as final try other upcoming events
        return $otherUpcomingEvent = $this->site
            ->events()
            ->where('active', 1)
            ->whereNotNull('region_id')
            ->where('date', '>=', $this->today)
            ->where('date', '<=', $event->date)
            ->where('id', '!=', $event->id)
            ->orderBy('date', 'desc')
            ->first();
    }

    /**
     * Create a calendar link
     *
     * @param Event $event
     * @return Link
     * @throws \Spatie\CalendarLinks\Exceptions\InvalidLink
     */
    public function createCalendarEvent(Event $event)
    {

        $addressString = '';
        foreach(['location_name', 'location_address', 'location_city'] as $locationPart)
        {
            if(empty($event->{$locationPart})) continue;
            $addressString .= $event->{$locationPart}. ' ';
        }

        // Create a calendar link
        if (isset($event->calendar_start_time) && $event->calendar_start_time != '' && isset($event->calendar_end_time) && $event->calendar_end_time != '') {

            $startTime = $event->date->setTimeFromTimeString($event->calendar_start_time);
            $endTime = $event->date->setTimeFromTimeString($event->calendar_end_time);

            $calenderEvent = Link::create( $event->translation->name, $startTime, $endTime)
                ->description($event->translation->sub_title);
        }
        else {
            $calenderEvent = Link::create( $event->translation->name, $event->date->startOfDay(), $event->date->endOfDay())
                ->description($event->translation->sub_title);
        }

        if($addressString != '') $calenderEvent->address($addressString);

        return $calenderEvent;
    }

    public function convertRequest(array $formValues)
    {
        /** @var EventSignUpService $eventSignUpService */
        $eventSignUpService = \App::make(EventSignUpService::class);

        $event = Event::find(array_pull($formValues, 'event'))
            ->load('translation')
            ->load('type');

        $eventProduct = (object)[
            'productable_type' => Event::class,
            'productable_id' => $event->id,
            'name'  => $event->translation->name,

            // Calculated the price without vat by the Event type Vat and ceil to whole integer
            'price' =>  round($event->price_amount / (($event->type->vat / 100) + 1)),

            'vat' => $event->type->vat,
            'price_including_vat' => $event->price_amount,
        ];


        // Create signee array and add the main signee
        $signees = [
            'main' => $eventSignUpService->createSignee($formValues),
        ];


        // Check if there is an other invoice mail address
        if (isset($formValues['other_invoice']) && $formValues['other_invoice'] == 'on'){
            $signees['main']->invoice_email = array_pull($formValues, 'other_invoice_email');
        }
        // Else populate it from the email value
        else{
            $signees['main']->invoice_email = $signees['main']->email;
        }
        array_forget($formValues, ['other_invoice', 'other_invoice_email']);

        // Check if the (main) signee has filled a reference number
        if(!empty($formValues['invoice_reference'])) {
            $signees['main']->reference = array_pull($formValues, 'invoice_reference');
        }
        array_forget($formValues, ['invoice_reference']);

        // Check if we need to get the plus one else remove the keys
        if (isset($formValues['plus_one']) && $formValues['plus_one'] == 'on') {
            $signees['plusOne'] = $eventSignUpService->createSignee($formValues, 'plus_one_');
            $signees['plusOne']->invoice_email = $signees['plusOne']->email;
        }
        else {
            foreach ($eventSignUpService::SigneeFields as $signeeField)
                array_forget($formValues, 'plus_one_' . $signeeField);
        }
        array_forget($formValues, 'plus_one');

        return [$signees, $eventProduct, $event];
    }

    /**
     * Save signee as event sign ups through the event model
     *
     * @param $signee
     * @param $mainSignee
     * @param  Event  $event
     * @param  int  $invoiceId
     * @param $invoiceIdPrefixed
     */
    public function addSigneeToEventSignUps($signee, $mainSignee, Event $event, int $invoiceId, $invoiceIdPrefixed)
    {
        /** @var EventSignUpService $eventSignUpService */
        $eventSignUpService = \App::make(EventSignUpService::class);

        return $eventSignUpService->addSigneeToEventSignUps($signee, $mainSignee, $event, $invoiceId, $invoiceIdPrefixed);

    }

    /**
     * Get the order status and belonging event through order id
     *
     * @param $orderId
     * @return array
     */
    public function getEventOrderStatus($orderId)
    {
        // Get the order
        $orderService = \App::make(OrderService::class);

        if(!$order = $orderService->getOrder($orderId)) \App::abort(404);

        // Get the belonging event product
        $orderProduct = $order->products()->first();

        // Get the event from the event product
        $event = $orderProduct->productable;
        $event = $event->load('translation');

        return [$order, $event];
    }

    /**
     * Sync a event with WeFact
     *
     * @param  Event  $event
     */
    public function syncEvent(Event $event)
    {
        if(empty($event->wefact_code)) {
            throw new \InvalidArgumentException('Event (id:' . $event->id .') did not have a wefact_code, and therefor could not sync.');
        }

        /** @var WeFactService $weFactService */
        $weFactService = app()->make(WeFactService::class);

        $weFactProduct = $weFactService->getProduct($event->wefact_code);
        $weFactProductModifiedDate = Carbon::createFromFormat(Carbon::DEFAULT_TO_STRING_FORMAT, $weFactProduct->Modified);

        // WeFact product and Event are up-to-date
        if($weFactProductModifiedDate == $event->wefact_modified_at) {
            Log::info(self::class.': Sync - WeFact product ' . $event->wefact_code . ' and event ' . $event->id . ' are up-to-date.');
            return;
        }

        // WeFact is newer, therefor sync WeFact to Product
        elseif($weFactProductModifiedDate > $event->wefact_modified_at){
            $this->syncWeFactProductToEvent($event, $weFactProduct, $weFactProductModifiedDate);
            return;
        }
    }

    /**
     * Sync all relevant events from WeFact.
     * Note; Rather then loading each event and the belonging WeFact Product individual, we load the product list and then compare it with found events in our database
     *
     * @return int
     */
    public function syncAllRelevantEvents()
    {

        /** @var WeFactService $weFactService */
        $weFactService = app()->make(WeFactService::class);
        $weFactProducts = collect($weFactService->getEventProducts());

        $events = Event::where('date', '>=', $this->today)
            ->whereIn('wefact_code', $weFactProducts->pluck('ProductCode')->toArray())
            ->get();

        // Track for logging how many event are synced
        $eventSynced = 0;

        foreach ($events as $event) {

            $weFactProduct = (object)$weFactProducts->where('ProductCode', '=', $event->wefact_code)->first();
            $weFactProductModifiedDate = Carbon::createFromFormat(Carbon::DEFAULT_TO_STRING_FORMAT, $weFactProduct->Modified);

            // If the WeFact date is the same continue
            if($weFactProductModifiedDate == $event->wefact_modified_at) continue;

            $eventSynced++;
            $this->syncWeFactProductToEvent($event, $weFactProduct, $weFactProductModifiedDate);
        }

        return $eventSynced;
    }

    /**
     * Save WeFact product into our belonging event
     *
     * @param  Event  $event
     * @param $weFactProduct
     * @param  Carbon  $weFactProductModifiedDate
     */
    private function syncWeFactProductToEvent(Event $event, $weFactProduct, Carbon $weFactProductModifiedDate)
    {
        $event->price_amount = WeFactAPI::convertPriceExclToIncl($weFactProduct->PriceExcl, $weFactProduct->TaxPercentage);;
        $event->price = '€ ' . number_format(($event->price_amount / 100), 2, ',', '.');

        $event->wefact_name = $weFactProduct->ProductName;
        $event->wefact_description = $weFactProduct->ProductKeyPhrase;
        $event->wefact_modified_at = $weFactProductModifiedDate;
        $event->save();

        Log::info(self::class.': Sync - Event ' . $event->id . ' has been synced with WeFact product ' . $event->wefact_code . '.');
    }

}