diff --git a/ajax/create.php b/ajax/create.php index 8058856f661106193bcd52c9a09de5d1cdfdac59..8ef89fbc60fb690a9cae960f4d64301528096f39 100644 --- a/ajax/create.php +++ b/ajax/create.php @@ -1,11 +1,9 @@ <?php -/** - * This file contains package_quiqqer_coupons_ajax_create - */ - use QUI\ERP\Coupons\Handler; use QUI\Utils\Security\Orthos; +use QUI\ERP\Coupons\CouponCodeException; +use QUI\ERP\Discount\Handler as DiscountsHandler; /** * Create new CouponCode(s) @@ -27,16 +25,63 @@ function ($attributes) { unset($attributes['amount']); } - $discountIds = []; + // Check required fields + $requiredFields = [ + 'discountAmount' + ]; + + foreach ($requiredFields as $field) { + if (!isset($attributes[$field])) { + throw new CouponCodeException([ + 'quiqqer/coupons', + 'exception.ajax.create.missing_field' + ]); + } + } + + // Create discount + switch ($attributes['discountType']) { + case 'percentage': + $discountType = DiscountsHandler::DISCOUNT_TYPE_PERCENT; + break; - if (!empty($attributes['discountIds'])) { - $discountIds = \explode(',', $attributes['discountIds']); + default: + $discountType = DiscountsHandler::DISCOUNT_TYPE_CURRENCY; } + $Discounts = DiscountsHandler::getInstance(); + $NewDiscount = $Discounts->createChild([ + 'active' => 1, + 'discount' => (float)$attributes['discountAmount'], + 'discount_type' => $discountType, + ]); + + $L = QUI::getLocale(); + for ($i = 0; $i < $amount; $i++) { - $couponCodes[] = Handler::createCouponCode($discountIds, $attributes); + $NewCouponCode = Handler::createCouponCode([$NewDiscount->getId()], $attributes); + + if ($i === 0) { + \QUI\Translator::update( + 'quiqqer/discount', + 'discount.'.$NewDiscount->getId().'.title', + 'quiqqer/discount', + [ + 'de' => $L->getByLang('de', 'quiqqer/coupons', 'Discount.default_title', [ + 'couponCode' => $NewCouponCode->getCode() + ]), + 'en' => $L->getByLang('en', 'quiqqer/coupons', 'Discount.default_title', [ + 'couponCode' => $NewCouponCode->getCode() + ]) + ] + ); + + \QUI\Translator::publish('quiqqer/discount'); + } + + $couponCodes[] = $NewCouponCode; } - } catch (\QUI\ERP\Coupons\CouponCodeException $Exception) { + } catch (CouponCodeException $Exception) { QUI::getMessagesHandler()->addError( QUI::getLocale()->get( 'quiqqer/coupons', diff --git a/bin/backend/controls/Manager.Create.html b/bin/backend/controls/Manager.Create.html index 54ba00f1668c9f533144f81b36e9a4e991ca8df5..c45b83309a63b5156550a43490c79f823cc1cbe2 100644 --- a/bin/backend/controls/Manager.Create.html +++ b/bin/backend/controls/Manager.Create.html @@ -1,43 +1,152 @@ <div class="quiqqer-coupons-manager-create"> <form> - <label> - <span>{{labelDiscount}}</span> - <input type="hidden" name="discountIds" data-qui="package/quiqqer/discount/bin/controls/Select"/> - </label> - <div class="quiqqer-coupons-manager-create-settings-btn"></div> - <div class="quiqqer-coupons-manager-create-settings"> - <label> - <span>{{labelTitle}}</span> - <input type="text" name="title"/> - </label> - <label> - <span>{{labelCode}}</span> - <input type="text" name="code"/> - </label> - <label> - <span>{{labelReusable}}</span> - <select name="maxUsages"> - <option value="oncePerUser" selected>{{labelReusableOncePerUser}}</option> - <option value="once">{{labelReusableOnce}}</option> - <option value="unlimited">{{labelReusableUnlimited}}</option> - </select> - </label> - <label> - <span>{{labelUsers}}</span> - <input type="hidden" name="userIds" data-qui="controls/users/Select"/> - </label> - <label> - <span>{{labelGroups}}</span> - <input type="hidden" name="groupIds" data-qui="controls/groups/Select"/> - </label> - <label> - <span>{{labelDate}}</span> - <input type="date" placeholder="YYYY-MM-DD HH:MM" name="validUntilDate"/> - </label> - <label> - <span>{{labelAmount}}</span> - <input type="number" name="amount" min="1" value="1"/> - </label> + <div class="quiqqer-coupons-manager-create-basic"> + <h2> + {{headerBasics}} + </h2> + <table class="data-table data-table-flexbox"> + <tbody> + <tr> + <td> + <label class="field-container"> + <span class="field-container-item">{{labelTitle}}</span> + <input type="text" class="field-container-field" name="title"/> + </label> + <div class="field-container-item-desc"> + {{{descTitle}}} + </div> + </td> + </tr> + <tr> + <td> + <label class="field-container"> + <span class="field-container-item">{{labelCode}}</span> + <input type="text" class="field-container-field" name="code"/> + </label> + <div class="field-container-item-desc"> + {{{descCode}}} + </div> + </td> + </tr> + <tr> + <td> + <label class="field-container"> + <span class="field-container-item">{{labelAmount}}</span> + <input type="number" name="amount" class="field-container-field" min="1" value="1"/> + </label> + </td> + </tr> + </tbody> + </table> + </div> + <div class="quiqqer-coupons-manager-create-advanced"> + <div class="quiqqer-coupons-manager-create-advanced-discount"> + <h2> + {{headerDiscount}} + </h2> + <table class="data-table data-table-flexbox"> + <tbody> + {{#isCreate}} + <tr> + <td> + <label class="field-container"> + <span class="field-container-item">{{labelDiscountAmount}}</span> + <input type="number" name="discountAmount" class="field-container-field" required/> + </label> + </td> + </tr> + <tr> + <td> + <label class="field-container"> + <span class="field-container-item">{{labelDiscountType}}</span> + <select class="field-container-field" name="discountType"> + <option value="flat" selected>{{labelDiscountTypeFlat}}</option> + <option value="percentage">{{labelDiscountTypePercentage}}</option> + </select> + </label> + </td> + </tr> + {{/isCreate}} + {{^isCreate}} + <tr class="quiqqer-coupons-manager-create-advanced-discount-select"> + <td> + <label class="field-container"> + <span class="field-container-item">{{labelDiscount}}</span> + <input type="hidden" + class="field-container-field" + name="discountIds" + data-qui="package/quiqqer/discount/bin/controls/Select" + data-qui-options-multiple="0" + data-qui-options-max="1" + required + /> + </label> + <div class="field-container-item-desc quiqqer-coupons-manager-create-advanced-discount-buttons"> + + </div> + </td> + </tr> + {{/isCreate}} + </tbody> + </table> + </div> + <div class="quiqqer-coupons-manager-create-advanced-restrictions"> + <h2> + {{headerRestrictions}} + </h2> + <table class="data-table data-table-flexbox"> + <tbody> + <tr> + <td> + <label class="field-container"> + <span class="field-container-item">{{labelReusable}}</span> + <select class="field-container-field" name="maxUsages"> + <option value="oncePerUser" selected>{{labelReusableOncePerUser}}</option> + <option value="once">{{labelReusableOnce}}</option> + <option value="unlimited">{{labelReusableUnlimited}}</option> + </select> + </label> + </td> + </tr> + <tr> + <td> + <label class="field-container"> + <span class="field-container-item">{{labelDate}}</span> + <input type="date" + name="validUntilDate" + placeholder="YYYY-MM-DD HH:MM" + class="field-container-field" + /> + </label> + </td> + </tr> + <tr> + <td> + <label class="field-container"> + <span class="field-container-item">{{labelUsers}}</span> + <input type="hidden" + name="userIds" + class="field-container-field" + data-qui="controls/users/Select" + /> + </label> + </td> + </tr> + <tr> + <td> + <label class="field-container"> + <span class="field-container-item">{{labelGroups}}</span> + <input type="hidden" + name="groupIds" + class="field-container-field" + data-qui="controls/groups/Select" + /> + </label> + </td> + </tr> + </tbody> + </table> + </div> </div> </form> </div> \ No newline at end of file diff --git a/bin/backend/controls/Manager.css b/bin/backend/controls/Manager.css index 7399dd2a1c6f3722f9f391ee269d336bddcf39f6..af2a0412443929e6bc7957a3ce4ec341b23a412f 100644 --- a/bin/backend/controls/Manager.css +++ b/bin/backend/controls/Manager.css @@ -23,11 +23,6 @@ margin-bottom: 15px; } -.quiqqer-coupons-manager-create label > span { - float: left; - margin-bottom: 5px; -} - .quiqqer-coupons-manager-create-sendmail span, .quiqqer-coupons-manager-create-sendmail input { float: right !important; @@ -42,7 +37,7 @@ } .quiqqer-coupons-manager-create-settings { - display: none; + } .quiqqer-coupons-manager-create-settings-btn { @@ -88,4 +83,35 @@ .quiqqer-coupons-manager-usages-tbl-user { width: 200px; +} + +.quiqqer-coupons-manager-create h2 { + font-size: 18px; + margin-bottom: 10px; +} + +.quiqqer-coupons-manager-create-advanced { + align-content: stretch; + display: flex; + flex-direction: row; + float: left; + justify-content: space-between; + width: 100%; +} + +.quiqqer-coupons-manager-create-advanced-discount { + padding-right: 10px; + width: 50%; +} + +.quiqqer-coupons-manager-create-advanced-discount table, +.quiqqer-coupons-manager-create-advanced-discount table tbody, +.quiqqer-coupons-manager-create-advanced-discount table tbody tr, +.quiqqer-coupons-manager-create-advanced-discount table tbody tr td { + display: block; +} + +.quiqqer-coupons-manager-create-advanced-restrictions { + padding-left: 10px; + width: 50%; } \ No newline at end of file diff --git a/bin/backend/controls/Manager.js b/bin/backend/controls/Manager.js index 13d3c8cf3147df3e7c0af2b615c76b37cff1eabb..625738aa5f5e591b248aa0a318e9ddf0ed02b50e 100644 --- a/bin/backend/controls/Manager.js +++ b/bin/backend/controls/Manager.js @@ -331,38 +331,67 @@ define('package/quiqqer/coupons/bin/backend/controls/Manager', [ * @param {Object} [CouponData] - If omitted create new CouponCode */ $showDetails: function (CouponData) { - var self = this; + let Form; + let DiscountSelect; CouponData = CouponData || false; - var FuncSubmit = function () { - var Content = Popup.getContent(); - var Form = Content.getElement('form'); + var FuncSubmit = () => { + if (!Form.reportValidity()) { + return; + } Popup.Loader.show(); if (CouponData) { - CouponCodes.edit(CouponData.id, QUIFormUtils.getFormData(Form)).then(function (couponCodeId) { + CouponCodes.edit(CouponData.id, QUIFormUtils.getFormData(Form)).then((couponCodeId) => { if (!couponCodeId) { Popup.Loader.hide(); return; } - self.refresh(); + this.refresh(); Popup.close(); }); return; } - CouponCodes.create(QUIFormUtils.getFormData(Form)).then(function (couponCodeId) { + CouponCodes.create(QUIFormUtils.getFormData(Form)).then((couponCodeId) => { if (!couponCodeId) { Popup.Loader.hide(); return; } - self.refresh(); - Popup.close(); + require([ + 'package/quiqqer/translator/bin/classes/Translator' + ], (Translator) => { + new Translator().publish(lg).then(() => { + this.refresh(); + Popup.close(); + }).then(() => { + this.refresh(); + Popup.close(); + }); + }); + }).catch(() => { + Popup.Loader.hide(); + }); + }; + + var openDiscount = () => { + Popup.Loader.show(); + + require([ + 'package/quiqqer/discount/bin/controls/Discounts', + 'utils/Panels' + ], function (DiscountsManagerPanel, PanelUtils) { + const Panel = new DiscountsManagerPanel(); + + PanelUtils.openPanelInTasks(Panel).then((PanelOpened) => { + PanelOpened.editChild(parseInt(DiscountSelect.getValue())); + Popup.close(); + }); }); }; @@ -374,22 +403,20 @@ define('package/quiqqer/coupons/bin/backend/controls/Manager', [ title : CouponData ? QUILocale.get(lg, 'controls.manager.details.popup.title_edit', {code: CouponData.code}) : QUILocale.get(lg, 'controls.manager.details.popup.title_new'), - maxHeight : 335, - maxWidth : 450, + maxHeight : 1000, + maxWidth : 1000, events : { - onOpen: function () { - var Content = Popup.getContent(); - var Form = Content.getElement('form'); - var SubmitBtn = Popup.getButton('submit'); + onOpen: (Win) => { + const Content = Popup.getContent(); + Form = Content.getElement('form'); Form.addEvent('submit', function (event) { event.stop(); FuncSubmit(); }); - var Settings = Content.getElement('.quiqqer-coupons-manager-create-settings'); - var Amount = Content.getElement('input[name="amount"]'); - var Code = Content.getElement('input[name="code"]'); + const Amount = Content.getElement('input[name="amount"]'); + const Code = Content.getElement('input[name="code"]'); Code.addEvent('keyup', function () { if (Code.value !== '') { @@ -400,97 +427,93 @@ define('package/quiqqer/coupons/bin/backend/controls/Manager', [ } }); - new QUIButton({ - textimage: 'fa fa-cogs', - text : QUILocale.get(lg, 'controls.Manager.create.settings_btn.show'), - events : { - onClick: function (Btn) { - if (Settings.getStyle('display') === 'none') { - Settings.setStyle('display', 'block'); - - Btn.setAttribute( - 'text', - QUILocale.get(lg, 'controls.Manager.create.settings_btn.hide') - ); - - Content.getElement('input[name="title"]').focus(); - - Popup.setAttribute('maxHeight', 850); - Popup.resize(); - } else { - Settings.setStyle('display', 'none'); - - Btn.setAttribute( - 'text', - QUILocale.get(lg, 'controls.Manager.create.settings_btn.show') - ); - - Popup.setAttribute('maxHeight', 335); - Popup.resize(); - } - } - } - }).inject(Content.getElement( - '.quiqqer-coupons-manager-create-settings-btn' - )); + let EditDiscountBtn = false; if (CouponData) { CouponData.discountIds = CouponData.discountIds.join(','); QUIFormUtils.setDataToForm(CouponData, Form); + + EditDiscountBtn = new QUIButton({ + 'class' : 'optional', + textimage: 'fa fa-percent', + text : QUILocale.get(lg, 'controls.manager.details.popup.btn.open_discount'), + title : QUILocale.get(lg, 'controls.manager.details.popup.btn.open_discount'), + events : { + onClick: openDiscount + }, + styles : { + float: 'right' + } + }).inject( + Content.getElement('.quiqqer-coupons-manager-create-advanced-discount-buttons') + ); } - Popup.Loader.show(); + Win.Loader.show(); + + QUI.parse(Content).then(() => { + Win.Loader.hide(); - QUI.parse(Content).then(function () { - var DiscountSelect = QUI.Controls.getById( + if (!CouponData) { + return; + } + + DiscountSelect = QUI.Controls.getById( Content.getElement('input[name="discountIds"]').get('data-quiid') ); DiscountSelect.addEvents({ - onChange: function () { - if (DiscountSelect.getValue() === '') { - SubmitBtn.disable(); - } else { - SubmitBtn.enable(); - } + onAddItem : () => { + EditDiscountBtn.enable(); + }, + onRemoveItem: () => { + EditDiscountBtn.disable(); } }); - - Popup.Loader.hide(); }); } }, closeButton: true, content : Mustache.render(templateCreate, { - labelTitle : QUILocale.get(lg, lgPrefix + 'labelTitle'), - labelCode : QUILocale.get(lg, lgPrefix + 'labelCode'), - labelUsers : QUILocale.get(lg, lgPrefix + 'labelUsers'), - labelGroups : QUILocale.get(lg, lgPrefix + 'labelGroups'), - labelDate : QUILocale.get(lg, lgPrefix + 'labelDate'), - labelAmount : QUILocale.get(lg, lgPrefix + 'labelAmount'), - labelReusable : QUILocale.get(lg, lgPrefix + 'labelReusable'), - labelReusableOncePerUser: QUILocale.get(lg, lgPrefix + 'labelReusableOncePerUser'), - labelReusableOnce : QUILocale.get(lg, lgPrefix + 'labelReusableOnce'), - labelReusableUnlimited : QUILocale.get(lg, lgPrefix + 'labelReusableUnlimited'), - labelDiscount : QUILocale.get(lg, lgPrefix + 'labelDiscount') + labelTitle : QUILocale.get(lg, lgPrefix + 'labelTitle'), + labelCode : QUILocale.get(lg, lgPrefix + 'labelCode'), + labelUsers : QUILocale.get(lg, lgPrefix + 'labelUsers'), + labelGroups : QUILocale.get(lg, lgPrefix + 'labelGroups'), + labelDate : QUILocale.get(lg, lgPrefix + 'labelDate'), + labelAmount : QUILocale.get(lg, lgPrefix + 'labelAmount'), + labelReusable : QUILocale.get(lg, lgPrefix + 'labelReusable'), + labelReusableOncePerUser : QUILocale.get(lg, lgPrefix + 'labelReusableOncePerUser'), + labelReusableOnce : QUILocale.get(lg, lgPrefix + 'labelReusableOnce'), + labelReusableUnlimited : QUILocale.get(lg, lgPrefix + 'labelReusableUnlimited'), + labelDiscount : QUILocale.get(lg, lgPrefix + 'labelDiscount'), + headerBasics : QUILocale.get(lg, lgPrefix + 'headerBasics'), + headerDiscount : QUILocale.get(lg, lgPrefix + 'headerDiscount'), + headerRestrictions : QUILocale.get(lg, lgPrefix + 'headerRestrictions'), + descCode : QUILocale.get(lg, lgPrefix + 'descCode'), + descTitle : QUILocale.get(lg, lgPrefix + 'descTitle'), + labelDiscountAmount : QUILocale.get(lg, lgPrefix + 'labelDiscountAmount'), + labelDiscountType : QUILocale.get(lg, lgPrefix + 'labelDiscountType'), + labelDiscountTypeFlat : QUILocale.get(lg, lgPrefix + 'labelDiscountTypeFlat'), + labelDiscountTypePercentage: QUILocale.get(lg, lgPrefix + 'labelDiscountTypePercentage'), + + isCreate: !CouponData }) }); Popup.open(); Popup.addButton(new QUIButton({ - name : 'submit', - disabled: true, - text : CouponData ? + name : 'submit', + text : CouponData ? QUILocale.get(lg, 'controls.manager.details.popup.btn.confirm_text_edit') : QUILocale.get(lg, 'controls.manager.details.popup.btn.confirm_text_new'), - alt : CouponData ? + alt : CouponData ? QUILocale.get(lg, 'controls.manager.details.popup.btn.confirm_edit') : QUILocale.get(lg, 'controls.manager.details.popup.btn.confirm_new'), - title : CouponData ? + title : CouponData ? QUILocale.get(lg, 'controls.manager.details.popup.btn.confirm_edit') : QUILocale.get(lg, 'controls.manager.details.popup.btn.confirm_new'), - events : { + events: { onClick: FuncSubmit } })); diff --git a/composer.json b/composer.json index 24a15ddb15b6b382d5176bc085148eadaa0f742c..3b95539fee64ccbca9652f9d926f087c37613496 100644 --- a/composer.json +++ b/composer.json @@ -1,26 +1,27 @@ { - "name": "quiqqer\/coupons", - "type": "quiqqer-module", - "description": "Coupons for QUIQQER", - "license": "GPL-3.0+", - "authors": [ - { - "name": "Patrick M\u00fcller", - "email": "support@pcsg.de", - "homepage": "http:\/\/www.pcsg.de", - "role": "Developer" - } - ], - "support": { - "email": "support@pcsg.de" - }, - "require": { - "quiqqer\/quiqqer": "^1.2|*@dev", - "quiqqer\/discount": "^1|*@dev" - }, - "autoload": { - "psr-4": { - "QUI\\ERP\\Coupons\\": "src\/QUI\/ERP\/Coupons" - } + "name": "quiqqer\/coupons", + "type": "quiqqer-module", + "description": "Coupons for QUIQQER", + "license": "GPL-3.0+", + "authors": [ + { + "name": "Patrick M\u00fcller", + "email": "support@pcsg.de", + "homepage": "https:\/\/www.pcsg.de", + "role": "Developer" } + ], + "support": { + "email": "support@pcsg.de" + }, + "require": { + "quiqqer\/quiqqer": "^1.2|*@dev", + "quiqqer\/discount": "^1.1|*@dev", + "quiqqer/products": "^1.3|*@dev" + }, + "autoload": { + "psr-4": { + "QUI\\ERP\\Coupons\\": "src\/QUI\/ERP\/Coupons" + } + } } diff --git a/events.xml b/events.xml index 4ce5084ffb1bbe7bb7800dfc341626c86a446679..de2021cc66c7ac6038149a377093b99504c6744f 100644 --- a/events.xml +++ b/events.xml @@ -1,5 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <events> + <event on="onPackageSetup-quiqqer/coupons" + fire="\QUI\ERP\Coupons\Events::onPackageSetup" + /> + + <event on="onQuiqqerProductsProductCreate" + fire="\QUI\ERP\Coupons\Events::onQuiqqerProductsProductCreate" + /> + <event on="onQuiqqer::order::orderProcessBasketEnd" fire="\QUI\ERP\Coupons\Events::templateOrderProcessBasketEnd" /> @@ -16,6 +24,10 @@ fire="\QUI\ERP\Coupons\Events::onQuiqqerOrderSuccessful" /> + <event on="onQuiqqerOrderCreated" + fire="\QUI\ERP\Coupons\Events::onQuiqqerOrderCreated" + /> + <event on="onOrderProcess" fire="\QUI\ERP\Coupons\Events::onOrderProcess" /> diff --git a/locale.xml b/locale.xml index bdffa68363f8cf416110b670c44b09fb7f9709e0..1389bafc1f0efde4ba84c47198bfed06ac16a253 100644 --- a/locale.xml +++ b/locale.xml @@ -80,9 +80,103 @@ <de><![CDATA[Gutschein-Code: [code]]]></de> <en><![CDATA[Coupon code: [code]]]></en> </locale> + + <locale name="fieldCategory.quiqqer-coupons-fields"> + <de><![CDATA[Gutschein-Einstellungen]]></de> + <en><![CDATA[Coupn code settings]]></en> + </locale> + </groups> <groups name="quiqqer/coupons" datatype="php"> + + <locale name="ProductCoupon.mail.subject"> + <de><![CDATA[Ihr persönlicher Gutschein-Code]]></de> + <en><![CDATA[Your personal coupon code]]></en> + </locale> + <locale name="ProductCoupon.mail.body"> + <de><![CDATA[ +<p>Hallo [customerName],</p> +<p>vielen Dank für Ihren Gutschein-Einkauf. Hiermit erhalten Sie Ihren exklusiven Gutschein-Code für "[productTitle]".</p> +<p> +<b>Gutschein-Code:</b> [couponCode]<br/> +<b>Wert:</b> [discountAmountFormatted]<br/> +<b>Ausgestellt am:</b> [createDateFormatted]<br/> +<b>Einlösbar bis:</b> [validUntilDateFormatted]<br/> +</p> +<p>[transferableInfo]</p> +<p>[profileDownloadInfo]</p> +<p> +Beste Grüße<br/> +[company] +</p> +]]></de> + <en><![CDATA[ + +]]></en> + </locale> + <locale name="ProductCoupon.mail.transferable_info.is_transferable"> + <de> + <![CDATA[Sie dürfen diesen Gutschein verschenken! Der Gutschein-Code ist nicht personalisiert und kann von jedem in unserem Shop eingelöst werden. Bewahren Sie ihn daher gut auf!]]></de> + <en><![CDATA[You may give this coupn as a gift! The coupon code is not personalized and can be redeemed by anyone in our store. Therefore, keep it safe!]]></en> + </locale> + <locale name="ProductCoupon.mail.transferable_info.is_not_transferable"> + <de> + <![CDATA[Bitte bachten Sie: Dieser Gutschein kann nur von Ihnen persönlich eingesetzt werden, wenn Sie in unserem Shop eingeloggt sind. Dritte können den Gutschein-Code nicht einlösen.]]></de> + <en><![CDATA[Please note: This voucher can only be used by you personally when you are logged in to our store. Third parties can not redeem the voucher code.]]></en> + </locale> + <locale name="ProductCoupon.mail.profile_download_info"> + <de><![CDATA[Sie finden den Gutschein als PDF-Datei im Anhang oder in Ihrem Benutzer-Profil in unserem Shop (bitte vorher einloggen).]]></de> + <en><![CDATA[You can find the coupon as a PDF file attached or in your user profile in our shop (please log in first).]]></en> + </locale> + <locale name="ProductCoupon.coupon_title"> + <de><![CDATA[Produkt #[productId] aus Bestellung [orderId]]]></de> + <en><![CDATA[Product #[productId] from order [orderId]]]></en> + </locale> + + <locale name="DownloadProduct.pdf.filename"> + <de><![CDATA[[productTitle]__[date]]]></de> + <en><![CDATA[[productTitle]__[date]]]></en> + </locale> + <locale name="CouponCode.tpl.header"> + <de><![CDATA[G U T S C H E I N]]></de> + <en><![CDATA[C O U P O N]]></en> + </locale> + <locale name="CouponCode.tpl.labelAmount"> + <de><![CDATA[über]]></de> + <en><![CDATA[for]]></en> + </locale> + <locale name="CouponCode.tpl.labelCreateDate"> + <de><![CDATA[ausgestellt am]]></de> + <en><![CDATA[issued at]]></en> + </locale> + <locale name="CouponCode.tpl.labelValidUntilDate"> + <de><![CDATA[einlösbar bis]]></de> + <en><![CDATA[redeemable until]]></en> + </locale> + <locale name="CouponCode.tpl.labelUser"> + <de><![CDATA[für]]></de> + <en><![CDATA[for]]></en> + </locale> + + <locale name="Discount.default_title"> + <de><![CDATA[Coupon-Code [couponCode]]]></de> + <en><![CDATA[Coupon code [couponCode]]]></en> + </locale> + <locale name="Discount.default_title.product"> + <de><![CDATA["[productTitle]"]]></de> + <en><![CDATA["[productTitle]"]]></en> + </locale> + + <locale name="exception.ajax.create.missing_field"> + <de><![CDATA[Bitte fülle alle Pflichtfelder aus.]]></de> + <en><![CDATA[Please fill out all required fields.]]></en> + </locale> + <locale name="product_type.CouponProductType.title"> + <de><![CDATA[Gutschein]]></de> + <en><![CDATA[Coupon]]></en> + </locale> + <!-- Ajax --> <locale name="message.ajax.general_error"> <de> @@ -181,6 +275,10 @@ <de><![CDATA[Verwendungszweck (optional)]]></de> <en><![CDATA[Usage (optional)]]></en> </locale> + <locale name="controls.manager.create.template.descTitle"> + <de><![CDATA[Dient der <b>internen</b> Beschreibung des Verwendungszwecks für diesen Gutschein-Code. Kunden sehen diese Beschreibung <b>nicht</b>.]]></de> + <en><![CDATA[Used for <b>internal</b> description of the purpose for this coupon code. Customers will <b>not</b> see this description.]]></en> + </locale> <locale name="controls.manager.create.template.labelAmount"> <de><![CDATA[Anzahl generierte Gutschein-Codes]]></de> <en><![CDATA[Amount of generated coupon codes]]></en> @@ -194,8 +292,12 @@ <en><![CDATA[Restrict to the following groups (optional)]]></en> </locale> <locale name="controls.manager.create.template.labelCode"> - <de><![CDATA[Gutschein-Code (optional - wird automatisch generiert, wenn Feld leergelassen wird)]]></de> - <en><![CDATA[Coupon code (optional - is generated automatically if left empty)]]></en> + <de><![CDATA[Gutschein-Code (optional)]]></de> + <en><![CDATA[Coupon code (optional)]]></en> + </locale> + <locale name="controls.manager.create.template.descCode"> + <de><![CDATA[Wird dieses Feld leergelassen, wird automatisch ein zufälliger neuer Gutschein-Code generiert.]]></de> + <en><![CDATA[If this field is left blank, a random new coupon code will be generated automatically.]]></en> </locale> <locale name="controls.manager.create.template.labelReusable"> <de><![CDATA[Erlaubte Verwendung]]></de> @@ -209,6 +311,22 @@ <de><![CDATA[Insgesamt nur einmal einlösbar]]></de> <en><![CDATA[Only redeemable once in total]]></en> </locale> + <locale name="controls.manager.create.template.labelDiscountAmount"> + <de><![CDATA[Rabatt-Betrag]]></de> + <en><![CDATA[Discount amount]]></en> + </locale> + <locale name="controls.manager.create.template.labelDiscountType"> + <de><![CDATA[Rabatt-Typ]]></de> + <en><![CDATA[Discount type]]></en> + </locale> + <locale name="controls.manager.create.template.labelDiscountTypeFlat"> + <de><![CDATA[Festbetrag]]></de> + <en><![CDATA[Fixed amount]]></en> + </locale> + <locale name="controls.manager.create.template.labelDiscountTypePercentage"> + <de><![CDATA[Prozentual]]></de> + <en><![CDATA[Percentage]]></en> + </locale> <locale name="controls.manager.create.template.labelReusableUnlimited"> <de><![CDATA[Unbegrenzt einlösbar]]></de> <en><![CDATA[Unlimited redeemable]]></en> @@ -217,6 +335,18 @@ <de><![CDATA[Verknüpfter Rabatt]]></de> <en><![CDATA[Linked discount]]></en> </locale> + <locale name="controls.manager.create.template.headerBasics"> + <de><![CDATA[Grundeinstellungen]]></de> + <en><![CDATA[Basic settings]]></en> + </locale> + <locale name="controls.manager.create.template.headerDiscount"> + <de><![CDATA[Ermäßigung]]></de> + <en><![CDATA[Discount]]></en> + </locale> + <locale name="controls.manager.create.template.headerRestrictions"> + <de><![CDATA[Nutzungseinschränkungen]]></de> + <en><![CDATA[Usage restrictions]]></en> + </locale> <locale name="controls.manager.details.popup.title_new"> <de><![CDATA[Neuen Gutschein-Code erstellen]]></de> <en><![CDATA[Create new Coupon code]]></en> @@ -241,6 +371,10 @@ <de><![CDATA[Hier klicken, um die Änderungen am Gutschein-Code zu speichern]]></de> <en><![CDATA[Click here to save the changes made to the Coupon code]]></en> </locale> + <locale name="controls.manager.details.popup.btn.open_discount"> + <de><![CDATA[Rabatt konfigurieren]]></de> + <en><![CDATA[Configure discount]]></en> + </locale> <locale name="controls.manager.tbl.btn.create"> <de><![CDATA[Erstellen]]></de> <en><![CDATA[Create]]></en> diff --git a/package.xml b/package.xml index fd311af7c241411976cd879537bc25c182fb19d2..95e98acc1ac9f93c5eb0464b377a929abf9c3c64 100644 --- a/package.xml +++ b/package.xml @@ -23,5 +23,9 @@ <license><![CDATA[GPL-3.0+]]></license> </copyright> + <provider> + <productType src="\QUI\ERP\Coupons\Products\CouponProductType"/> + </provider> + </package> </quiqqer> \ No newline at end of file diff --git a/products.xml b/products.xml new file mode 100644 index 0000000000000000000000000000000000000000..cb83a200233686b2968e608445da0e8497511830 --- /dev/null +++ b/products.xml @@ -0,0 +1,20 @@ +<quiqqer> + <products> + <fieldCategories> + <fieldCategory name="quiqqer-coupons-fields"> + <title> + <locale group="quiqqer/coupons" var="fieldCategory.quiqqer-coupons-fields"/> + </title> + <icon>fa fa-credit-card</icon> + <fields> + <field>670</field> + <field>671</field> + <field>672</field> + <field>673</field> + <field>674</field> + <field>675</field> + </fields> + </fieldCategory> + </fieldCategories> + </products> +</quiqqer> \ No newline at end of file diff --git a/src/QUI/ERP/Coupons/CouponCode.php b/src/QUI/ERP/Coupons/CouponCode.php index 1ccd815a4c261fce57f36e5b963ef87852d59e62..87e2f9a83ac2deabd103c2df1a283f8e60e7cc67 100644 --- a/src/QUI/ERP/Coupons/CouponCode.php +++ b/src/QUI/ERP/Coupons/CouponCode.php @@ -240,6 +240,22 @@ public function getDiscounts(): array return $discounts; } + /** + * @return int[] + */ + public function getUserIds(): array + { + return $this->userIds; + } + + /** + * @return int[] + */ + public function getGroupIds(): array + { + return $this->groupIds; + } + /** * Redeems this CouponCode * diff --git a/src/QUI/ERP/Coupons/Events.php b/src/QUI/ERP/Coupons/Events.php index 102ae37a217741c3340fae85fc58583d5934d6bd..676d3fda336e8a49ea4538ce7a7028a0eda7f079 100644 --- a/src/QUI/ERP/Coupons/Events.php +++ b/src/QUI/ERP/Coupons/Events.php @@ -3,11 +3,15 @@ namespace QUI\ERP\Coupons; use QUI; +use QUI\ERP\Order\AbstractOrder; +use QUI\ERP\Products\Handler\Fields; +use QUI\ERP\Products\Interfaces\ProductInterface; use Quiqqer\Engine\Collector; use QUI\ERP\Order\Basket\Basket; use QUI\ERP\Order\Basket\BasketGuest; use QUI\ERP\Coupons\Handler as CouponsHandler; use QUI\ERP\Discount\EventHandling as DiscountEvents; +use QUI\ERP\Coupons\Products\CouponProductType; /** * Class Events @@ -16,6 +20,21 @@ */ class Events { + /** + * quiqqer/quiqqer: onPackageSetup + * + * @param QUI\Package\Package $Package + * @return void + */ + public static function onPackageSetup(QUI\Package\Package $Package) + { + try { + self::createProductFields(); + } catch (\Exception $Exception) { + QUI\System\Log::writeException($Exception); + } + } + /** * Template event quiqqer/order: onQuiqqer::order::orderProcessBasketEnd * @@ -395,10 +414,173 @@ protected static function addCouponToOrder($Order, $coupon) } /** + * Removes all coupons from the current session. * + * @return void */ public static function removeCouponsFromSession() { QUI::getSession()->remove('quiqqer-coupons'); } + + /** + * Create all fixed product fields that quiqqer/stock-management provides + * + * @return void + * @throws QUI\Exception + */ + protected static function createProductFields() + { + $fields = [ + CouponProductType::PRODUCT_FIELD_ID_TRANSFERABLE => [ + 'title' => [ + 'de' => 'Gutschein-Code ist übertragbar', + 'en' => 'Coupon code is transferable' + ], + 'type' => Fields::TYPE_BOOL, + 'public' => false, + 'standard' => false, + 'requiredField' => false + ], + CouponProductType::PRODUCT_FIELD_ID_GENERATE_PDF => [ + 'title' => [ + 'de' => 'Gutschein-Code als PDF bereitstellen', + 'en' => 'Provide coupon code as PDF' + ], + 'type' => Fields::TYPE_BOOL, + 'public' => false, + 'standard' => false, + 'requiredField' => false + ], + CouponProductType::PRODUCT_FIELD_ID_COUPON_AMOUNT => [ + 'title' => [ + 'de' => 'Gutschein Wert', + 'en' => 'Coupon amount' + ], + 'type' => Fields::TYPE_FLOAT, + 'public' => false, + 'standard' => false, + 'requiredField' => true + ], + CouponProductType::PRODUCT_FIELD_ID_DAYS_VALID => [ + 'title' => [ + 'de' => 'Gutschein-Code Gültigkeit (Tage)', + 'en' => 'Coupon code validity (days)' + ], + 'type' => Fields::TYPE_INT, + 'public' => false, + 'standard' => false, + 'requiredField' => true + ], + CouponProductType::PRODUCT_FIELD_ID_COUPON_DESCRIPTION => [ + 'title' => [ + 'de' => 'Gutschein-Beschreibung', + 'en' => 'Coupon description' + ], + 'type' => Fields::TYPE_INPUT_MULTI_LANG, + 'public' => false, + 'standard' => false, + 'requiredField' => true + ], + CouponProductType::PRODUCT_FIELD_ID_IS_PRODUCT_SPECIFIC_COUPON => [ + 'title' => [ + 'de' => 'Ist Gutschein für spezifische Produkte ("Einzweck-Gutschein")', + 'en' => 'Is coupon for specific products ("single purpose coupon")' + ], + 'type' => Fields::TYPE_BOOL, + 'public' => false, + 'standard' => false, + 'requiredField' => false + ] + ]; + + $fieldsCreated = false; + + foreach ($fields as $fieldId => $field) { + try { + Fields::getField($fieldId); + continue; + } catch (\Exception $Exception) { + // Field does not exist -> create it + } + + try { + Fields::createField([ + 'id' => $fieldId, + 'type' => $field['type'], + 'titles' => $field['title'], + 'workingtitles' => $field['title'], + 'systemField' => 0, + 'standardField' => !empty($field['standard']) ? 1 : 0, + 'publicField' => !empty($field['public']) ? 1 : 0, + 'options' => !empty($field['options']) ? $field['options'] : null, + 'requiredField' => !empty($field['requiredField']) ? 1 : 0 + ]); + } catch (\Exception $Exception) { + QUI\System\Log::writeException($Exception); + continue; + } + + $fieldsCreated = true; + } + + if ($fieldsCreated) { + QUI\Translator::publish('quiqqer/products'); + } + } + + /** + * Assign plan product fields to a product + * + * @param ProductInterface $Product + * @return void + */ + public static function onQuiqqerProductsProductCreate(ProductInterface $Product) + { + if (!($Product instanceof CouponProductType)) { + return; + } + + $UniqueProduct = $Product->createUniqueProduct(); + $UniqueProduct->calc(); + + $fields = [ + CouponProductType::PRODUCT_FIELD_ID_TRANSFERABLE => true, + CouponProductType::PRODUCT_FIELD_ID_GENERATE_PDF => true, + CouponProductType::PRODUCT_FIELD_ID_COUPON_AMOUNT => $UniqueProduct->getPrice()->getValue(), + CouponProductType::PRODUCT_FIELD_ID_DAYS_VALID => 1095, // 3 years + CouponProductType::PRODUCT_FIELD_ID_COUPON_DESCRIPTION => null, +// CouponProductType::PRODUCT_FIELD_ID_IS_PRODUCT_SPECIFIC_COUPON => false // @todo later + ]; + + foreach ($fields as $fieldId => $value) { + try { + $Field = Fields::getField($fieldId); + $Field->setValue($value); + + $Product->addOwnField($Field); + } catch (\Exception $Exception) { + QUI\System\Log::writeException($Exception); + } + } + + try { + $Product->update(QUI::getUsers()->getSystemUser()); + } catch (\Exception $Exception) { + QUI\System\Log::writeException($Exception); + } + } + + /** + * 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) + { + QUI\ERP\Coupons\Products\Handler::createCouponCodesFromOrder($Order); + } } diff --git a/src/QUI/ERP/Coupons/Products/CouponProductType.php b/src/QUI/ERP/Coupons/Products/CouponProductType.php new file mode 100644 index 0000000000000000000000000000000000000000..3d5ea3b97cf0b9d11808dcf17b16935c29e32a14 --- /dev/null +++ b/src/QUI/ERP/Coupons/Products/CouponProductType.php @@ -0,0 +1,50 @@ +<?php + +namespace QUI\ERP\Coupons\Products; + +use QUI; +use QUI\ERP\Products\Product\Types\DigitalProduct; + +/** + * Class DigitalProduct + * + * Represents a non-physical product that does not require shipping. + */ +class CouponProductType extends DigitalProduct +{ + /** + * Special fields for coupon products + */ + const PRODUCT_FIELD_ID_TRANSFERABLE = 670; + const PRODUCT_FIELD_ID_GENERATE_PDF = 671; + const PRODUCT_FIELD_ID_COUPON_AMOUNT = 672; + const PRODUCT_FIELD_ID_DAYS_VALID = 673; + const PRODUCT_FIELD_ID_COUPON_DESCRIPTION = 674; + const PRODUCT_FIELD_ID_IS_PRODUCT_SPECIFIC_COUPON = 675; + + /** + * @param QUI\Locale $Locale + * @return string + */ + public static function getTypeTitle($Locale = null) + { + if ($Locale === null) { + $Locale = QUI::getLocale(); + } + + return $Locale->get('quiqqer/coupons', 'product_type.CouponProductType.title'); + } + + /** + * @param QUI\Locale $Locale + * @return string + */ + public static function getTypeDescription($Locale = null) + { + if ($Locale === null) { + $Locale = QUI::getLocale(); + } + + return $Locale->get('quiqqer/coupons', 'product_type.CouponProductType.description'); + } +} diff --git a/src/QUI/ERP/Coupons/Products/Handler.php b/src/QUI/ERP/Coupons/Products/Handler.php new file mode 100644 index 0000000000000000000000000000000000000000..ab2c75c8df7a15e7111fe7617aec9e7229c0249c --- /dev/null +++ b/src/QUI/ERP/Coupons/Products/Handler.php @@ -0,0 +1,372 @@ +<?php + +namespace QUI\ERP\Coupons\Products; + +use QUI; +use QUI\ERP\Products\Product\Product; +use QUI\ERP\Discount\Discount; +use QUI\ERP\Discount\Handler as DiscountHandler; +use QUI\ERP\Coupons\Handler as CouponsHandler; +use QUI\ERP\Coupons\CouponCode; +use QUI\ERP\Products\Handler\Products as ProductsHandler; +use QUI\HtmlToPdf\Document; +use QUI\ERP\Customer\CustomerFiles; + +/** + * Class Handler + * + * Handles coupon code generation from orders / products. + */ +class Handler +{ + /** + * Create coupons from all coupon products of an order. + * + * Each coupon created is sent via e-mail (either as PDF or text). + * + * @param QUI\ERP\Order\AbstractOrder $Order + * @return void + */ + public static function createCouponCodesFromOrder(QUI\ERP\Order\AbstractOrder $Order): void + { + $Customer = $Order->getCustomer(); + + /** @var QUI\ERP\Accounting\Article $Article */ + foreach ($Order->getArticles() as $Article) { + try { + // Do not parse coupon codes / discounts + if (empty($Article->getId()) || !\is_numeric($Article->getId())) { + continue; + } + + $Product = ProductsHandler::getProduct($Article->getId()); + + // Only parse coupon products + if (!($Product instanceof CouponProductType)) { + continue; + } + + $CouponCode = self::createCouponCodeFromProduct($Product, $Order, $Customer); + $couponFilePathCustomerDir = null; + + // Generate coupon PDF + if ($Product->getFieldValue(CouponProductType::PRODUCT_FIELD_ID_GENERATE_PDF)) { + $couponPdfFile = self::createCouponCodePdf($CouponCode, $Product); + + // Rename file + $productTitelSanitized = preg_replace("/[^\p{L}\p{N} ]/ui", '', $Product->getTitle()); + + $fileName = QUI::getLocale()->get( + 'quiqqer/coupons', + 'DownloadProduct.pdf.filename', + [ + 'productTitle' => \str_replace(' ', '_', $productTitelSanitized), + 'date' => \date('Y_m_d') + ] + ); + + $fileName .= '__'.\mb_substr($Order->getHash(), 0, 6); + + $newCouponPdfFile = \str_replace(\basename($couponPdfFile, '.pdf'), $fileName, $couponPdfFile); + + \rename($couponPdfFile, $newCouponPdfFile); + + // Add PDF file to customer files + CustomerFiles::addFileToCustomer($Customer->getId(), $newCouponPdfFile); + CustomerFiles::addFileToDownloadEntry($Customer->getId(), \basename($newCouponPdfFile)); + + $customerDir = CustomerFiles::getFolderPath($Customer); + $couponFilePathCustomerDir = $customerDir.DIRECTORY_SEPARATOR.\basename($newCouponPdfFile); + } + + // Send coupon via email + self::sendCouponMail($CouponCode, $Product, $Customer, $couponFilePathCustomerDir); + } catch (\Exception $Exception) { + if ($Exception->getCode() === 404) { + QUI\System\Log::writeDebugException($Exception); + } else { + QUI\System\Log::writeException($Exception); + } + } + } + } + + /** + * Create coupon codes from an order + * + * @param Product $Product + * @param QUI\ERP\Order\AbstractOrder $Order + * @param QUI\Interfaces\Users\User $User - The user that bought the product + * @return CouponCode + * + * @throws QUI\Exception + * @throws \Exception + */ + protected static function createCouponCodeFromProduct( + Product $Product, + QUI\ERP\Order\AbstractOrder $Order, + QUI\Interfaces\Users\User $User + ): CouponCode { + $Discount = self::createDiscountFromProduct($Product); + $couponAttributes = [ + 'title' => QUI::getSystemLocale()->get( + 'quiqqer/coupons', + 'ProductCoupon.coupon_title', + [ + 'orderId' => $Order->getPrefixedId(), + 'productId' => $Product->getId() + ] + ), + 'maxUsages' => CouponsHandler::MAX_USAGE_ONCE + ]; + + // Valid until + $daysValid = $Product->getFieldValue(CouponProductType::PRODUCT_FIELD_ID_DAYS_VALID); + + if (empty($daysValid)) { + $daysValid = 1095; // 3 years; + } + + $ValidUntil = \date_create('+ '.$daysValid.' days'); + $couponAttributes['validUntilDate'] = $ValidUntil->format('Y-m-d'); + + // Transferable (=usable by other users or guests) + $isTransferable = $Product->getFieldValue(CouponProductType::PRODUCT_FIELD_ID_TRANSFERABLE); + + if (empty($isTransferable)) { + $couponAttributes['userIds'] = [$User->getId()]; + } + + return CouponsHandler::createCouponCode([$Discount->getId()], $couponAttributes); + } + + /** + * Creates a new discount for all coupon codes generated by the product. + * + * @param Product $Product + * @return Discount + * + * @throws QUI\Exception + */ + protected static function createDiscountFromProduct(Product $Product): Discount + { + $Handler = DiscountHandler::getInstance(); + + $discountAmount = $Product->getFieldValue(CouponProductType::PRODUCT_FIELD_ID_COUPON_AMOUNT); + + if (empty($discountAmount)) { + $discountAmount = $Product->getPrice()->getValue(); + } + + // Determine discount calculation basis based on coupon type + $isProductSpecificCoupon = $Product->getFieldValue( + CouponProductType::PRODUCT_FIELD_ID_IS_PRODUCT_SPECIFIC_COUPON + ); + + // @todo für Einzweck-Gutscheine muss ein spezieller Rabatt-Typ verwendet werden + + /** @var Discount $NewDiscount */ + $NewDiscount = $Handler->createChild([ + 'active' => 1, + 'discount' => $discountAmount, + 'discount_type' => DiscountHandler::DISCOUNT_TYPE_CURRENCY, + 'hidden' => 1, + 'scope' => DiscountHandler::DISCOUNT_SCOPE_GRAND_TOTAL + ]); + + $L = new QUI\Locale(); + $discountTitle = []; + + foreach (QUI::availableLanguages() as $lang) { + $L->setCurrent($lang); + + $discountTitle[$lang] = $L->get('quiqqer/coupons', 'Discount.default_title.product', [ + 'productId' => $Product->getId(), + 'productTitle' => $Product->getTitle($L) + ]); + } + + \QUI\Translator::update( + 'quiqqer/discount', + 'discount.'.$NewDiscount->getId().'.title', + 'quiqqer/discount', + $discountTitle + ); + + \QUI\Translator::publish('quiqqer/discount'); + + return $NewDiscount; + } + + /** + * @param CouponCode $CouponCode - The coupon code + * @param Product $Product - The product the coupon code was created from + * @return string - PDF file path + */ + public static function createCouponCodePdf(CouponCode $CouponCode, Product $Product): string + { + $Document = new Document(); + +// $Document->setAttribute('foldingMarks', true); + $Document->setAttribute('disableSmartShrinking', true); + $Document->setAttribute('headerSpacing', 0); + $Document->setAttribute('marginTop', 85); + $Document->setAttribute('marginBottom', 40); + $Document->setAttribute('marginLeft', 0); + $Document->setAttribute('marginRight', 0); + $Document->setAttribute('showPageNumbers', false); + + $Engine = QUI::getTemplateManager()->getEngine(); + + $Engine->assign(\array_merge( + [ + 'CouponCode' => $CouponCode, + 'Product' => $Product->getViewFrontend(), + ], + self::getCouponViewData($CouponCode, $Product) + )); + + $tplDir = QUI::getPackage('quiqqer/coupons')->getDir().'templates/'; + + try { + $Document->setHeaderHTML($Engine->fetch($tplDir.'CouponCode.header.html')); + $Document->setContentHTML($Engine->fetch($tplDir.'CouponCode.body.html')); + $Document->setFooterHTML($Engine->fetch($tplDir.'CouponCode.footer.html')); + } catch (\Exception $Exception) { + QUI\System\Log::writeException($Exception); + } + + return $Document->createPDF(); + } + + /** + * Send coupon code via e-mail to a customer. + * + * @param CouponCode $CouponCode - The coupon code + * @param Product $Product - The product the coupon code was created from + * @param QUI\Interfaces\Users\User $Customer + * @param string|null $couponPdfFile (optional) - Coupon PDF that is attached to the email + * + * @throws \Exception + */ + public static function sendCouponMail( + CouponCode $CouponCode, + Product $Product, + QUI\Interfaces\Users\User $Customer, + ?string $couponPdfFile + ) { + $recipient = QUI\ERP\Customer\Utils::getInstance()->getEmailByCustomer($Customer); + + if (empty($recipient)) { + QUI\System\Log::addWarning( + 'Cannot send coupon code e-mail to customer #'.$Customer->getUniqueId().' because user has no' + .' email address!' + ); + + return; + } + + $Mailer = QUI::getMailManager()->getMailer(); + $Mailer->addRecipient($recipient); + $Mailer->setSubject(QUI::getLocale()->get('quiqqer/coupons', 'ProductCoupon.mail.subject')); + + $couponViewData = self::getCouponViewData($CouponCode, $Product); + + if ($couponViewData['isTransferable']) { + $transferableInfo = QUI::getLocale()->get( + 'quiqqer/coupons', + 'ProductCoupon.mail.transferable_info.is_transferable' + ); + } else { + $transferableInfo = QUI::getLocale()->get( + 'quiqqer/coupons', + 'ProductCoupon.mail.transferable_info.is_not_transferable' + ); + } + + $profileDownloadInfo = ''; + + if ($couponPdfFile) { + $Mailer->addAttachment($couponPdfFile); + + $profileDownloadInfo = QUI::getLocale()->get( + 'quiqqer/coupons', + 'ProductCoupon.mail.profile_download_info' + ); + } + + $Mailer->setBody( + QUI::getLocale()->get( + 'quiqqer/coupons', + 'ProductCoupon.mail.body', + \array_merge( + $couponViewData, + [ + 'customerName' => $Customer->getName(), + 'company' => QUI\ERP\Defaults::conf('company', 'name'), + 'transferableInfo' => $transferableInfo, + 'profileDownloadInfo' => $profileDownloadInfo + ] + ) + ) + ); + + $Mailer->send(); + } + + /** + * Get view data of a coupon. + * + * @param CouponCode $CouponCode + * @param Product $Product + * @return array + * + * @throws QUI\ERP\Products\Product\Exception + * @throws QUI\Users\Exception + */ + protected static function getCouponViewData(CouponCode $CouponCode, Product $Product): array + { + $discounts = $CouponCode->getDiscounts(); + $Discount = $discounts[0]; + $Currency = QUI\ERP\Defaults::getCurrency(); + $DiscountAmount = new QUI\ERP\Money\Price($Discount->getAttribute('discount'), $Currency); + + $Locale = QUI::getLocale(); + + // Check if coupon is restricted to specific user + $User = false; + $restrictedToUserIds = $CouponCode->getUserIds(); + + if (!empty($restrictedToUserIds)) { + $User = QUI::getUsers()->get($restrictedToUserIds[0]); + } + + // Parse valid until date + $dateFormat = QUI\ERP\Defaults::getDateFormat(); + $ValidUntilDate = $CouponCode->getValidUntilDate(); + $validUntilDateFormatted = '-'; + + if ($ValidUntilDate) { + $validUntilDateFormatted = $Locale->formatDate( + $ValidUntilDate->getTimestamp(), + $dateFormat + ); + } + + return [ + 'isTransferable' => empty($User), + 'couponCode' => $CouponCode->getCode(), + 'productTitle' => $Product->getTitle(), + 'userName' => $User ? $User->getName() : false, + 'couponDescription' => $Product->getFieldValueByLocale( + CouponProductType::PRODUCT_FIELD_ID_COUPON_DESCRIPTION + ), + 'discountAmountFormatted' => $DiscountAmount->getDisplayPrice(), + 'createDateFormatted' => $Locale->formatDate( + $CouponCode->getCreateDate()->getTimestamp(), + $dateFormat + ), + 'validUntilDateFormatted' => $validUntilDateFormatted, + ]; + } +} diff --git a/templates/CouponCode.body.html b/templates/CouponCode.body.html new file mode 100644 index 0000000000000000000000000000000000000000..656ab791bea8aa7cb66fa66dfa6e1c471bd53244 --- /dev/null +++ b/templates/CouponCode.body.html @@ -0,0 +1,110 @@ +<style> + *, *:before, *:after { + box-sizing: border-box; + } + + .couponcode-body { + padding: 5mm 15mm 0 20mm; + text-align: center; + width: 210mm; + } + + .couponcode-body-extra p { + line-height: 20px; + margin: 0; + padding: 0; + } + + .couponcode-body-code { + background-color: #dedede; + border-radius: 5px; + font-family: monospace; + font-size: 2em; + margin: 20px auto; + padding: 20px; + width: 225px; + } + + .couponcode-body-data { + margin-top: 80px; + } + + .couponcode-body-data table { + margin: auto; + text-align: left; + width: 50%; + } + + .couponcode-body-data-label { + font-size: 12px; + } + + .couponcode-body-data-value { + background-color: #f5f5f5; + display: block; + padding: 10px; + } +</style> + +<div class="couponcode-body"> + <h1> + {locale group="quiqqer/coupons" var="CouponCode.tpl.header"} + </h1> + + {if !empty($couponDescription)} + <div class="couponcode-body-description"> + {$couponDescription} + </div> + {/if} + + <div class="couponcode-body-code"> + {$CouponCode->getCode()} + </div> + + <div class="couponcode-body-data"> + <table> + <tbody> + <tr> + <td colspan="2"> + <span class="couponcode-body-data-label"> + {locale group="quiqqer/coupons" var="CouponCode.tpl.labelAmount"} + </span> + <span class="couponcode-body-data-value"> + {$discountAmountFormatted} + </span> + </td> + </tr> + {if $userName} + <tr> + <td colspan="2"> + <span class="couponcode-body-data-label"> + {locale group="quiqqer/coupons" var="CouponCode.tpl.labelUser"} + </span> + <span class="couponcode-body-data-value"> + {$userName} + </span> + </td> + </tr> + {/if} + <tr> + <td> + <span class="couponcode-body-data-label"> + {locale group="quiqqer/coupons" var="CouponCode.tpl.labelCreateDate"} + </span> + <span class="couponcode-body-data-value"> + {$createDateFormatted} + </span> + </td> + <td> + <span class="couponcode-body-data-label"> + {locale group="quiqqer/coupons" var="CouponCode.tpl.labelValidUntilDate"} + </span> + <span class="couponcode-body-data-value"> + {$validUntilDateFormatted} + </span> + </td> + </tr> + </tbody> + </table> + </div> +</div> diff --git a/templates/CouponCode.footer.html b/templates/CouponCode.footer.html new file mode 100644 index 0000000000000000000000000000000000000000..00e65014551af04ae661c089d12f5df45ebcc52b --- /dev/null +++ b/templates/CouponCode.footer.html @@ -0,0 +1,41 @@ +<style> + *, *:before, *:after { + box-sizing: border-box; + } + + .couponcode-footer { + height: 40mm; + clear: both; + display: inline-block; + margin-top: 20px; + margin-left: 0; + padding: 0 15mm 0 20mm; + position: relative; + top: 0; + width: 210mm; + } + + #pages { + font-size: 0.75rem; + position: absolute; + right: 13mm; + top: 30mm; + white-space: nowrap; + } + + .couponcode-footer-text { + font-size: 0.7em; + position: relative; + text-align: center; + top: 15mm; + width: 100%; + } + +</style> + +<footer class="couponcode-footer"> + <div class="couponcode-footer-text"> + {locale group="quiqqer/erp-accounting-templates" var="header" + shopownerAdressShort=QUI\ERP\Defaults::getShortAddress()} + </div> +</footer> \ No newline at end of file diff --git a/templates/CouponCode.header.html b/templates/CouponCode.header.html new file mode 100644 index 0000000000000000000000000000000000000000..7443c6d17cedce9da9c89b5f36f953855d75d882 --- /dev/null +++ b/templates/CouponCode.header.html @@ -0,0 +1,144 @@ +<style> + *, *:before, *:after { + box-sizing: border-box; + } + + .couponcode-header { + display: inline-block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + height: 85mm; + margin-left: 0; + padding: 5mm 15mm 5mm 20mm; + position: relative; + text-align: center; + width: 210mm; + } + + .couponcode-header-image { + height: 40mm; + margin-top: 5mm; + margin-bottom: 0; + position: relative; + } + + .couponcode-header-image img { + height: auto; + margin: 5mm 0 0 0; + max-height: 20mm; + max-width: 350px; + } + + .couponcode-body-header-text { + border-bottom: 1px solid #999; + clear: both; + display: inline-block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 10px; + line-height: 2rem; + margin-bottom: 1rem; + width: 100%; + } + + .couponcode-body-header-text p { + margin: 0; + } + + .couponcode-customer, + .couponcode-customer address { + display: block; + float: left; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + width: 55%; + font-style: normal !important; + font-size: 14px !important; + } + + .couponcode-data { + float: right; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + width: 45%; + } + + .value-id, + .value-date, + .value-customer { + font-weight: bold; + } + + .couponcode-data-highlight { + background: #e5e5e5; + border-radius: 3px; + margin-left: auto; + padding: 10px; + width: 100%; + } + + .delivery-address { + font-size: 12px; + margin-top: 0; + margin-left: auto; + padding: 10px; + width: 100%; + } + + .delivery-address-label { + font-weight: bold; + } + + .couponcode-data table { + border-spacing: 0; + border-collapse: collapse; + } + + .couponcode-data table td + td { + line-height: 20px; + padding: 0 0 0 10px; + } + + .couponcode-data-highlight h2 { + font-size: 1.2rem; + margin: 0 0 10px 0; + padding: 0; + text-align: left; + text-transform: uppercase; + } + + .couponcode-data-highlight table { + border-collapse: separate; + width: 100%; + } + + .couponcode-data-highlight td { + font-size: 14px; + } + + .couponcode-data-highlight td:first-child { + width: 35%; + } + + .couponcode-data-highlight td span { + background: #fff; + border-radius: 3px; + display: inline-block; + margin: 0 0 2px; + padding: 0 10px; + width: 160px; + } + + .couponcode-customer h3 { + margin: 0 0 10px 0; + } +</style> + +<!-- Workaround to achieve full a4 format as PDF file --> +<div style="position: absolute; left: 0; top: 0; width: 210mm; background: #fff; height: 5mm; z-index: -1;"></div> +<div style="position: absolute; left: 0; top: 0; width: 5mm; background: #fff; height: 297mm; z-index: -1;"></div> + +<div class="couponcode-header couponcode-header-top"> + <div class="couponcode-header-image"> + {assign var=Logo value=\QUI\ERP\Defaults::getLogo()} + {image image=$Logo height="120" svgtopng=1 host=1} + </div> + + <div class="couponcode-header-line"></div> +</div>