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

namespace QUI\ERP\Coupons;

Henning Leutz's avatar
Henning Leutz committed
use DateTime;
use Exception;
Patrick Müller's avatar
Patrick Müller committed
use QUI;
use QUI\ERP\Discount\Handler as DiscountHandler;
use QUI\ERP\Order\AbstractOrder;
Henning Leutz's avatar
Henning Leutz committed
use QUI\ERP\Order\OrderInterface;
use QUI\ExceptionStack;
use QUI\Interfaces\Users\User;
Patrick Müller's avatar
Patrick Müller committed
use QUI\Permissions\Permission;

Henning Leutz's avatar
Henning Leutz committed
use function array_key_first;
use function count;
use function current;
use function in_array;
use function is_array;
use function is_null;
Henning Leutz's avatar
Henning Leutz committed
use function is_numeric;
Henning Leutz's avatar
Henning Leutz committed
use function json_decode;
use function json_encode;
Henning Leutz's avatar
Henning Leutz committed
use function method_exists;

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 int $id;
Patrick Müller's avatar
Patrick Müller committed

    /**
     * Actual code
     *
     * @var string
     */
Patrick Müller's avatar
Patrick Müller committed

    /**
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
     */
    protected mixed $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
     */
    protected mixed $groupIds = [];
Patrick Müller's avatar
Patrick Müller committed

    /**
     * IDs of all linked discounts
     *
    protected array $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
     */
    protected mixed $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|null
Patrick Müller's avatar
Patrick Müller committed
     */
    protected ?DateTime $CreateDate = null;
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|null
Patrick Müller's avatar
Patrick Müller committed
     */
    protected ?DateTime $ValidUntilDate = null;
Patrick Müller's avatar
Patrick Müller committed

    /**
Patrick Müller's avatar
Patrick Müller committed
     * CouponCode title
Patrick Müller's avatar
Patrick Müller committed
     *
Patrick Müller's avatar
Patrick Müller committed
     */
    protected ?string $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
     */
Henning Leutz's avatar
Henning Leutz committed
    protected bool $valid = true;
Patrick Müller's avatar
Patrick Müller committed

    /**
Patrick Müller's avatar
Patrick Müller committed
     *
     * @var string
Patrick Müller's avatar
Patrick Müller committed
     */
    protected string $maxUsages = Handler::MAX_USAGE_ONCE_PER_USER;
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
Henning Leutz's avatar
Henning Leutz committed
     *
Henning Leutz's avatar
Henning Leutz committed
     * @throws CouponCodeException
     * @throws Exception
Patrick Müller's avatar
Patrick Müller committed
     */
Henning Leutz's avatar
Henning Leutz committed
    public function __construct(int $id)
Patrick Müller's avatar
Patrick Müller committed
    {
Henning Leutz's avatar
Henning Leutz committed
        try {
            $result = QUI::getDataBase()->fetch([
                'from' => Handler::getTable(),
Henning Leutz's avatar
Henning Leutz committed
                'where' => [
                    'id' => $id
                ]
            ]);
Henning Leutz's avatar
Henning Leutz committed
        } catch (QUI\Database\Exception $Exception) {
Henning Leutz's avatar
Henning Leutz committed
            QUI\System\Log::addError($Exception->getMessage());

            throw new CouponCodeException([
                'quiqqer/coupons',
                'exception.CouponCode.not_found',
                [
                    'id' => $id
                ]
            ], 404);
        }
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
        }

Henning Leutz's avatar
Henning Leutz committed
        $data = current($result);
Patrick Müller's avatar
Patrick Müller committed

        $this->id = (int)$data['id'];
        $this->code = $data['code'];
Patrick Müller's avatar
Patrick Müller committed
        $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'])) {
Henning Leutz's avatar
Henning Leutz committed
            $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'])) {
Henning Leutz's avatar
Henning Leutz committed
            $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

Henning Leutz's avatar
Henning Leutz committed
        // migrate user ids
        foreach ($this->userIds as $k => $userId) {
            if (is_numeric($userId)) {
                try {
                    $this->userIds[$k] = QUI::getUsers()->get($userId)->getUUID();
                } catch (QUI\Exception) {
                }
            }
        }

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

Henning Leutz's avatar
Henning Leutz committed
        // migrate user ids
        foreach ($this->groupIds as $k => $groupId) {
            if (is_numeric($groupId)) {
                try {
                    $this->groupIds[$k] = QUI::getGroups()->get($groupId)->getUUID();
                } catch (QUI\Exception) {
                }
            }
        }

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

        if (!empty($data['discountIds'])) {
Henning Leutz's avatar
Henning Leutz committed
            $this->discountIds = json_decode($data['discountIds'], true) ?? [];
Henning Leutz's avatar
Henning Leutz committed
        $this->CreateDate = new DateTime($data['createDate']);
Patrick Müller's avatar
Patrick Müller committed

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

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

    /**
     * @return int
     */
Henning Leutz's avatar
Henning Leutz committed
    public function getId(): int
Patrick Müller's avatar
Patrick Müller committed
    {
        return $this->id;
    }

    /**
     * @return string
     */
Henning Leutz's avatar
Henning Leutz committed
    public function getCode(): string
Patrick Müller's avatar
Patrick Müller committed
    {
        return $this->code;
    }

    /**
     * @return DateTime
Patrick Müller's avatar
Patrick Müller committed
     */
Henning Leutz's avatar
Henning Leutz committed
    public function getCreateDate(): DateTime
Patrick Müller's avatar
Patrick Müller committed
    {
        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
     */
Henning Leutz's avatar
Henning Leutz committed
    public function getUsages(): array
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
Patrick Müller's avatar
Patrick Müller committed
     */
Henning Leutz's avatar
Henning Leutz committed
    public function getValidUntilDate(): ?DateTime
Patrick Müller's avatar
Patrick Müller committed
    {
        return $this->ValidUntilDate;
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     */
Henning Leutz's avatar
Henning Leutz committed
    public function getTitle(): ?string
Patrick Müller's avatar
Patrick Müller committed
    {
        return $this->title;
    }

    /**
     * @return int[]
     */
Henning Leutz's avatar
Henning Leutz committed
    public function getDiscountIds(): array
    {
        return $this->discountIds;
    }

    /**
     * Get all discounts associated with this CouponCode
     *
     * @return QUI\ERP\Discount\Discount[]
     */
Henning Leutz's avatar
Henning Leutz committed
    public function getDiscounts(): array
        $discounts = [];
        $DiscountHandler = DiscountHandler::getInstance();

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

        return $discounts;
    }

Patrick Müller's avatar
Patrick Müller committed
    /**
     * @return int[]
     */
    public function getUserIds(): array
    {
        return $this->userIds;
    }

    /**
     * @return int[]
     */
    public function getGroupIds(): array
    {
        return $this->groupIds;
    }

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 User|null $User - The user that redeems the CouponCode [if omitted use Session User]
     * @param AbstractOrder|null $Order (optional) - Link redemption to a specific Order
Patrick Müller's avatar
Patrick Müller committed
     * @return void
     * @throws CouponCodeException
     * @throws QUI\Database\Exception
     * @throws ExceptionStack
Patrick Müller's avatar
Patrick Müller committed
     */
    public function redeem(QUI\Interfaces\Users\User $User = null, AbstractOrder $Order = null): void
Patrick Müller's avatar
Patrick Müller committed
    {
Henning Leutz's avatar
Henning Leutz committed
        if (is_null($User)) {
Patrick Müller's avatar
Patrick Müller committed
            $User = QUI::getUserBySession();
        }

        $this->checkRedemption($User);

Henning Leutz's avatar
Henning Leutz committed
        $Now = new DateTime();
Henning Leutz's avatar
Henning Leutz committed
            'userId' => $User->getUUID(),
            'date' => $Now->format('Y-m-d H:i:s'),
            'orderPrefixedId' => false
Patrick Müller's avatar
Patrick Müller committed
        ];

Henning Leutz's avatar
Henning Leutz committed
        if ($Order instanceof QUI\ERP\Order\Order) {
            $usage['orderPrefixedId'] = $Order->getPrefixedNumber();
Patrick Müller's avatar
Patrick Müller committed
        QUI::getDataBase()->update(
            Handler::getTable(),
            ['usages' => json_encode($this->usages)],
            ['id' => $this->id]
Patrick Müller's avatar
Patrick Müller committed
        );

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

        QUI::getEvents()->fireEvent(
            'quiqqerCouponsRedeem',
            [
                'User' => $User,
Patrick Müller's avatar
Patrick Müller committed
                '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
     *
     * @param QUI\Interfaces\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(QUI\Interfaces\Users\User $User): void
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);
Henning Leutz's avatar
Henning Leutz committed
            } catch (Exception $Exception) {
                $discountError = $Exception->getMessage();
                continue;
            }

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

        if (!$discountsValid) {
Henning Leutz's avatar
Henning Leutz committed
            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'
                ]);
            }
        }

        // Max usage restrictions
        switch ($this->maxUsages) {
            case Handler::MAX_USAGE_ONCE_PER_USER:
                if ($this->hasUserRedeemed($User)) {
                    throw new CouponCodeException([
                        'quiqqer/coupons',
                        'exception.CouponCode.already_used'
                    ]);
                }
                break;

            case Handler::MAX_USAGE_ONCE:
                if (!empty($this->usages)) {
                    throw new CouponCodeException([
                        'quiqqer/coupons',
                        'exception.CouponCode.already_used'
                    ]);
                }
                break;
        }

        // Restriction to QUIQQER user(s)
Patrick Müller's avatar
Patrick Müller committed
        if (!empty($this->userIds)) {
Henning Leutz's avatar
Henning Leutz committed
            if (in_array($User->getUUID(), $this->userIds)) {
                if (
                    $this->maxUsages !== Handler::MAX_USAGE_UNLIMITED
                    && $this->hasUserRedeemed($User)
                ) {
Patrick Müller's avatar
Patrick Müller committed
                    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
        }

        // Restriction to QUIQQER group(s)
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->maxUsages !== Handler::MAX_USAGE_UNLIMITED
                    && $this->hasUserRedeemed($User)
                ) {
Patrick Müller's avatar
Patrick Müller committed
                    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
    }

Henning Leutz's avatar
Henning Leutz committed
    /**
     * Check if the given Order can redeem this CouponCode
     *
     * @param OrderInterface|null $Order
     * @throws CouponCodeException
     */
    public function checkOrderRedemption(?OrderInterface $Order): void
Henning Leutz's avatar
Henning Leutz committed
    {
        if ($Order === null) {
            return;
        }

        $DiscountHandler = DiscountHandler::getInstance();
        $discountsValid = false;
        $discountError = false;
Henning Leutz's avatar
Henning Leutz committed

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

            if ($Discount->canUsedInOrder($Order)) {
                $discountsValid = true;
                break;
            }
        }

        if (!$discountsValid) {
Henning Leutz's avatar
Henning Leutz committed
            if (count($this->discountIds) === 1) {
Henning Leutz's avatar
Henning Leutz committed
                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
    /**
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
     *
     * @param User|null $User - If omitted, use session user
     * @param OrderInterface|null $Order
Patrick Müller's avatar
Patrick Müller committed
     * @return bool
     */
    public function isRedeemable(QUI\Interfaces\Users\User $User = null, OrderInterface $Order = null): bool
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        try {
            $this->checkRedemption($User);
        } catch (CouponCodeException) {
Patrick Müller's avatar
Patrick Müller committed
            return false;
        }

Henning Leutz's avatar
Henning Leutz committed
        if ($Order) {
            try {
                $this->checkOrderRedemption($Order);
            } catch (CouponCodeException $Exception) {
                $Order->addFrontendMessage($Exception->getMessage());

                return false;
            }
        }

Patrick Müller's avatar
Patrick Müller committed
        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
     */
Henning Leutz's avatar
Henning Leutz committed
    public function isValid(): bool
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
     *
Henning Leutz's avatar
Henning Leutz committed
     * @param QUI\Interfaces\Users\User $User
Patrick Müller's avatar
Patrick Müller committed
     * @return bool
Patrick Müller's avatar
Patrick Müller committed
     */
Henning Leutz's avatar
Henning Leutz committed
    public function hasUserRedeemed(QUI\Interfaces\Users\User $User): bool
Patrick Müller's avatar
Patrick Müller committed
    {
Henning Leutz's avatar
Henning Leutz committed
        $userId = $User->getUUID();
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(): void
Patrick Müller's avatar
Patrick Müller committed
    {
        Permission::checkPermission(Handler::PERMISSION_DELETE);

Henning Leutz's avatar
Henning Leutz committed
        try {
            QUI::getDataBase()->delete(
                Handler::getTable(),
                ['id' => $this->id]
            );
Henning Leutz's avatar
Henning Leutz committed
        } catch (QUI\Database\Exception $Exception) {
Henning Leutz's avatar
Henning Leutz committed
            QUI\System\Log::addError($Exception->getMessage());
        }
        // If hidden discount are connected to this coupon -> delete them as well
        foreach ($this->getDiscounts() as $Discount) {
            if (!empty($Discount->getAttribute('hidden'))) {
                try {
                    $Discount->delete();
Henning Leutz's avatar
Henning Leutz committed
                } catch (Exception $Exception) {
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
     */
Henning Leutz's avatar
Henning Leutz committed
    public function toArray(): array
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        $data = [
            'id' => $this->getId(),
            'code' => $this->getCode(),
            'userIds' => $this->userIds,
            'groupIds' => $this->groupIds,
            'createDate' => $this->getCreateDate()->format('Y-m-d H:i:s'),
            'usages' => $this->usages,
Patrick Müller's avatar
Patrick Müller committed
            'validUntilDate' => false,
            'title' => $this->getTitle() ?: false,
            'isValid' => $this->isValid(),
            'maxUsages' => $this->maxUsages,
            '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(): void
Patrick Müller's avatar
Patrick Müller committed
    {
        // Check if the expiration date has been reached
        if (!empty($this->ValidUntilDate)) {
Henning Leutz's avatar
Henning Leutz committed
            $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;
Patrick Müller's avatar
Patrick Müller committed
                return;
            }
        }
Patrick Müller's avatar
Patrick Müller committed

        if ($this->maxUsages === Handler::MAX_USAGE_UNLIMITED) {
            return;
        }

        if ($this->maxUsages === Handler::MAX_USAGE_ONCE && !empty($this->usages)) {
            $this->valid = false;
Patrick Müller's avatar
Patrick Müller committed
            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;
            }
        }
    }

    /**
     * @param QUI\ERP\Order\OrderInProcess $Order
     * @throws QUI\Exception
     */
    public function addToOrder(QUI\ERP\Order\OrderInProcess $Order): void
    {
        $coupons = $Order->getDataEntry('quiqqer-coupons');

        if (!$coupons) {
            return;
        }

Henning Leutz's avatar
Henning Leutz committed
        if (!is_array($coupons)) {
            return;
Patrick Müller's avatar
Patrick Müller committed
        }

        $priceFactors = [];
        $articles = [];
        $calculations = $Order->getArticles()->getCalculations();
        $vatArray = $calculations['vatArray'];
        $vat = false;
Henning Leutz's avatar
Henning Leutz committed
        if (count($vatArray) === 1) {
            $vat = array_key_first($vatArray);

        foreach ($coupons as $coupon) {
            /* @var $Coupon CouponCode */
            try {
                $Coupon = Handler::getCouponCodeByCode($coupon);
                continue;
            }

            // coupon check
Henning Leutz's avatar
Henning Leutz committed
            if (!$Coupon->isRedeemable($Order->getCustomer(), $Order)) {
                continue;
            }

            /* @var $Discount QUI\ERP\Discount\Discount */
            $discounts = $Coupon->getDiscounts();

            foreach ($discounts as $Discount) {
                $PriceFactor = $Discount->toPriceFactor(null, $Order->getCustomer());

                if ($vat !== false) {
                    $PriceFactor->setVat($vat);
                }

                $PriceFactor->setTitle(
                    QUI::getLocale()->get('quiqqer/coupons', 'coupon.discount.title', [
                        'code' => $Coupon->getCode()
                    ])
                );

                $priceFactors[] = $PriceFactor;
                // @todo wenn fest preis (zb 10$), dann eigener produkt typ hinzufügen

                $articles[] = new QUI\ERP\Accounting\Articles\Text([
                    'id' => -1,
                    'articleNo' => $Coupon->getCode(),
                    'title' => $PriceFactor->getTitle(),
Henning Leutz's avatar
Henning Leutz committed
                    'description' => '',
                    'unitPrice' => 0,
                    'control' => '',
                    'quantity' => 1,
                    'customData' => [
Henning Leutz's avatar
Henning Leutz committed
                        'package' => 'quiqqer/coupon',
                        'code' => $Coupon->getCode()
Henning Leutz's avatar
Henning Leutz committed
        if (empty($priceFactors)) {
            return;
Henning Leutz's avatar
Henning Leutz committed
         * @param $Article
Henning Leutz's avatar
Henning Leutz committed
         * @return boolean
         */
        $isInArticles = function ($Article) use ($Order) {
            $articles = $Order->getArticles();
            $code = $Article->getCustomData()['code'];
Henning Leutz's avatar
Henning Leutz committed

            foreach ($articles as $Entry) {
Henning Leutz's avatar
Henning Leutz committed
                if (!method_exists($Entry, 'getCustomData')) {
Henning Leutz's avatar
Henning Leutz committed
                    continue;
                }

                $customData = $Entry->getCustomData();

Henning Leutz's avatar
Henning Leutz committed
                if (!$customData || !is_array($customData)) {
Henning Leutz's avatar
Henning Leutz committed
                    continue;
                }

                if (!isset($customData['package']) || !isset($customData['code'])) {
                    continue;
                }

                return $customData['package'] === 'quiqqer/coupon'
                    && $customData['code'] === $code;
Henning Leutz's avatar
Henning Leutz committed
            }

            return false;
        };

        foreach ($articles as $Article) {
Henning Leutz's avatar
Henning Leutz committed
            /* @var $PriceFactor QUI\ERP\Accounting\Articles\Text */
Henning Leutz's avatar
Henning Leutz committed
            if ($isInArticles($Article) === false) {
                $Order->addArticle($Article);
            }
        }

        $Order->update();
        $Order->addPriceFactors($priceFactors);
Patrick Müller's avatar
Patrick Müller committed
    }
}