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/farmfun.komma.pro/app/Komma/Availability/Types/Availability.php
<?php

namespace App\Komma\Availability\Types;

use App\Komma\Documents\Models\Document;
use App\Komma\LocationProducts\Models\LocationProduct;
use App\Komma\Locations\Models\Location;
use App\Komma\Products\Models\Product;
use Carbon\Carbon;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Collection;
use JsonSerializable;

class Availability implements JsonSerializable, Arrayable
{
    /** @var Product */
    private $product;

    /** @var Location */
    private $location;

    /** @var Carbon */
    private $date;

    /** @var string */
    public $name;

    /** @var int */
    public $type;

    /** @var int */
    public $price;

    /** @var Document */
    public $image;

    /** @var bool */
    public $available;

    /** @var string */
    public $available_reason;

    /** @var int */
    public $amountOfPersons;

    /** @var string */
    public $notification;

    /** @var Collection */
    public $timeSlots;

    /** @var int */
    public $maxPersonEachBlock;

    /** @var int */
    public $availableEachBlock;

    /** @var int */
    public $capacityType;

    public function __construct(Product $product, Location $location)
    {
        $this->product = $product;
        $this->location = $location;
    }

    public function appendCapacity(int $maxPersonEachBlock, int $availableEachBlock, int $capacityType)
    {
        $this->maxPersonEachBlock = $maxPersonEachBlock;
        $this->availableEachBlock = $availableEachBlock;
        $this->capacityType = $capacityType;
    }

    public function loadCapacity()
    {
        $locationProduct = LocationProduct::where('product_id', $this->product->id)
            ->where('location_id', $this->location->id)
            ->first();

        if (! $locationProduct->active) {
            throw new \UnexpectedValueException(self::class.': Somehow the loaded location product is marked as inactive. This shouldn\'t be possible, because it should only loaded products that are active on the location relation.');
        }

        $this->appendCapacity($locationProduct->max_persons_each_block, $locationProduct->available_each_block, $locationProduct->capacity_type);
    }

    /**
     * Get the capacity attributes
     * When calling you should use the list function to map the attributes to their values :)
     *
     * @return array
     */
    public function getCapacity()
    {
        if (! isset($this->maxPersonEachBlock) || ! isset($this->availableEachBlock) || ! isset($this->capacityType)) {
            $this->loadCapacity();
//            Log::warning(self::class. ': LoadCapacity has been called by the getCapacity function. To improve performance make sure this is already loaded (probably when adding to the shopping cart).');
        }

        return [$this->maxPersonEachBlock, $this->availableEachBlock, $this->capacityType];
    }

    /**
     * @param Carbon $date
     */
    public function setDate($date): void
    {
        $this->date = Carbon::createFromFormat('d-m-Y', $date)->startOfDay();
    }

    /**
     * @return Carbon
     */
    public function getDate(): Carbon
    {
        return $this->date;
    }

    /**
     * @return int
     */
    public function getAmountOfPersons(): ?int
    {
        return $this->amountOfPersons;
    }

    /**
     * @param int $amountOfPersons
     */
    public function setAmountOfPersons(int $amountOfPersons): void
    {
        $this->amountOfPersons = $amountOfPersons;
    }

    /**
     * @return string|null
     */
    public function getNotification(): ?string
    {
        return $this->notification;
    }

    /**
     * @param string $notification
     */
    public function setNotification(string $notification): void
    {
        $this->notification = $notification;
    }

//    public function getKey()
//    {
//        return 'availability_product-' . $this->product->id . '_location-' . $this->location->id;
//    }

    /**
     * Get the product.
     *
     * @return Product
     */
    public function getProduct(): Product
    {
        return $this->checkProductPriceForLocation();
    }

    /**
     * Get the location.
     *
     * @return Location
     */
    public function getLocation(): Location
    {
        return $this->location;
    }

    /**
     * Get the time slots.
     *
     * @return Location
     */
    public function getTimeSlots(): Collection
    {
        return $this->timeSlots;
    }

    public function setSelectedTimeSlot(string $timeSlotValue)
    {
        if (! isset($this->timeSlots)) {
            throw new \UnderflowException(self::class.": The time slots aren't defined. They should be, before calling this method. This should be automatically be done when adding to the shopping cart.", 500);
        }

        /** @var TimeSlot $timeSlot */
        foreach ($this->timeSlots as $timeSlot) {
            if ($timeSlot->getTimeSlotValue() == $timeSlotValue) {
                $timeSlot->selected = true;
                continue;
            }
            $timeSlot->selected = false;
        }
    }

    /**
     * Get the selected time slot.
     *
     * @return TimeSlot
     */
    public function getSelectedTimeSlot(): ? TimeSlot
    {
        if (! isset($this->timeSlots)) {
            throw new \UnderflowException(self::class.": The time slots aren't defined. They should be, before calling this method. This should be automatically be done when adding to the shopping cart.", 500);
        }

        $selectedTimeSlots = $this->timeSlots->where('selected', '=', true);

//        if($selectedTimeSlots->count() <= 0 )
//            throw new \OutOfBoundsException(self::class. ": There is no selected time slot. When calling this function there should be a selected time slot.", 500);

        if ($selectedTimeSlots->count() > 1) {
            throw new \OutOfBoundsException(self::class.': There are multiple selected time slots. When calling this function there should only be one selected time slot.', 500);
        }

        return $selectedTimeSlots->first();
    }

    /**
     * Get the label for the line below the price on the shopping cart
     * We have defined this here to keep the blades clean.
     *
     * @param bool $withVat
     * @param bool $formatted
     * @return array|string|null
     */
    public function getCartLabel(bool $withVat = true, bool $formatted = true)
    {
        // Price each person without start up
        if (! $this->product->has_fixed_price && $this->product->price_start_up === 0) {
            return config('site.shop_currency').' '.$this->product->getPriceLabel($withVat, $formatted).' '.__('site/availability.price_each_person');
        }

        //  Price is fixed
        if ($this->product->has_fixed_price) {
            // Is the same as the poster line
            return $this->product->getPosterLabel();
        }

        // Price for each person and with additional start up price
        if (! $this->product->has_fixed_price && $this->product->price_start_up !== 0) {
            $cartLine = config('site.shop_currency').' '.$this->product->getPriceLabel($withVat, $formatted, false);
            $cartLine .= ' <br/>+ '.config('site.shop_currency').' '.euro_pricing_format($this->product->price_start_up).' '.__('site/availability.start_up_small');

            return $cartLine;
        }
    }

    public function getAddonLabel(bool $withVat = true)
    {
        // Price each person without start up
        if (! $this->product->has_fixed_price && $this->product->price_start_up === 0) {
            return config('site.shop_currency').' '.$this->product->getPriceLabel($withVat, true).'<sub>'.__('site/availability.price_each_person').'</sub>';
        }

        if ($this->product->has_fixed_price) {
            // Is the same as the poster line
            return config('site.shop_currency').' '.$this->product->getPriceLabel($withVat, true);
        }

        // Price for each person and with additional start up price
        if (! $this->product->has_fixed_price && $this->product->price_start_up !== 0) {
            $cartLine = config('site.shop_currency').' '.euro_pricing_format($this->product->price_each_unit).'<sub>'.__('site/availability.price_each_person').'</sub>';
            $cartLine .= '<br/><sub>+ '.config('site.shop_currency').' '.euro_pricing_format($this->product->price_start_up).'</sub>';

            return $cartLine;
        }
    }

    /**
     * Get the total price.
     * This can be formatted or as integer.
     *
     * @param bool $withVat
     * @param bool $formatted
     * @return float|int|mixed|string|null
     */
    public function getTotal(bool $withVat = true, bool $formatted = true)
    {
        $price = null;

        // Price each person without start up
        if (! $this->product->has_fixed_price && $this->product->price_start_up === 0) {
            if (empty($this->amountOfPersons)) {
                throw new \UnexpectedValueException(self::class.': Amount of persons is undefined or zero. Expected an integer for calculation the total price for shopping cart item"'.$this->product->code_name.'"');
            }

            if ($withVat) {
                $price = $this->amountOfPersons * $this->product->price_each_unit;
            } else {
                $price = $this->amountOfPersons * $this->product->price_each_unit_excluding_vat;
            }
        }

        //  Price is fixed
        if ($this->product->has_fixed_price) {
            if ($withVat) {
                $price = $this->product->price_start_up;
            } else {
                $price = $this->product->price_start_up_excluding_vat;
            }
        }

        // Price for each person and with additional start up price
        if (! $this->product->has_fixed_price && $this->product->price_start_up !== 0) {
            if ($withVat) {
                $price = $this->amountOfPersons * $this->product->price_each_unit + $this->product->price_start_up;
            } else {
                $price = $this->amountOfPersons * $this->product->price_each_unit_excluding_vat + $this->product->price_start_up_excluding_vat;
            }
        }

        if (! isset($price)) {
            throw new \UnexpectedValueException(self::class.': We have not defined the total for the given situation "'.$this->product->code_name.'"');
        }

        // Return formatted or not
        if ($formatted) {
            return euro_pricing_format($price);
        }

        return $price;
    }

    public function toArray(): array
    {
        $product = Product::where('id', '=', $this->product->id)->with('translation', 'overviewImage')->first();

        return [
            'product' => [
                'id' => $product->id,
                'type' => $product->product_type,
                'name' => $product->translation->name,
                'slug' => $product->translation->slug,
                'overview_image' => $product->overviewImage->small_image_url ?? null,
            ],
            'has_timeslot_selection' => $product->hasTimeSelection(),
            'date' => $this->date->toArray(),
            'available' => $this->available,
            'amountOfPersons' => $this->amountOfPersons,
            'notification' => $this->notification,

            'timeSlots' => isset($this->timeSlots) ? $this->timeSlots->map(fn ($timeSlot) => TimeSlot::vueOption($timeSlot)) : [],

            'maxPersonEachBlock' => $this->maxPersonEachBlock,
            'availableEachBlock' => $this->availableEachBlock,

            'sub_total' => isset($this->amountOfPersons) ? $this->getTotal() : null,
            'price_label' => $this->getCartLabel(true, false),

            'selectedTimeslot' => isset($this->timeSlots) ? $this->getSelectedTimeSlot() : null,
        ];
    }

    public function jsonSerialize()
    {
        return $this->toArray();
    }

    private function checkProductPriceForLocation(): Product
    {
        $locationProduct = LocationProduct::where('product_id', $this->product->id)
            ->where('location_id', $this->location->id)
            ->first();

        if(! $locationProduct->use_location_product_price) {
            $product = Product::find($this->product->id);
            // Change product prices and vat (back) to product data
            $this->product->vat_percentage = $product->vat_percentage;
            $this->product->price_each_unit = $product->price_each_unit;
            $this->product->price_each_unit_excluding_vat = $product->price_each_unit_excluding_vat;
            $this->product->price_start_up = $product->price_start_up;
            $this->product->price_start_up_excl = $product->price_start_up_excl;
            $this->product->has_fixed_price = $product->has_fixed_price;
        } else {
            // Change product prices and vat (back) to location product data
            $this->product->vat_percentage = $locationProduct->vat_percentage;
            $this->product->price_each_unit = $locationProduct->price_each_unit;
            $this->product->price_each_unit_excluding_vat = $locationProduct->price_each_unit_excluding_vat;
            $this->product->price_start_up = $locationProduct->price_start_up;
            $this->product->price_start_up_excl = $locationProduct->price_start_up_excl;
            $this->product->has_fixed_price = $locationProduct->has_fixed_price;
        }

        return $this->product;

    }
}