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/SBogers10/netwerkbrabant.komma.pro/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 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.'.');
    }
}