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>