Skip to content
Code-Schnipsel Gruppen Projekte
CouponCode.php 11,7 KiB
Newer Older
Patrick Müller's avatar
Patrick Müller committed
<?php

namespace QUI\ERP\Coupons;

use QUI;
use QUI\Permissions\Permission;
use QUI\ERP\Discount\Handler as DiscountHandler;
Patrick Müller's avatar
Patrick Müller committed

/**
Patrick Müller's avatar
Patrick Müller committed
 * Class CouponCode
Patrick Müller's avatar
Patrick Müller committed
 */
class CouponCode
{
    /**
Patrick Müller's avatar
Patrick Müller committed
     * CouponCode ID
Patrick Müller's avatar
Patrick Müller committed
     *
     * @var int
     */
    protected $id;

    /**
     * Actual code
     *
     * @var string
     */
    protected $code;

    /**
Patrick Müller's avatar
Patrick Müller committed
     * IDs of users that this CouponCode is restricted to
Patrick Müller's avatar
Patrick Müller committed
     *
     * @var int[]
Patrick Müller's avatar
Patrick Müller committed
     */
Patrick Müller's avatar
Patrick Müller committed
    protected $userIds = [];
Patrick Müller's avatar
Patrick Müller committed

    /**
Patrick Müller's avatar
Patrick Müller committed
     * IDs of groups that this CouponCode is restricted to
Patrick Müller's avatar
Patrick Müller committed
     *
     * @var int[]
Patrick Müller's avatar
Patrick Müller committed
     */
Patrick Müller's avatar
Patrick Müller committed
    protected $groupIds = [];
Patrick Müller's avatar
Patrick Müller committed

    /**
     * IDs of all linked discounts
     *
     * @var int[]
     */
    protected $discountIds = [];

Patrick Müller's avatar
Patrick Müller committed
    /**
Patrick Müller's avatar
Patrick Müller committed
     * List of usages of this CouponCode
Patrick Müller's avatar
Patrick Müller committed
     *
Patrick Müller's avatar
Patrick Müller committed
     * @var array
Patrick Müller's avatar
Patrick Müller committed
     */
Patrick Müller's avatar
Patrick Müller committed
    protected $usages = [];
Patrick Müller's avatar
Patrick Müller committed

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Creation Date
Patrick Müller's avatar
Patrick Müller committed
     *
     * @var \DateTime
     */
Patrick Müller's avatar
Patrick Müller committed
    protected $CreateDate;
Patrick Müller's avatar
Patrick Müller committed

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Date until the CouponCode is valid
Patrick Müller's avatar
Patrick Müller committed
     *
     * @var \DateTime
     */
    protected $ValidUntilDate = null;

    /**
Patrick Müller's avatar
Patrick Müller committed
     * CouponCode title
Patrick Müller's avatar
Patrick Müller committed
     *
     * @var string
     */
Patrick Müller's avatar
Patrick Müller committed
    protected $title = null;
Patrick Müller's avatar
Patrick Müller committed

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Flag - Is the CouponCode valid?
Patrick Müller's avatar
Patrick Müller committed
     *
     * @var bool
     */
Patrick Müller's avatar
Patrick Müller committed
    protected $valid = true;
Patrick Müller's avatar
Patrick Müller committed

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Flag - Is the CouponCode reusable?
     *
Patrick Müller's avatar
Patrick Müller committed
     * @var bool
     */
Patrick Müller's avatar
Patrick Müller committed
    protected $reusable;
Patrick Müller's avatar
Patrick Müller committed

    /**
Patrick Müller's avatar
Patrick Müller committed
     * CouponCode constructor.
Patrick Müller's avatar
Patrick Müller committed
     *
     * @param int $id - Invite Code ID
Patrick Müller's avatar
Patrick Müller committed
     * @throws \QUI\ERP\Coupons\CouponCodeException
Patrick Müller's avatar
Patrick Müller committed
     */
    public function __construct($id)
    {
Patrick Müller's avatar
Patrick Müller committed
        $result = QUI::getDataBase()->fetch([
Patrick Müller's avatar
Patrick Müller committed
            'from'  => Handler::getTable(),
Patrick Müller's avatar
Patrick Müller committed
            'where' => [
Patrick Müller's avatar
Patrick Müller committed
                'id' => $id
Patrick Müller's avatar
Patrick Müller committed
            ]
        ]);
Patrick Müller's avatar
Patrick Müller committed

        if (empty($result)) {
Patrick Müller's avatar
Patrick Müller committed
            throw new CouponCodeException([
                'quiqqer/coupons',
                'exception.CouponCode.not_found',
                [
Patrick Müller's avatar
Patrick Müller committed
                    'id' => $id
Patrick Müller's avatar
Patrick Müller committed
                ]
            ], 404);
Patrick Müller's avatar
Patrick Müller committed
        }

        $data = current($result);

Patrick Müller's avatar
Patrick Müller committed
        $this->id    = (int)$data['id'];
        $this->code  = $data['code'];
        $this->title = $data['title'];
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        if (!empty($data['usages'])) {
            $this->usages = json_decode($data['usages'], true);
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        if (!empty($data['userIds'])) {
            $this->userIds = json_decode($data['userIds'], true);
        }
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        if (!empty($data['groupIds'])) {
            $this->groupIds = json_decode($data['groupIds'], true);
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        if (!empty($data['isReusable'])) {
            $this->reusable = true;
        }

        if (!empty($data['discountIds'])) {
            $this->discountIds = json_decode($data['discountIds'], true);
        }

Patrick Müller's avatar
Patrick Müller committed
        $this->CreateDate = new \DateTime($data['createDate']);

Patrick Müller's avatar
Patrick Müller committed
        if (!empty($data['validUntilDate'])) {
            $this->ValidUntilDate = new \DateTime($data['validUntilDate']);
        }
Patrick Müller's avatar
Patrick Müller committed

        $this->checkValidity();
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return string
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * @return \DateTime
     */
    public function getCreateDate()
    {
        return $this->CreateDate;
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Get usage data
     *
     * @return array
Patrick Müller's avatar
Patrick Müller committed
     */
Patrick Müller's avatar
Patrick Müller committed
    public function getUsages()
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        return $this->usages;
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * @return \DateTime|null
     */
    public function getValidUntilDate()
    {
        return $this->ValidUntilDate;
    }

    /**
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * @return int[]
     */
    public function getDiscountIds()
    {
        return $this->discountIds;
    }

    /**
     * Get all discounts associated with this CouponCode
     *
     * @return QUI\ERP\Discount\Discount[]
     */
    public function getDiscounts()
    {
        $discounts       = [];
        $DiscountHandler = DiscountHandler::getInstance();

        foreach ($this->discountIds as $discountId) {
            try {
                $discounts[] = $DiscountHandler->getChild($discountId);
            } catch (\Exception $Exception) {
                QUI\System\Log::writeDebugException($Exception);
            }
        }

        return $discounts;
    }

Patrick Müller's avatar
Patrick Müller committed
    /**
Patrick Müller's avatar
Patrick Müller committed
     * Redeems this CouponCode
     *
     * Hint: This may invalidate the code for future use
     *
     * @param QUI\Users\User $User - The user that redeems the CouponCode [if omitted use Session User]
     * @return void
     * @throws CouponCodeException
Patrick Müller's avatar
Patrick Müller committed
     * @throws QUI\Exception
Patrick Müller's avatar
Patrick Müller committed
     */
Patrick Müller's avatar
Patrick Müller committed
    public function redeem($User = null)
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        if (is_null($User)) {
            $User = QUI::getUserBySession();
        }

        $this->checkRedemption($User);

        $Now            = new \DateTime();
        $this->usages[] = [
            'userId' => $User->getId(),
            'date'   => $Now->format('Y-m-d H:i:s')
        ];

        QUI::getDataBase()->update(
            Handler::getTable(),
            [
                'usages' => json_encode($this->usages)
            ],
            [
                'id' => $this->id
            ]
        );

        $this->checkValidity();
Patrick Müller's avatar
Patrick Müller committed

        QUI::getEvents()->fireEvent(
            'quiqqerCouponsRedeem',
            [
                'User'       => $User,
                'CouponCode' => $this
            ]
        );
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Check if the given User can redeem this CouponCode
Patrick Müller's avatar
Patrick Müller committed
     *
Patrick Müller's avatar
Patrick Müller committed
     * @param QUI\Users\User $User - If omitted, use session user
Patrick Müller's avatar
Patrick Müller committed
     * @return void
Patrick Müller's avatar
Patrick Müller committed
     * @throws CouponCodeException - Thrown if not redeemable by the given User
Patrick Müller's avatar
Patrick Müller committed
     */
    public function checkRedemption($User)
Patrick Müller's avatar
Patrick Müller committed
    {
        if (!$this->isValid()) {
            throw new CouponCodeException([
                'quiqqer/coupons',
                'exception.CouponCode.no_longer_valid'
            ]);
        }

        $DiscountHandler = DiscountHandler::getInstance();
        $discountsValid  = false;
        $discountError   = false;

        foreach ($this->discountIds as $discountId) {
            try {
                /** @var QUI\ERP\Discount\Discount $Discount */
                $Discount = $DiscountHandler->getChild($discountId);
            } catch (\Exception $Exception) {
                $discountError = $Exception->getMessage();
                continue;
            }

            if ($Discount->canUsedBy($User)) {
                $discountsValid = true;
                break;
            }
        }

        if (!$discountsValid) {
            if (count($this->discountIds) === 1) {
                throw new CouponCodeException([
                    'quiqqer/coupons',
                    'exception.CouponCode.discount_invalid',
                    [
                        'reason' => $discountError
                    ]
                ]);
            } else {
                throw new CouponCodeException([
                    'quiqqer/coupons',
                    'exception.CouponCode.discounts_invalid'
                ]);
            }
        }

Patrick Müller's avatar
Patrick Müller committed
        if (!empty($this->userIds)) {
            if (in_array($User->getId(), $this->userIds)) {
                if (!$this->reusable && $this->hasUserRedeemed($User)) {
                    throw new CouponCodeException([
                        'quiqqer/coupons',
                        'exception.CouponCode.already_used'
                    ]);
                }
            } else {
                throw new CouponCodeException([
                    'quiqqer/coupons',
                    'exception.CouponCode.user_not_allowed'
                ]);
            }
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        if (!empty($this->groupIds)) {
            $userInGroup = false;
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            foreach ($this->groupIds as $groupId) {
                if ($User->isInGroup($groupId)) {
                    $userInGroup = true;
                    break;
                }
            }
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            if ($userInGroup) {
                if (!$this->reusable && $this->hasUserRedeemed($User)) {
                    throw new CouponCodeException([
                        'quiqqer/coupons',
                        'exception.CouponCode.already_used'
                    ]);
                }
            } else {
                throw new CouponCodeException([
                    'quiqqer/coupons',
                    'exception.CouponCode.user_not_allowed_group'
                ]);
            }
        }
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Check if the given User can redeem this CouponCode
Patrick Müller's avatar
Patrick Müller committed
     *
Patrick Müller's avatar
Patrick Müller committed
     * @param QUI\Users\User $User - If omitted, use session user
Patrick Müller's avatar
Patrick Müller committed
     * @return bool
     */
Patrick Müller's avatar
Patrick Müller committed
    public function isRedeemable($User = null)
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        try {
            $this->checkRedemption($User);
        } catch (CouponCodeException $Exception) {
            return false;
        }

        return true;
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Check if this CouponCode is still valid
Patrick Müller's avatar
Patrick Müller committed
     *
     * @return bool
     */
Patrick Müller's avatar
Patrick Müller committed
    public function isValid()
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        return $this->valid;
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Checks if an CouponCode has been redeemed by a user
Patrick Müller's avatar
Patrick Müller committed
     *
Patrick Müller's avatar
Patrick Müller committed
     * @param QUI\Users\User $User
     * @return bool
Patrick Müller's avatar
Patrick Müller committed
     */
Patrick Müller's avatar
Patrick Müller committed
    public function hasUserRedeemed($User)
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        $userId = $User->getId();
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        foreach ($this->usages as $usage) {
            if ($usage['userId'] === $userId) {
                return true;
            }
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        return false;
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Permanently delete this CouponCode
Patrick Müller's avatar
Patrick Müller committed
     *
     * @return void
     * @throws \QUI\Permissions\Exception
Patrick Müller's avatar
Patrick Müller committed
     */
    public function delete()
    {
        Permission::checkPermission(Handler::PERMISSION_DELETE);

        QUI::getDataBase()->delete(
            Handler::getTable(),
Patrick Müller's avatar
Patrick Müller committed
            [
Patrick Müller's avatar
Patrick Müller committed
                'id' => $this->id
Patrick Müller's avatar
Patrick Müller committed
            ]
Patrick Müller's avatar
Patrick Müller committed
        );
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Get CouponCode attributes as array
Patrick Müller's avatar
Patrick Müller committed
     *
     * @return array
     */
    public function toArray()
    {
Patrick Müller's avatar
Patrick Müller committed
        $data = [
Patrick Müller's avatar
Patrick Müller committed
            'id'             => $this->getId(),
            'code'           => $this->getCode(),
Patrick Müller's avatar
Patrick Müller committed
            'userIds'        => $this->userIds,
            'groupIds'       => $this->groupIds,
Patrick Müller's avatar
Patrick Müller committed
            'createDate'     => $this->getCreateDate()->format('Y-m-d H:i:s'),
Patrick Müller's avatar
Patrick Müller committed
            'usages'         => $this->usages,
Patrick Müller's avatar
Patrick Müller committed
            'validUntilDate' => false,
            'title'          => $this->getTitle() ?: false,
Patrick Müller's avatar
Patrick Müller committed
            'isValid'        => $this->isValid(),
            'reusable'       => $this->reusable,
            'discountIds'    => $this->discountIds
Patrick Müller's avatar
Patrick Müller committed
        ];
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        $ValidUntilDate = $this->getValidUntilDate();
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        if ($ValidUntilDate) {
            $data['validUntilDate'] = $ValidUntilDate->format('Y-m-d');
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        return $data;
    }
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Checks if this CouponCode is still valid
     *
     * @return void
     */
    protected function checkValidity()
    {
        // Check if the expiration date has been reached
        if (!empty($this->ValidUntilDate)) {
            $Now = new \DateTime();
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            if ($Now > $this->ValidUntilDate) {
                $this->valid = false;
                return;
            }
        }
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        if ($this->reusable) {
            return;
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        // If the CouponCode is restricted to certain users -> Check if all those
        // users have already redeemed the code
        if (!empty($this->userIds)) {
            $usedByAllUsers = true;

            foreach ($this->userIds as $userId) {
                foreach ($this->usages as $usage) {
                    if ($userId == $usage['userId']) {
                        continue 2;
                    }
                }

                $usedByAllUsers = false;
                break;
            }

            if ($usedByAllUsers) {
                $this->valid = false;
                return;
            }
        } elseif (!empty($this->usages)) {
            $this->valid = false;
            return;
Patrick Müller's avatar
Patrick Müller committed
        }
Patrick Müller's avatar
Patrick Müller committed
    }
}