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;
}
}