Skip to content
Code-Schnipsel Gruppen Projekte
Events.php 29 KiB
Newer Older
<?php

namespace QUI\ERP\Coupons;

Henning Leutz's avatar
Henning Leutz committed
use Exception;
use QUI;
Henning Leutz's avatar
Henning Leutz committed
use QUI\ERP\Accounting\Calc as ErpCalc;
use QUI\ERP\Coupons\Handler as CouponsHandler;
use QUI\ERP\Coupons\Products\CouponProductException;
use QUI\ERP\Coupons\Products\DigitalCouponProductType;
use QUI\ERP\Coupons\Products\Handler as CouponProductsHandler;
use QUI\ERP\Coupons\Products\PhysicalCouponProductType;
use QUI\ERP\Discount\EventHandling as DiscountEvents;
Patrick Müller's avatar
Patrick Müller committed
use QUI\ERP\Order\AbstractOrder;
Henning Leutz's avatar
Henning Leutz committed
use QUI\ERP\Order\Basket\Basket;
Patrick Müller's avatar
Patrick Müller committed
use QUI\ERP\Products\Handler\Fields;
use QUI\ERP\Products\Interfaces\ProductInterface;
Henning Leutz's avatar
Henning Leutz committed
use QUI\System\Console\Tools\MigrationV2;

Henning Leutz's avatar
Henning Leutz committed
use function array_merge;
use function array_search;
use function array_unique;
use function in_array;
use function is_array;
Henning Leutz's avatar
Henning Leutz committed
use function is_numeric;
Henning Leutz's avatar
Henning Leutz committed
use function is_string;
use function json_decode;
use function json_encode;
use function method_exists;
Henning Leutz's avatar
Henning Leutz committed

/**
 * Class Events
 *
 * Global Event Handler for quiqqer/payment-paypal
 */
class Events
{
Patrick Müller's avatar
Patrick Müller committed
    /**
     * quiqqer/core: onPackageSetup
Patrick Müller's avatar
Patrick Müller committed
     *
     * @param QUI\Package\Package $Package
     * @return void
     */
    public static function onPackageSetup(QUI\Package\Package $Package): void
Patrick Müller's avatar
Patrick Müller committed
    {
        try {
            self::createProductFields();
Henning Leutz's avatar
Henning Leutz committed
        } catch (Exception $Exception) {
Patrick Müller's avatar
Patrick Müller committed
            QUI\System\Log::writeException($Exception);
        }
    }

Henning Leutz's avatar
Henning Leutz committed
    /**
     * event : on admin load footer
     */
    public static function onAdminLoadFooter(): void
Henning Leutz's avatar
Henning Leutz committed
    {
        echo '<script src="' . URL_OPT_DIR . 'quiqqer/coupons/bin/backend/load.js"></script>';
    }

    /**
     * Template event quiqqer/order: onQuiqqer::order::orderProcessBasketEnd
     *
     * @param Collector $Collector
    public static function templateOrderProcessBasketEnd(
        Collector $Collector,
        mixed $Basket,
        if (
            !($Basket instanceof Basket)
            && !($Basket instanceof QUI\ERP\Order\Basket\BasketOrder)
        ) {
        if (isset($Order) && isset($_GET['coupon'])) {
            try {
                $code = Handler::sanitizeCode($_GET['coupon']);

                $CouponCode = Handler::getCouponCodeByCode($code);
                $CouponCode->checkRedemption(QUI::getUserBySession());

                if ($Order instanceof QUI\ERP\Order\OrderInProcess) {
                    $CouponCode->addToOrder($Order);
                }
            } catch (Exception) {
        $Collector->append(
            '<div data-qui="package/quiqqer/coupons/bin/frontend/controls/CouponCodeInput"></div>'
        );
    }

    public static function templateOrderSimpleOrder(
        Collector $Collector,
        AbstractOrder $Order
    ): void {
        if ($Order instanceof QUI\ERP\Order\OrderInProcess && isset($_GET['coupon'])) {
            try {
                $code = Handler::sanitizeCode($_GET['coupon']);

                $CouponCode = Handler::getCouponCodeByCode($code);
                $CouponCode->checkRedemption(QUI::getUserBySession());
                $CouponCode->addToOrder($Order);
            } catch (Exception) {
            }
        }

        $Collector->append(
            '<div data-qui="package/quiqqer/coupons/bin/frontend/controls/CouponCodeInput"></div>'
        );
    }

    /**
     * @param QUI\ERP\Order\OrderProcess $OrderProcess
     * @throws QUI\ERP\Order\Exception
     * @throws QUI\Exception
     */
    public static function onOrderProcess(QUI\ERP\Order\OrderProcess $OrderProcess): void
    {
        $CurrentStep = $OrderProcess->getCurrentStep();
        $currentStep = $CurrentStep->getType();

        if ($currentStep !== QUI\ERP\Order\Controls\OrderProcess\Basket::class) {
            return;
        }

        $coupons = QUI::getSession()->get('quiqqer-coupons');

Henning Leutz's avatar
Henning Leutz committed
        if (is_string($coupons)) {
            $coupons = json_decode($coupons, true);
Henning Leutz's avatar
Henning Leutz committed
        if (!is_array($coupons)) {
            $coupons = [];
        }

        if (isset($_GET['coupon'])) {
            $coupons[] = $_GET['coupon'];
        }

Henning Leutz's avatar
Henning Leutz committed
        $coupons = array_unique($coupons);

        if (empty($coupons)) {
        $Order = $OrderProcess->getOrder();

        foreach ($coupons as $coupon) {
            self::addCouponToOrder($Order, $coupon);
        }
    }

    /**
     * @param QUI\ERP\Order\Basket\Basket $Basket
     * @param $pos
     */
    public static function onQuiqqerOrderBasketRemovePos(
        QUI\ERP\Order\Basket\Basket $Basket,
        $pos
        $Order = null;
            $Order = $Basket->getOrder();
            $Orders = QUI\ERP\Order\Handler::getInstance();
            try {
                $Order = $Orders->getLastOrderInProcessFromUser(QUI::getUserBySession());
        if (!$Order) {
            QUI::getSession()->remove('quiqqer-coupons');
            return;
        }
        $Article = $Order->getArticles()->getArticle($pos);
        $customData = $Article->getCustomData();
        $orderCoupons = $Order->getDataEntry('quiqqer-coupons');
        $articleCouponCode = false;

        if (isset($customData['package']) && isset($customData['code'])) {
            $articleCouponCode = $customData['code'];
        }

        if (!$articleCouponCode) {
            return;
        }

        // custom data has code params, so article is a coupon code
        // we need to delete it
Henning Leutz's avatar
Henning Leutz committed
        if (in_array($articleCouponCode, $orderCoupons)) {
            $pos = array_search($articleCouponCode, $orderCoupons);
            unset($orderCoupons[$pos]);

            $Order->setData('quiqqer-coupons', $orderCoupons);

            try {
                if (method_exists($Order, 'save')) {
                    $Order->save();
                }
            }
        }

        // look at session coupons
        // we need to delete it
        $coupons = QUI::getSession()->get('quiqqer-coupons');

Henning Leutz's avatar
Henning Leutz committed
        if (is_string($coupons)) {
            $coupons = json_decode($coupons, true);
Henning Leutz's avatar
Henning Leutz committed
        if (!is_array($coupons) || empty($coupons)) {
Henning Leutz's avatar
Henning Leutz committed
        if (!in_array($customData['code'], $coupons)) {
            return;
        }

        // remove code from session
        // because code is deleted
        $newCouponList = [];

        foreach ($coupons as $coupon) {
            if ($customData['code'] !== $coupon) {
                $newCouponList[] = $coupon;
            }
        }

        if (empty($newCouponList)) {
            QUI::getSession()->remove('quiqqer-coupons');
        } else {
Henning Leutz's avatar
Henning Leutz committed
            QUI::getSession()->set('quiqqer-coupons', json_encode($newCouponList));
    /**
     * event - on price factor init
     *
     * @param QUI\ERP\Order\AbstractOrder $Order
     * @param QUI\ERP\Products\Product\ProductList $Products
     */
    public static function onQuiqqerOrderBasketToOrder(
        QUI\ERP\Order\AbstractOrder $Order,
        QUI\ERP\Products\Product\ProductList $Products
        $coupons = $Order->getDataEntry('quiqqer-coupons');
        $sessionCoupons = QUI::getSession()->get('quiqqer-coupons');
Henning Leutz's avatar
Henning Leutz committed
        if (!is_array($coupons)) {
            $coupons = [];
Henning Leutz's avatar
Henning Leutz committed
        if (is_string($sessionCoupons)) {
            $sessionCoupons = json_decode($sessionCoupons, true);
Henning Leutz's avatar
Henning Leutz committed
            if (is_array($sessionCoupons)) {
                $coupons = array_merge($coupons, $sessionCoupons);
Henning Leutz's avatar
Henning Leutz committed
            $coupons = array_unique($coupons);

            self::addSessionCouponsToOrder($Order, $sessionCoupons);
        $PriceFactors = $Products->getPriceFactors();
        $products = $Products->toArray();
        $productCount = $Products->count();
        $subSum = $products['calculations']['subSum'];
        $checkRedeemable = !$Order->isSuccessful(); // if order is successful we don't need a check
        $OrderInProcess = $Order->getAttribute('OrderInProcess');
        $added = false;

        if ($Order->getAttribute('inOrderCreation')) {
            $checkRedeemable = false;
        }

        if (
            $OrderInProcess instanceof QUI\ERP\Order\OrderInProcess
            && $OrderInProcess->getAttribute('inOrderCreation')
        ) {
            $checkRedeemable = false;
        }

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

            // coupon check
            if ($checkRedeemable && !$Coupon->isRedeemable($Order->getCustomer())) {
                continue;
            }

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

            foreach ($discounts as $Discount) {
                if (!DiscountEvents::isDiscountUsableWithQuantity($Discount, $productCount)) {
                    continue;
                }

Henning Leutz's avatar
Henning Leutz committed
                if ($Discount->getAttribute('scope') === QUI\ERP\Discount\Handler::DISCOUNT_SCOPE_GRAND_TOTAL) {
                    // do nothing for this scope
                    // since this scope requires all price factors etc., this cannot be calculated here
Henning Leutz's avatar
Henning Leutz committed
                } elseif (!DiscountEvents::isDiscountUsableWithPurchaseValue($Discount, $subSum)) {
                $PriceFactor = $Discount->toPriceFactor(null, $Order->getCustomer());
                $PriceFactor->setTitle(
                    QUI::getLocale()->get('quiqqer/coupons', 'coupon.discount.title', [
                        'code' => $Coupon->getCode()
                    ])
                $scope = $Discount->getAttribute('scope');
                $isUnique = $scope === QUI\ERP\Discount\Handler::DISCOUNT_SCOPE_UNIQUE;
Henning Leutz's avatar
Henning Leutz committed
                $everyProduct = $scope === QUI\ERP\Discount\Handler::DISCOUNT_SCOPE_EVERY_PRODUCT;
Henning Leutz's avatar
Henning Leutz committed

                if ($everyProduct || $isUnique) {
                    // add to the product
                    $products = $Products->getProducts();
Henning Leutz's avatar
Henning Leutz committed
                    $alreadyAdded = false;
Henning Leutz's avatar
Henning Leutz committed

                    foreach ($products as $Product) {
                        if ($Discount->canUsedWith($Product) === false) {
                            continue;
                        }

Henning Leutz's avatar
Henning Leutz committed
                        if ($isUnique && $alreadyAdded) {
                            continue;
                        }

                        if ($Product instanceof QUI\ERP\Products\Product\UniqueProduct) {
                            $Product->getPriceFactors()->add($PriceFactor);
                            $added = true;
Henning Leutz's avatar
Henning Leutz committed
                            $alreadyAdded = true;
                $PriceFactors->addToEnd($PriceFactor);
            }
        }

        if ($added) {
            try {
                $Products->recalculation();
            } catch (QUI\Exception $Exception) {
                QUI\System\Log::writeDebugException($Exception);
            }

    /**
     * quiqqer/order: onQuiqqerOrderSuccessful
     *
     * Redeem coupons used in (completed) orders
     *
     * @param AbstractOrder $Order
    public static function onQuiqqerOrderSuccessful(AbstractOrder $Order): void
    {
        $coupons = $Order->getDataEntry('quiqqer-coupons');

        foreach ($coupons as $couponCode) {
            try {
                $CouponCode = CouponsHandler::getCouponCodeByCode($couponCode);
                $CouponCode->redeem($Order->getCustomer(), $Order);
Henning Leutz's avatar
Henning Leutz committed
            } catch (Exception $Exception) {
                QUI\System\Log::writeException($Exception);
            }
        }
    }

    /**
     * @param $Order
     * @param $coupons
     */
    protected static function addSessionCouponsToOrder($Order, $coupons): void
Henning Leutz's avatar
Henning Leutz committed
        if (!is_array($coupons)) {
            return;
        }

        // coupons as article if not added
        $Articles = $Order->getArticles();
        $isInArticles = function ($code) use ($Articles) {
            foreach ($Articles as $Article) {
                $customData = $Article->getCustomData();

                if (isset($customData['code']) && $customData['code'] === $code) {
                    return true;
                }
            }

            return false;
        };

        foreach ($coupons as $coupon) {
            if ($isInArticles($coupon) === false) {
                self::addCouponToOrder($Order, $coupon);
            }
        }
    }

    /**
     * @param $Order
     * @param $coupon
     */
    protected static function addCouponToOrder($Order, $coupon): void
        if (!($Order instanceof QUI\ERP\Order\OrderInProcess)) {
            return;
        }

        try {
            $code = Handler::sanitizeCode($coupon);

            $CouponCode = Handler::getCouponCodeByCode($code);
            $CouponCode->checkRedemption(QUI::getUserBySession());
Henning Leutz's avatar
Henning Leutz committed
            $CouponCode->checkOrderRedemption($Order);
            $coupons = $Order->getDataEntry('quiqqer-coupons');
            $coupons[] = $code;

Henning Leutz's avatar
Henning Leutz committed
            $coupons = array_unique($coupons);

            $Order->setData('quiqqer-coupons', $coupons);
            $Order->update();

            $CouponCode->addToOrder($Order);
     * Removes all coupons from the current session.
Henning Leutz's avatar
Henning Leutz committed
     *
     * @return void
Henning Leutz's avatar
Henning Leutz committed
     */
    public static function removeCouponsFromSession(): void
Henning Leutz's avatar
Henning Leutz committed
    {
        QUI::getSession()->remove('quiqqer-coupons');
    }
Patrick Müller's avatar
Patrick Müller committed
    /**
     * Create all fixed product fields that quiqqer/stock-management provides
     *
     * @return void
     * @throws QUI\Exception
     */
    protected static function createProductFields(): void
Patrick Müller's avatar
Patrick Müller committed
    {
        $fields = [
            CouponProductsHandler::PRODUCT_FIELD_ID_TRANSFERABLE => [
                'title' => [
Patrick Müller's avatar
Patrick Müller committed
                    'de' => 'Gutschein-Code ist übertragbar',
                    'en' => 'Coupon code is transferable'
                ],
                'description' => [
                    'de' => 'Übertragbare Gutscheine sind auch von anderen Personen als dem Käufer einlösbar.'
Henning Leutz's avatar
Henning Leutz committed
                        . ' Nicht übertragbare Gutscheine können nur vom Käufer eingelöst werden, wenn dieser'
                        . ' eingeloggt ist.',
                    'en' => 'Transferable coupons are also redeemable by persons other than the buyer.'
Henning Leutz's avatar
Henning Leutz committed
                        . ' Non-transferable vouchers can only be redeemed by the buyer when logged in.'
                'type' => Fields::TYPE_BOOL,
                'public' => false,
                'standard' => false,
Patrick Müller's avatar
Patrick Müller committed
                'requiredField' => false
Patrick Müller's avatar
Patrick Müller committed
            ],
            CouponProductsHandler::PRODUCT_FIELD_ID_SEND_MAIL => [
                'title' => [
                    'de' => 'Gutschein-Code per E-Mail senden',
                    'en' => 'Send coupon code via email'
                ],
                'description' => [
                    'de' => 'Der Gutschein-Code wird dem Käufer per E-Mail gesendet. Gilt nicht, wenn der Benutzer zw.'
Henning Leutz's avatar
Henning Leutz committed
                        . ' Post- und Mail-Versand wählen kann und den Postversand auswählt.'
                        . ' Wird der Gutschein auch als PDF-Datei generiert, wird die PDF-Datei an diese E-Mail angehanden.',
                    'en' => 'The coupon code is sent to the buyer via email. It is not sent if the customer is able to'
Henning Leutz's avatar
Henning Leutz committed
                        . ' choose between email and mail delivery type and chooses delivery by mail.'
                        . ' If the coupon is also generated as a PDF file, the file is attached to this email.'
                'type' => Fields::TYPE_BOOL,
                'public' => false,
                'standard' => false,
            CouponProductsHandler::PRODUCT_FIELD_ID_GENERATE_PDF => [
                'title' => [
Patrick Müller's avatar
Patrick Müller committed
                    'de' => 'Gutschein-Code als PDF bereitstellen',
Patrick Müller's avatar
Patrick Müller committed
                    'en' => 'Provide coupon code as PDF'
Patrick Müller's avatar
Patrick Müller committed
                ],
                'description' => [
                    'de' => 'Der Gutschein wird auch als PDF-Datei erstellt und dem Käufer (je nach Wahl) per E-Mail gesendet.'
Henning Leutz's avatar
Henning Leutz committed
                        . ' Zusätzlich wird die PDF-Datei dem Käufer in seinem Frontend-Profil (sofern eingerichtet)'
                        . ' bereitgestellt.',
                    'en' => 'The coupon is also generated as a PDF file and is sent to the customer (depending on choice)'
Henning Leutz's avatar
Henning Leutz committed
                        . ' via email. Additionally, the PDF file is made available via the customers frontend profile'
                        . ' (if set up).'
                'type' => Fields::TYPE_BOOL,
                'public' => false,
                'standard' => false,
Patrick Müller's avatar
Patrick Müller committed
                'requiredField' => false
Patrick Müller's avatar
Patrick Müller committed
            ],
            CouponProductsHandler::PRODUCT_FIELD_ID_COUPON_AMOUNT => [
                'title' => [
Patrick Müller's avatar
Patrick Müller committed
                    'de' => 'Gutschein Wert',
                    'en' => 'Coupon amount'
Patrick Müller's avatar
Patrick Müller committed
                ],
                'type' => Fields::TYPE_FLOAT,
                'public' => false,
                'standard' => false,
Patrick Müller's avatar
Patrick Müller committed
                'requiredField' => true
Patrick Müller's avatar
Patrick Müller committed
            ],
            CouponProductsHandler::PRODUCT_FIELD_ID_DAYS_VALID => [
                'title' => [
Patrick Müller's avatar
Patrick Müller committed
                    'de' => 'Gutschein-Code Gültigkeit (Tage)',
                    'en' => 'Coupon code validity (days)'
                ],
                'type' => Fields::TYPE_INT,
                'public' => false,
                'standard' => false,
Patrick Müller's avatar
Patrick Müller committed
                'requiredField' => true
Patrick Müller's avatar
Patrick Müller committed
            ],
            CouponProductsHandler::PRODUCT_FIELD_ID_COUPON_DESCRIPTION => [
                'title' => [
Patrick Müller's avatar
Patrick Müller committed
                    'de' => 'Gutschein-Beschreibung',
                    'en' => 'Coupon description'
                ],
                'description' => [
                    'de' => 'Diese Beschreibung taucht unter dem Titel "GUTSCHEIN" auf der generierten PDF-Datei auf.',
                    'en' => 'This description appears under the title caption "COUPON" on the generated PDF file.'
                ],
                'type' => Fields::TYPE_INPUT_MULTI_LANG,
                'public' => false,
                'standard' => false,
                'requiredField' => false
            CouponProductsHandler::PRODUCT_FIELD_ID_IS_SINGLE_PURPOSE_COUPON => [
                'title' => [
                    'de' => 'Ist Einzweck-Gutschein (Besteuerung bei Gutschein-Kauf)',
                    'en' => 'Is single purpose coupon (taxation on voucher purchase)'
                'description' => [
                    'de' => 'Einzweck-Gutscheine sind solche, bei denen die Besteuerung und der Leistungsort bereits'
Henning Leutz's avatar
Henning Leutz committed
                        . ' beim Gutschein-Kauf feststehen. Beispiel: Gutschein für eine Massage in einem Spa.'
                        . ' Alles andere (wie z.B. Wertgutscheine für den Einsatz unabhängig vom Artikel) sind'
                        . ' Mehrzweck-Gutscheine und werden bei Einkauf nicht besteuert.',
                    'en' => 'Single-purpose coupons are those for which the taxation and place of performance are already'
Henning Leutz's avatar
Henning Leutz committed
                        . ' determined at the time of coupon purchase. Example: voucher for a massage in a spa.'
                        . ' Everything else (such as money value coupons for use regardless of the item) are'
                        . ' multi-purpose coupons and are not taxed at the time of purchase.'
                'type' => Fields::TYPE_BOOL,
                'public' => false,
                'standard' => false,
Patrick Müller's avatar
Patrick Müller committed
                'requiredField' => false
            CouponProductsHandler::PRODUCT_FIELD_ID_USER_DELIVERY_TYPE_SELECT => [
                'title' => [
                    'de' => 'Gutschein - Versand',
                'type' => Fields::TYPE_ATTRIBUTE_LIST,
                'public' => true,
                'standard' => false,
                'options' => [
                            'title' => [
                            'sum' => 0,
                            'type' => ErpCalc::CALCULATION_COMPLEMENT,
                            'selected' => true,
                            'title' => [
                            'sum' => 0,
                            'type' => ErpCalc::CALCULATION_COMPLEMENT,
                            'selected' => false,
            CouponProductsHandler::PRODUCT_FIELD_ID_USER_DELIVERY_TYPE_SELECT_ALLOW => [
                'title' => [
                    'de' => 'Kunde darf Gutschein - Versandart wählen',
                    'en' => 'Customer can choose coupon delivery type'
                ],
                'description' => [
                    'de' => 'Ist diese Funktion aktiviert, kann der Kunde beim Artikel im Shop wählen, ob der Gutschein'
Henning Leutz's avatar
Henning Leutz committed
                        . ' per E - Mail oder Post versandt werden soll.',
                    'en' => 'if this option is enabled, the customer can choose at the article in the store whether the'
Henning Leutz's avatar
Henning Leutz committed
                        . ' coupon should be sent by e - mail or by(physical) mail.'
                'type' => Fields::TYPE_BOOL,
                'public' => false,
                'standard' => false,
Patrick Müller's avatar
Patrick Müller committed
            ]
        ];

        $fieldsCreated = false;

        foreach ($fields as $fieldId => $field) {
            try {
                Fields::getField($fieldId);
                continue;
Patrick Müller's avatar
Patrick Müller committed
                // Field does not exist -> create it
            }

            try {
                Fields::createField([
                    'id' => $fieldId,
                    'type' => $field['type'],
                    'titles' => $field['title'],
Patrick Müller's avatar
Patrick Müller committed
                    'workingtitles' => $field['title'],
                    'description' => !empty($field['description']) ? $field['description'] : null,
                    'systemField' => 0,
Patrick Müller's avatar
Patrick Müller committed
                    'standardField' => !empty($field['standard']) ? 1 : 0,
                    'publicField' => !empty($field['public']) ? 1 : 0,
                    'options' => !empty($field['options']) ? $field['options'] : null,
Patrick Müller's avatar
Patrick Müller committed
                    'requiredField' => !empty($field['requiredField']) ? 1 : 0
Patrick Müller's avatar
Patrick Müller committed
                ]);
Henning Leutz's avatar
Henning Leutz committed
            } catch (Exception $Exception) {
Patrick Müller's avatar
Patrick Müller committed
                QUI\System\Log::writeException($Exception);
                continue;
            }

            $fieldsCreated = true;
        }

        if ($fieldsCreated) {
            QUI\Translator::publish('quiqqer / products');
Patrick Müller's avatar
Patrick Müller committed
        }
    }

    /**
     * Assign plan product fields to a product
     *
     * @param ProductInterface $Product
     * @return void
Patrick Müller's avatar
Patrick Müller committed
     */
    public static function onQuiqqerProductsProductCreate(ProductInterface $Product): void
Patrick Müller's avatar
Patrick Müller committed
    {
        if (!($Product instanceof DigitalCouponProductType) && !($Product instanceof PhysicalCouponProductType)) {
Patrick Müller's avatar
Patrick Müller committed
            return;
        }

        $isDigital = $Product instanceof DigitalCouponProductType;
Patrick Müller's avatar
Patrick Müller committed
        $UniqueProduct = $Product->createUniqueProduct();
        $UniqueProduct->calc();

Patrick Müller's avatar
Patrick Müller committed
        $fields = [
//            CouponProductsHandler::PRODUCT_FIELD_ID_TRANSFERABLE             => true,
            CouponProductsHandler::PRODUCT_FIELD_ID_COUPON_AMOUNT => $UniqueProduct->getPrice()->getValue(),
            CouponProductsHandler::PRODUCT_FIELD_ID_DAYS_VALID => 1095, // 3 years
            CouponProductsHandler::PRODUCT_FIELD_ID_IS_SINGLE_PURPOSE_COUPON => false
Patrick Müller's avatar
Patrick Müller committed
        ];

        // Digital coupons get some extra fields
        if ($isDigital) {
            $fields[CouponProductsHandler::PRODUCT_FIELD_ID_SEND_MAIL] = true;
            $fields[CouponProductsHandler::PRODUCT_FIELD_ID_GENERATE_PDF] = true;
            $fields[CouponProductsHandler::PRODUCT_FIELD_ID_USER_DELIVERY_TYPE_SELECT] = false;
            $fields[CouponProductsHandler::PRODUCT_FIELD_ID_COUPON_DESCRIPTION] = null;
Patrick Müller's avatar
Patrick Müller committed
        foreach ($fields as $fieldId => $value) {
            try {
                $Field = Fields::getField($fieldId);
                $Field->setValue($value);

                $Product->addOwnField($Field);
Henning Leutz's avatar
Henning Leutz committed
            } catch (Exception $Exception) {
Patrick Müller's avatar
Patrick Müller committed
                QUI\System\Log::writeException($Exception);
            }
        }

        // No VAT tax types -> Choose first one found
        $noVatTaxTypes = QUI\ERP\Coupons\Products\Handler::getNoVatTaxTypes();

        if (!empty($noVatTaxTypes)) {
            $Product->getField(Fields::FIELD_VAT)->setValue($noVatTaxTypes[0]->getId());
        }

Patrick Müller's avatar
Patrick Müller committed
        try {
            $Product->update(QUI::getUsers()->getSystemUser());
Henning Leutz's avatar
Henning Leutz committed
        } catch (Exception $Exception) {
Patrick Müller's avatar
Patrick Müller committed
            QUI\System\Log::writeException($Exception);
        }
    }

    /**
     * quiqqer/products: onQuiqqerProductsProductActivate
     *
     * Check if a coupon product has the correct tax type!
     *
     * @param ProductInterface $Product
     *
     * @throws CouponProductException
     * @throws QUI\Exception
     */
    public static function onQuiqqerProductsProductActivate(ProductInterface $Product): void
        if (!($Product instanceof DigitalCouponProductType) && !($Product instanceof PhysicalCouponProductType)) {
Henning Leutz's avatar
Henning Leutz committed
        $isSinglePurposeCoupon = $Product->getFieldValue(
            CouponProductsHandler::PRODUCT_FIELD_ID_IS_SINGLE_PURPOSE_COUPON
        );

        if (!empty($isSinglePurposeCoupon)) {
            return;
        }

        $productTaxTypeId = (int)$Product->getFieldValue(Fields::FIELD_VAT);
        $noVatTaxTypes = QUI\ERP\Coupons\Products\Handler::getNoVatTaxTypes();

        foreach ($noVatTaxTypes as $TaxType) {
            if ($TaxType->getId() === $productTaxTypeId) {
                return;
            }
        }

        throw new CouponProductException([
            'exception.CouponProduct.no_vat_tax_type_required',
            [
                'productTitle' => $Product->getTitle(),
                'productId' => $Product->getId()
Patrick Müller's avatar
Patrick Müller committed
    /**
     * quiqqer/order: onQuiqqerOrderCreated
     *
     * Parse coupon attributes from order and create coupon codes for the buyer.
     *
     * @param AbstractOrder $Order
     * @return void
     */
    public static function onQuiqqerOrderCreated(AbstractOrder $Order): void
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        QUI\ERP\Coupons\Products\Handler::createCouponCodesFromOrder($Order);
Patrick Müller's avatar
Patrick Müller committed
    }
Henning Leutz's avatar
Henning Leutz committed

    public static function onQuiqqerMigrationV2(MigrationV2 $Console): void
    {
        $Console->writeLn('- Migrate coupons');
        $table = QUI::getDBTableName('quiqqer_coupons');

        $result = QUI::getDataBase()->fetch([
            'from' => $table
        ]);

        foreach ($result as $entry) {
            $id = $entry['id'];
            $userIds = $entry['userIds'];
            $groupIds = $entry['groupIds'];

            if (!empty($userIds)) {
                $userIds = json_decode($userIds, true) ?? [];

                foreach ($userIds as $k => $userId) {
                    if (is_numeric($userId)) {
                        try {
                            $userIds[$k] = QUI::getUsers()->get($userId)->getUUID();
                        } catch (QUI\Exception) {
                        }
                    }
                }

                QUI::getDataBase()->update(
                    $table,
                    ['userIds' => json_encode($userIds)],
                    ['id' => $id]
                );
            }

            if (!empty($groupIds)) {
                $groupIds = json_decode($groupIds, true) ?? [];

                foreach ($groupIds as $k => $groupId) {
                    if (is_numeric($groupId)) {
                        try {
                            $groupIds[$k] = QUI::getGroups()->get($groupId)->getUUID();
                        } catch (QUI\Exception) {
                        }
                    }
                }

                QUI::getDataBase()->update(
                    $table,
                    ['groupIds' => json_encode($groupIds)],
                    ['id' => $id]
                );
            }
        }
    }