inklabs/kommerce-core

View on GitHub
src/Entity/Order.php

Summary

Maintainability
C
1 day
Test Coverage
<?php
namespace inklabs\kommerce\Entity;

use inklabs\kommerce\Event\OrderShippedEvent;
use inklabs\kommerce\Lib\CartCalculatorInterface;
use inklabs\kommerce\Lib\ReferenceNumber\ReferenceNumberEntityInterface;
use Doctrine\Common\Collections\ArrayCollection;
use inklabs\kommerce\Lib\UuidInterface;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints as Assert;

class Order implements IdEntityInterface, ReferenceNumberEntityInterface
{
    use TimeTrait, IdTrait, EventGeneratorTrait;

    /** @var string|null */
    protected $externalId;

    /** @var string|null */
    protected $referenceNumber;

    /** @var string|null */
    protected $discountNames;

    /** @var CartTotal */
    protected $total;

    /** @var OrderAddress */
    protected $shippingAddress;

    /** @var OrderAddress */
    protected $billingAddress;

    /** @var OrderStatusType */
    protected $status;

    /** @var User */
    protected $user;

    /** @var OrderItem[] */
    protected $orderItems;

    /** @var AbstractPayment[] */
    protected $payments;

    /** @var Coupon[] */
    protected $coupons;

    /** @var CartPriceRule[] */
    protected $cartPriceRules;

    /** @var ShipmentRate|null */
    protected $shipmentRate;

    /** @var TaxRate|null */
    protected $taxRate;

    /** @var Shipment[] */
    protected $shipments;

    /** @var int */
    protected $ip4;

    public function __construct(UuidInterface $id = null)
    {
        $this->setId($id);
        $this->setCreated();
        $this->orderItems = new ArrayCollection();
        $this->payments = new ArrayCollection();
        $this->coupons = new ArrayCollection();
        $this->cartPriceRules = new ArrayCollection();
        $this->shipments = new ArrayCollection();

        $this->setStatus(OrderStatusType::pending());
    }

    public static function fromCart(
        UuidInterface $orderId,
        User $user,
        Cart $cart,
        CartCalculatorInterface $cartCalculator,
        string $ip4
    ): Order {
        $order = new Order($orderId);
        $order->setIp4($ip4);

        foreach ($cart->getCartItems() as $item) {
            $orderItem = $item->getOrderItem($order, $cartCalculator->getPricing());
        }

        foreach ($cart->getCoupons() as $coupon) {
            $order->addCoupon($coupon);
        }

        $order->setUser($user);
        $order->setTaxRate($cart->getTaxRate());
        $order->setShipmentRate($cart->getShipmentRate());
        $order->setTotal($cart->getTotal($cartCalculator));

        return $order;
    }

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint('externalId', new Assert\Length([
            'max' => 255,
        ]));

        $metadata->addPropertyConstraint('status', new Assert\Valid);

        $metadata->addPropertyConstraint('ip4', new Assert\NotBlank);
        $metadata->addPropertyConstraint('ip4', new Assert\GreaterThanOrEqual([
            'value' => 0,
        ]));

        $metadata->addPropertyConstraint('total', new Assert\Valid);
        $metadata->addPropertyConstraint('shippingAddress', new Assert\Valid);
        $metadata->addPropertyConstraint('billingAddress', new Assert\Valid);
        $metadata->addPropertyConstraint('orderItems', new Assert\Valid);
        $metadata->addPropertyConstraint('payments', new Assert\Valid);
        $metadata->addPropertyConstraint('shipments', new Assert\Valid);
    }

    public function getReferenceNumber(): ?string
    {
        return $this->referenceNumber;
    }

    public function setReferenceNumber(string $referenceNumber = null)
    {
        $this->referenceNumber = $referenceNumber;
    }

    public function addOrderItem(OrderItem $orderItem)
    {
        $this->orderItems->add($orderItem);
    }

    public function getExternalId(): ?string
    {
        return $this->externalId;
    }

    public function setExternalId(string $externalId)
    {
        $this->externalId = $externalId;
    }

    public function totalItems(): int
    {
        return count($this->orderItems);
    }

    public function totalQuantity(): int
    {
        $total = 0;

        foreach ($this->orderItems as $item) {
            $total += $item->getQuantity();
        }

        return $total;
    }

    public function setStatus(OrderStatusType $status)
    {
        $this->status = $status;
    }

    public function getStatus(): OrderStatusType
    {
        return $this->status;
    }

    public function getTotal(): CartTotal
    {
        return $this->total;
    }

    public function setTotal(CartTotal $total)
    {
        $this->total = $total;
        $this->setDiscountNames();
    }

    private function setDiscountNames()
    {
        $discountNames = [];
        foreach ($this->total->getCartPriceRules() as $cartPriceRule) {
            $this->cartPriceRules[] = $cartPriceRule;
            $discountNames[] = $cartPriceRule->getName();
        }

        foreach ($this->getCoupons() as $coupon) {
            $discountNames[] = $coupon->getName();
        }

        $this->discountNames = implode(', ', $discountNames);
    }

    public function setShippingAddress(OrderAddress $shippingAddress)
    {
        $this->shippingAddress = $shippingAddress;
    }

    public function getShippingAddress(): OrderAddress
    {
        return $this->shippingAddress;
    }

    public function setBillingAddress(OrderAddress $billingAddress)
    {
        $this->billingAddress = $billingAddress;
    }

    public function getBillingAddress(): OrderAddress
    {
        return $this->billingAddress;
    }

    /**
     * @return OrderItem[]
     */
    public function getOrderItems()
    {
        return $this->orderItems;
    }

    public function getOrderItem(int $orderItemIndex): OrderItem
    {
        return $this->orderItems[$orderItemIndex];
    }

    public function addPayment(AbstractPayment $payment)
    {
        $payment->setOrder($this);
        $this->payments[] = $payment;
    }

    /**
     * @return AbstractPayment[]
     */
    public function getPayments()
    {
        return $this->payments;
    }

    public function addCoupon(Coupon $coupon)
    {
        $this->coupons[] = $coupon;
    }

    /**
     * @return Coupon[]
     */
    public function getCoupons()
    {
        return $this->coupons;
    }


    public function setUser(User $user)
    {
        $this->user = $user;
    }

    public function getUser(): User
    {
        return $this->user;
    }

    public function getShipmentRate(): ?ShipmentRate
    {
        return $this->shipmentRate;
    }

    public function setShipmentRate(ShipmentRate $shipmentRate = null)
    {
        $this->shipmentRate = $shipmentRate;
    }

    public function getTaxRate(): ?TaxRate
    {
        return $this->taxRate;
    }

    public function setTaxRate(TaxRate $taxRate = null)
    {
        $this->taxRate = $taxRate;
    }

    /**
     * @return Product[]
     */
    public function getProducts()
    {
        $products = [];
        foreach ($this->getOrderItems() as $orderItem) {
            $product = $orderItem->getProduct();

            if ($product !== null) {
                $products[] = $product;
            }
        }

        return $products;
    }

    public function addShipment(Shipment $shipment)
    {
        $shipment->setOrder($this);
        $this->shipments->add($shipment);
        $this->setOrderShippedStatus();

        $this->raise(
            new OrderShippedEvent($this->getId(), $shipment->getId())
        );
    }

    /**
     * @return Shipment[]
     */
    public function getShipments()
    {
        return $this->shipments;
    }

    private function setOrderShippedStatus()
    {
        if ($this->isFullyShipped()) {
            $this->setStatus(OrderStatusType::shipped());
        } else {
            $this->setStatus(OrderStatusType::partiallyShipped());
        }
    }

    private function isFullyShipped(): bool
    {
        foreach ($this->orderItems as $orderItem) {
            if (! $this->isOrderItemFullyShipped($orderItem)) {
                return false;
            }
        }

        return true;
    }

    private function isOrderItemFullyShipped(OrderItem $orderItem): bool
    {
        foreach ($this->getShipments() as $shipment) {
            if ($orderItem->isShipmentFullyShipped($shipment)) {
                return true;
            }
        }

        return false;
    }

    public function setIp4(?string $ip4)
    {
        $this->ip4 = ip2long($ip4);
    }

    public function getIp4(): string
    {
        return long2ip($this->ip4);
    }

    public function getDiscountNames(): ?string
    {
        return $this->discountNames;
    }

    /**
     * @return ArrayCollection|CartPriceRule[]
     */
    public function getCartPriceRules()
    {
        return $this->cartPriceRules;
    }
}