diff --git a/ajax/create.php b/ajax/create.php
new file mode 100644
index 0000000000000000000000000000000000000000..9f9d08e2ed92ba751a157de16b175df25b6b184c
--- /dev/null
+++ b/ajax/create.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * This file contains package_quiqqer_coupons_ajax_create
+ */
+
+use QUI\ERP\Coupons\Handler;
+use QUI\Utils\Security\Orthos;
+
+/**
+ * Create new InviteCode(s)
+ *
+ * @param array $attributes
+ * @return bool - success
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_coupons_ajax_create',
+    function ($attributes) {
+        $attributes = Orthos::clearArray(json_decode($attributes, true));
+
+        try {
+            $amount      = 1;
+            $inviteCodes = array();
+
+            if (!empty($attributes['amount'])) {
+                $amount = (int)$attributes['amount'];
+                unset($attributes['amount']);
+            }
+
+            for ($i = 0; $i < $amount; $i++) {
+                $inviteCodes[] = Handler::createCouponCode($attributes);
+            }
+        } catch (\QUI\ERP\Coupons\CouponCodeException $Exception) {
+            QUI::getMessagesHandler()->addError(
+                QUI::getLocale()->get(
+                    'quiqqer/invitecode',
+                    'message.ajax.create.error',
+                    array(
+                        'error' => $Exception->getMessage()
+                    )
+                )
+            );
+
+            return false;
+
+        } catch (QUI\Permissions\Exception $Exception) {
+            throw $Exception;
+        } catch (\Exception $Exception) {
+            QUI\System\Log::writeException($Exception);
+
+            QUI::getMessagesHandler()->addError(
+                QUI::getLocale()->get(
+                    'quiqqer/invitecode',
+                    'message.ajax.general_error'
+                )
+            );
+
+            return false;
+        }
+
+        QUI::getMessagesHandler()->addSuccess(
+            QUI::getLocale()->get(
+                'quiqqer/invitecode',
+                'message.ajax.create.success'
+            )
+        );
+
+        return true;
+    },
+    array('attributes'),
+    'Permission::checkAdminUser'
+);
diff --git a/ajax/delete.php b/ajax/delete.php
new file mode 100644
index 0000000000000000000000000000000000000000..b6da82b9865f4cf93ca3d1882d051fb780a7fe1f
--- /dev/null
+++ b/ajax/delete.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * This file contains package_quiqqer_coupons_ajax_delete
+ */
+
+use QUI\ERP\Coupons\Handler;
+use QUI\Utils\Security\Orthos;
+
+/**
+ * Delete InviteCodes
+ *
+ * @param array $ids - InviteCode IDs
+ * @return bool - success
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_coupons_ajax_delete',
+    function ($ids) {
+        $ids = Orthos::clearArray(json_decode($ids, true));
+
+        try {
+            foreach ($ids as $id) {
+                $CouponCode = Handler::getCouponCode((int)$id);
+                $CouponCode->delete();
+            }
+        } catch (\QUI\ERP\Coupons\CouponCodeException $Exception) {
+            QUI::getMessagesHandler()->addError(
+                QUI::getLocale()->get(
+                    'quiqqer/invitecode',
+                    'message.ajax.delete.error',
+                    array(
+                        'error' => $Exception->getMessage()
+                    )
+                )
+            );
+
+            return false;
+        } catch (QUI\Permissions\Exception $Exception) {
+            throw $Exception;
+        } catch (\Exception $Exception) {
+            QUI\System\Log::writeException($Exception);
+
+            QUI::getMessagesHandler()->addError(
+                QUI::getLocale()->get(
+                    'quiqqer/invitecode',
+                    'message.ajax.general_error'
+                )
+            );
+
+            return false;
+        }
+
+        QUI::getMessagesHandler()->addSuccess(
+            QUI::getLocale()->get(
+                'quiqqer/invitecode',
+                'message.ajax.delete.success'
+            )
+        );
+
+        return true;
+    },
+    array('ids'),
+    'Permission::checkAdminUser'
+);
diff --git a/ajax/getList.php b/ajax/getList.php
new file mode 100644
index 0000000000000000000000000000000000000000..022b2e8ae901f8a595c3599cf264e8395b0efa1b
--- /dev/null
+++ b/ajax/getList.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * This file contains package_quiqqer_coupons_ajax_getList
+ */
+
+use QUI\ERP\Coupons\Handler;
+use QUI\Utils\Security\Orthos;
+use QUI\Utils\Grid;
+use QUI\Permissions\Permission;
+
+/**
+ * Get list of CouponCodes
+ *
+ * @param array $searchParams
+ * @return array
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_coupons_ajax_getList',
+    function ($searchParams) {
+        Permission::hasPermission(Handler::PERMISSION_VIEW);
+
+        $searchParams = Orthos::clearArray(json_decode($searchParams, true));
+        $inviteCodes  = array();
+
+        try {
+            foreach (Handler::search($searchParams) as $InviteCode) {
+                $inviteCodes[] = $InviteCode->toArray();
+            }
+        } catch (\Exception $Exception) {
+            QUI\System\Log::writeException($Exception);
+
+            QUI::getMessagesHandler()->addSuccess(
+                QUI::getLocale()->get(
+                    'quiqqer/invitecode',
+                    'message.ajax.general_error'
+                )
+            );
+
+            return false;
+        }
+
+        $Grid = new Grid($searchParams);
+
+        return $Grid->parseResult(
+            $inviteCodes,
+            Handler::search($searchParams, true)
+        );
+    },
+    array('searchParams'),
+    'Permission::checkAdminUser'
+);
diff --git a/ajax/settings/getCodeGenerators.php b/ajax/settings/getCodeGenerators.php
new file mode 100644
index 0000000000000000000000000000000000000000..458204946cfc1de2b09ec07074423a17124f5996
--- /dev/null
+++ b/ajax/settings/getCodeGenerators.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * This file contains package_quiqqer_coupons_ajax_settings_getCodeGenerators
+ */
+
+use QUI\ERP\Coupons\CodeGenerator;
+
+/**
+ * Get list of all CodeGenerators
+ *
+ * @return array
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_coupons_ajax_settings_getCodeGenerators',
+    function () {
+        return CodeGenerator::getList();
+    },
+    array(),
+    'Permission::checkAdminUser'
+);
diff --git a/bin/CouponCodes.js b/bin/CouponCodes.js
new file mode 100644
index 0000000000000000000000000000000000000000..8656be40a41f7144426a6830846dfa3bc196b68d
--- /dev/null
+++ b/bin/CouponCodes.js
@@ -0,0 +1,17 @@
+/**
+ * CouponCodes Handler
+ *
+ * @module package/quiqqer/coupons/bin/classes/CouponCodes
+ * @author www.pcsg.de (Patrick Müller)
+ *
+ * @require Ajax
+ */
+define('package/quiqqer/coupons/bin/CouponCodes', [
+
+    'package/quiqqer/coupons/bin/classes/CouponCodes'
+
+], function (CouponCodesManager) {
+    "use strict";
+
+    return new CouponCodesManager();
+});
diff --git a/bin/classes/CouponCodes.js b/bin/classes/CouponCodes.js
new file mode 100644
index 0000000000000000000000000000000000000000..54241c086e05f36ceace0806fdd90c63ed6903df
--- /dev/null
+++ b/bin/classes/CouponCodes.js
@@ -0,0 +1,88 @@
+/**
+ * CouponCodes Handler
+ *
+ * @module package/quiqqer/coupons/bin/classes/CouponCodes
+ * @author www.pcsg.de (Patrick Müller)
+ *
+ * @require Ajax
+ */
+define('package/quiqqer/coupons/bin/classes/CouponCodes', [
+
+    'Ajax'
+
+], function (QUIAjax) {
+    "use strict";
+
+    var pkg = 'quiqqer/coupons';
+
+    return new Class({
+
+        Type: 'package/quiqqer/coupons/bin/classes/CouponCodes',
+
+        /**
+         * Create new InviteCode
+         *
+         * @param {Object} Attributes
+         * @return {Promise}
+         */
+        create: function (Attributes) {
+            return new Promise(function (resolve, reject) {
+                QUIAjax.post('package_quiqqer_coupons_ajax_create', resolve, {
+                    'package' : pkg,
+                    attributes: JSON.encode(Attributes),
+                    onError   : reject
+                });
+            });
+        },
+
+        /**
+         * Delete Invite Codes
+         *
+         * @param {Array} ids
+         * @return {Promise}
+         */
+        delete: function (ids) {
+            return new Promise(function (resolve, reject) {
+                QUIAjax.post('package_quiqqer_coupons_ajax_delete', resolve, {
+                    'package': pkg,
+                    ids      : JSON.encode(ids),
+                    onError  : reject
+                });
+            });
+        },
+
+        /**
+         * Get list of all CouponCodes
+         *
+         * @param {Object} SearchParams
+         * @return {Promise}
+         */
+        getList: function (SearchParams) {
+            return new Promise(function (resolve, reject) {
+                QUIAjax.get('package_quiqqer_coupons_ajax_getList', resolve, {
+                    'package'   : pkg,
+                    searchParams: JSON.encode(SearchParams),
+                    onError     : reject
+                });
+            });
+        },
+
+        /**
+         * Send CouponCodes via mail
+         *
+         * @param {Array} ids
+         * @param {Boolean} resend
+         * @return {Promise}
+         */
+        sendMail: function (ids, resend) {
+            return new Promise(function (resolve, reject) {
+                QUIAjax.post('package_quiqqer_coupons_ajax_sendMail', resolve, {
+                    'package': pkg,
+                    ids      : JSON.encode(ids),
+                    resend   : resend ? 1 : 0,
+                    onError  : reject
+                });
+            });
+        }
+    });
+});
diff --git a/bin/controls/Manager.Create.html b/bin/controls/Manager.Create.html
new file mode 100644
index 0000000000000000000000000000000000000000..3f8d80e0dd1c25e2dad21453575b66e4f73a6045
--- /dev/null
+++ b/bin/controls/Manager.Create.html
@@ -0,0 +1,24 @@
+<div class="quiqqer-invitecode-manager-create">
+    <form>
+        <label>
+            <span>{{labelTitle}}</span>
+            <input type="text" name="title"/>
+        </label>
+        <label>
+            <span>{{labelEmail}}</span>
+            <input type="email" name="email"/>
+        </label>
+        <label class="quiqqer-invitecode-manager-create-sendmail">
+            <span>{{labelSendMail}}</span>
+            <input type="checkbox" name="sendmail" disabled/>
+        </label>
+        <label>
+            <span>{{labelDate}}</span>
+            <input type="date" placeholder="YYYY-MM-DD HH:MM" name="validUntil"/>
+        </label>
+        <label>
+            <span>{{labelAmount}}</span>
+            <input type="number" name="amount" min="1" value="1"/>
+        </label>
+    </form>
+</div>
\ No newline at end of file
diff --git a/bin/controls/Manager.css b/bin/controls/Manager.css
new file mode 100644
index 0000000000000000000000000000000000000000..1eebca97c7f5cf6ff2ba4bbe497af7c7b52a3c1f
--- /dev/null
+++ b/bin/controls/Manager.css
@@ -0,0 +1,62 @@
+.quiqqer-invitecode-manager {
+    float: left;
+    height: 100%;
+    width: 100%;
+}
+
+.quiqqer-invitecode-manager-table {
+    float: left;
+    height: 100% !important;
+    width: 100%;
+}
+
+.quiqqer-invitecode-manager-create label {
+    float: left;
+    width: 100%;
+}
+
+.quiqqer-invitecode-manager-create label:not(:last-of-type) {
+    margin-bottom: 15px;
+}
+
+.quiqqer-invitecode-manager-create label span {
+    float: left;
+    margin-bottom: 5px;
+}
+
+.quiqqer-invitecode-manager-create-sendmail span,
+.quiqqer-invitecode-manager-create-sendmail input {
+    float: right !important;
+    width: unset !important;
+}
+
+.quiqqer-invitecode-manager-create input {
+    width: 100%;
+}
+
+.quiqqer-invitecode-manager-tbl-status-unused {
+    color: #bb252a;
+}
+
+.quiqqer-invitecode-manager-tbl-status-used {
+    color: #608a0e;
+}
+
+.quiqqer-invitecode-manager-tbl-status-invalid {
+    color: #454545;
+    font-style: italic;
+}
+
+.quiqqer-invitecode-manager-sendmail-resend {
+    float: right;
+    margin-top: 20px;
+}
+
+.quiqqer-invitecode-manager-sendmail-resend input {
+    float: left;
+}
+
+.quiqqer-invitecode-manager-tbl-user-not_exist {
+    color: #999;
+    font-style: italic;
+}
\ No newline at end of file
diff --git a/bin/controls/Manager.html b/bin/controls/Manager.html
new file mode 100644
index 0000000000000000000000000000000000000000..8529310bf03357d344143bdea36343f3aa57de48
--- /dev/null
+++ b/bin/controls/Manager.html
@@ -0,0 +1 @@
+<div class="quiqqer-invitecode-manager-table"></div>
\ No newline at end of file
diff --git a/bin/controls/Manager.js b/bin/controls/Manager.js
new file mode 100644
index 0000000000000000000000000000000000000000..fde8614d4357b23ba18b6cba209316c625330860
--- /dev/null
+++ b/bin/controls/Manager.js
@@ -0,0 +1,624 @@
+/**
+ * Manage Invite Codes
+ *
+ * @module package/quiqqer/coupons/bin/controls/Manager
+ * @author www.pcsg.de (Patrick Müller)
+ */
+define('package/quiqqer/coupons/bin/controls/Manager', [
+
+    'qui/controls/desktop/Panel',
+    'qui/controls/loader/Loader',
+    'qui/controls/windows/Popup',
+    'qui/controls/windows/Confirm',
+    'qui/controls/buttons/Button',
+    'qui/controls/buttons/Separator',
+
+    'controls/grid/Grid',
+    'qui/utils/Form',
+
+    'package/quiqqer/coupons/bin/CouponCodes',
+
+    'Locale',
+    'Mustache',
+
+    'text!package/quiqqer/coupons/bin/controls/Manager.html',
+    'text!package/quiqqer/coupons/bin/controls/Manager.Create.html',
+    'css!package/quiqqer/coupons/bin/controls/Manager.css'
+
+], function (QUIPanel, QUILoader, QUIPopup, QUIConfirm, QUIButton, QUISeparator,
+             Grid, QUIFormUtils, CouponCodes, QUILocale, Mustache, template, templateCreate) {
+    "use strict";
+
+    var lg = 'quiqqer/coupons';
+
+    return new Class({
+
+        Extends: QUIPanel,
+        Type   : 'package/quiqqer/coupons/bin/controls/Manager',
+
+        Binds: [
+            '$onCreate',
+            '$onResize',
+            '$listRefresh',
+            '$onRefresh',
+            '$load',
+            '$setGridData',
+            '$create',
+            '$toggleActiveStatus',
+            '$managePackages',
+            '$delete',
+            '$editBundle',
+            'refresh',
+            '$openUserPanel',
+            '$sendMail'
+        ],
+
+        options: {
+            title: QUILocale.get(lg, 'controls.manager.title')
+        },
+
+        initialize: function (options) {
+            this.parent(options);
+
+            this.Loader      = new QUILoader();
+            this.$User       = null;
+            this.$Grid       = null;
+            this.$GridParent = null;
+            this.$Panel      = null;
+
+            this.addEvents({
+                onCreate : this.$onCreate,
+                onRefresh: this.$onRefresh,
+                onResize : this.$onResize
+            });
+        },
+
+        /**
+         * Event: onCreate
+         */
+        $onCreate: function () {
+            var self = this;
+
+            this.Loader.inject(this.$Elm);
+
+            this.addButton({
+                name     : 'create',
+                text     : QUILocale.get(lg, 'controls.manager.tbl.btn.create'),
+                textimage: 'fa fa-plus',
+                events   : {
+                    onClick: function () {
+                        self.$create();
+                    }
+                }
+            });
+
+            this.addButton({
+                name     : 'quickcreate',
+                text     : QUILocale.get(lg, 'controls.manager.tbl.btn.quickcreate'),
+                textimage: 'fa fa-plus',
+                events   : {
+                    onClick: function () {
+                        self.$create(true);
+                    }
+                }
+            });
+
+            this.addButton(new QUISeparator());
+
+            this.addButton({
+                name     : 'sendmail',
+                text     : QUILocale.get(lg, 'controls.manager.tbl.btn.sendmail'),
+                textimage: 'fa fa-envelope-o',
+                events   : {
+                    onClick: this.$sendMail
+                }
+            });
+
+            this.addButton(new QUISeparator());
+
+            this.addButton({
+                name     : 'delete',
+                text     : QUILocale.get(lg, 'controls.manager.tbl.btn.delete'),
+                textimage: 'fa fa-trash',
+                events   : {
+                    onClick: this.$delete
+                }
+            });
+
+            this.$load();
+        },
+
+        /**
+         * Refresh data
+         */
+        refresh: function () {
+            if (this.$Grid) {
+                this.$Grid.refresh();
+            }
+        },
+
+        /**
+         * event: onResize
+         */
+        $onResize: function () {
+            if (this.$GridParent && this.$Grid) {
+                var size = this.$GridParent.getSize();
+
+                this.$Grid.setHeight(size.y);
+                this.$Grid.resize();
+            }
+        },
+
+        /**
+         * Load Grid
+         */
+        $load: function () {
+            var self = this;
+
+            this.setContent(Mustache.render(template));
+            var Content = this.getContent();
+
+            this.$GridParent = Content.getElement(
+                '.quiqqer-invitecode-manager-table'
+            );
+
+            this.$Grid = new Grid(this.$GridParent, {
+                columnModel      : [{
+                    header   : QUILocale.get('quiqqer/system', 'id'),
+                    dataIndex: 'id',
+                    dataType : 'number',
+                    width    : 50
+                }, {
+                    header   : QUILocale.get(lg, 'controls.manager.tbl.header.code'),
+                    dataIndex: 'code',
+                    dataType : 'string',
+                    width    : 150
+                }, {
+                    header   : QUILocale.get(lg, 'controls.manager.tbl.header.title'),
+                    dataIndex: 'title',
+                    dataType : 'string',
+                    width    : 200
+                }, {
+                    header   : QUILocale.get(lg, 'controls.manager.tbl.header.status'),
+                    dataIndex: 'status',
+                    dataType : 'node',
+                    width    : 200
+                }, {
+                    header   : QUILocale.get(lg, 'controls.manager.tbl.header.email'),
+                    dataIndex: 'email',
+                    dataType : 'string',
+                    width    : 200
+                }, {
+                    header   : QUILocale.get(lg, 'controls.manager.tbl.header.mailSent'),
+                    dataIndex: 'mailSent',
+                    dataType : 'node',
+                    width    : 30
+                }, {
+                    header   : QUILocale.get(lg, 'controls.manager.tbl.header.user'),
+                    dataIndex: 'user',
+                    dataType : 'node',
+                    width    : 200,
+                    className: 'clickable'
+                }, {
+                    header   : QUILocale.get(lg, 'controls.manager.tbl.header.validUntilDate'),
+                    dataIndex: 'validUntilDate',
+                    dataType : 'string',
+                    width    : 150
+                }, {
+                    header   : QUILocale.get(lg, 'controls.manager.tbl.header.createDate'),
+                    dataIndex: 'createDate',
+                    dataType : 'string',
+                    width    : 150
+                }, {
+                    dataIndex: 'userId',
+                    dataType : 'number',
+                    hidden   : true
+                }],
+                pagination       : true,
+                serverSort       : true,
+                selectable       : true,
+                multipleSelection: true
+            });
+
+            this.$Grid.addEvents({
+                onDblClick: function () {
+                    // @todo
+                    //self.$managePackages(
+                    //    self.$Grid.getSelectedData()[0].id
+                    //);
+                },
+                onClick   : function (event) {
+                    var selected = self.$Grid.getSelectedData();
+
+                    self.getButtons('delete').enable();
+                    self.getButtons('sendmail').enable();
+
+                    if (!event.cell.hasClass('clickable')) {
+                        return;
+                    }
+
+                    var Row = selected[0];
+
+                    if (Row.userId) {
+                        self.Loader.show();
+
+                        self.$openUserPanel(Row.userId).then(function () {
+                            self.Loader.hide();
+                        });
+                    }
+                },
+                onRefresh : this.$listRefresh
+            });
+
+            this.resize();
+            this.$Grid.refresh();
+        },
+
+        /**
+         * Event: onRefresh
+         */
+        $onRefresh: function () {
+            if (this.$Grid) {
+                this.$Grid.refresh();
+            }
+        },
+
+        /**
+         * Refresh bundle list
+         *
+         * @param {Object} Grid
+         */
+        $listRefresh: function (Grid) {
+            if (!this.$Grid) {
+                return;
+            }
+
+            var self = this;
+
+            self.getButtons('delete').disable();
+            self.getButtons('sendmail').disable();
+
+            var GridParams = {
+                sortOn : Grid.getAttribute('sortOn'),
+                sortBy : Grid.getAttribute('sortBy'),
+                perPage: Grid.getAttribute('perPage'),
+                page   : Grid.getAttribute('page')
+            };
+
+            switch (GridParams.sortOn) {
+                case 'status':
+                    GridParams.sortOn = 'useDate';
+                    break;
+
+                case 'user':
+                    GridParams.sortOn = 'userId';
+                    break;
+            }
+
+            this.Loader.show();
+
+            CouponCodes.getList(GridParams).then(function (ResultData) {
+                self.Loader.hide();
+                self.$setGridData(ResultData);
+            });
+        },
+
+        /**
+         * Set license data to grid
+         *
+         * @param {Object} GridData
+         */
+        $setGridData: function (GridData) {
+            var textUnused       = QUILocale.get(lg, 'controls.manager.tbl.status.unused');
+            var textUnlimited    = QUILocale.get(lg, 'controls.manager.tbl.validUntil.unlimited');
+            var textInvalid      = QUILocale.get(lg, 'controls.manager.tbl.status.invalid');
+            var textUserNotExist = QUILocale.get(lg, 'controls.manager.tbl.user.not_exist');
+
+            for (var i = 0, len = GridData.data.length; i < len; i++) {
+                var Row = GridData.data[i];
+
+                if (!Row.email) {
+                    Row.email = '-';
+                }
+
+                var StatusElm = new Element('span', {
+                    'class': 'quiqqer-invitecode-manager-tbl-status'
+                });
+
+                if (!Row.valid) {
+                    StatusElm.set('html', textInvalid);
+                    StatusElm.addClass('quiqqer-invitecode-manager-tbl-status-invalid');
+                } else if (!Row.useDate) {
+                    StatusElm.set('html', textUnused);
+                    StatusElm.addClass('quiqqer-invitecode-manager-tbl-status-unused');
+                } else {
+                    StatusElm.set('html', QUILocale.get(lg, 'controls.manager.tbl.status.used', {
+                        useDate: Row.useDate
+                    }));
+                    StatusElm.addClass('quiqqer-invitecode-manager-tbl-status-used');
+                }
+
+                Row.status = StatusElm;
+
+                if (!Row.validUntilDate) {
+                    Row.validUntilDate = textUnlimited;
+                }
+
+                if (!Row.userId) {
+                    if (Row.useDate) {
+                        Row.user = new Element('span', {
+                            'class': 'quiqqer-invitecode-manager-tbl-user-not_exist',
+                            html   : textUserNotExist
+                        });
+                    } else {
+                        Row.user = new Element('span', {html: '-'});
+                    }
+                } else {
+                    Row.user = new Element('div', {
+                        'class': 'quiqqer-invitecode-manager-tbl-user',
+                        html   : Row.username
+                    });
+                }
+
+                if (!Row.title) {
+                    Row.title = '-';
+                }
+
+                var MailSentElm = new Element('span', {
+                    'class': 'fa'
+                });
+
+                if (!Row.mailSent) {
+                    MailSentElm.addClass('fa-close');
+                } else {
+                    MailSentElm.addClass('fa-check');
+                }
+
+                Row.mailSent = MailSentElm;
+            }
+
+            this.$Grid.setData(GridData);
+        },
+
+        /**
+         * Create new InviteCode
+         *
+         * @param {Boolean} [quickCreate]
+         */
+        $create: function (quickCreate) {
+            var self = this;
+
+            quickCreate = quickCreate || false;
+
+            if (quickCreate) {
+                CouponCodes.create({}).then(function (inviteCodeId) {
+                    if (!inviteCodeId) {
+                        return;
+                    }
+
+                    self.refresh();
+                });
+
+                return;
+            }
+
+            var FuncSubmit = function () {
+                var Content = Popup.getContent();
+                var Form    = Content.getElement('form');
+
+                Popup.Loader.show();
+
+                CouponCodes.create(QUIFormUtils.getFormData(Form)).then(function (inviteCodeId) {
+                    if (!inviteCodeId) {
+                        Popup.Loader.hide();
+                        return;
+                    }
+
+                    self.refresh();
+                    Popup.close();
+                });
+            };
+
+            // open popup
+            var lgPrefix = 'controls.manager.create.template.';
+
+            var Popup = new QUIPopup({
+                icon       : 'fa fa-plus',
+                title      : QUILocale.get(
+                    lg, 'controls.manager.create.popup.title'
+                ),
+                maxHeight  : 450,
+                maxWidth   : 450,
+                events     : {
+                    onOpen: function () {
+                        var Content = Popup.getContent();
+                        var Form    = Content.getElement('form');
+
+                        Form.addEvent('submit', function (event) {
+                            event.stop();
+                            FuncSubmit();
+                        });
+
+                        var EmailInput       = Content.getElement('input[name="email"]');
+                        var SendMailCheckbox = Content.getElement('input[name="sendmail"]');
+
+                        EmailInput.addEvent('keyup', function (event) {
+                            if (event.target.value.trim() === '') {
+                                SendMailCheckbox.checked  = false;
+                                SendMailCheckbox.disabled = true;
+
+                                return;
+                            }
+
+                            SendMailCheckbox.disabled = false;
+                        });
+
+                        Content.getElement('input[name="title"]').focus();
+                    }
+                },
+                closeButton: true,
+                content    : Mustache.render(templateCreate, {
+                    labelTitle   : QUILocale.get(lg, lgPrefix + 'labelTitle'),
+                    labelEmail   : QUILocale.get(lg, lgPrefix + 'labelEmail'),
+                    labelDate    : QUILocale.get(lg, lgPrefix + 'labelDate'),
+                    labelSendMail: QUILocale.get(lg, lgPrefix + 'labelSendMail'),
+                    labelAmount  : QUILocale.get(lg, lgPrefix + 'labelAmount')
+                })
+            });
+
+            Popup.open();
+
+            Popup.addButton(new QUIButton({
+                text  : QUILocale.get(lg, 'controls.manager.create.popup.btn.confirm_text'),
+                alt   : QUILocale.get(lg, 'controls.manager.create.popup.btn.confirm'),
+                title : QUILocale.get(lg, 'controls.manager.create.popup.btn.confirm'),
+                events: {
+                    onClick: FuncSubmit
+                }
+            }));
+        },
+
+        /**
+         * Remove all selected licenses
+         */
+        $delete: function () {
+            var self       = this;
+            var deleteData = [];
+            var deleteIds  = [];
+            var rows       = this.$Grid.getSelectedData();
+
+            for (var i = 0, len = rows.length; i < len; i++) {
+                deleteData.push(
+                    rows[i].title + ' (ID: #' + rows[i].id + ')'
+                );
+
+                deleteIds.push(rows[i].id);
+            }
+
+            // open popup
+            var Popup = new QUIConfirm({
+                'maxHeight': 300,
+                'autoclose': false,
+
+                'information': QUILocale.get(
+                    lg,
+                    'controls.manager.delete.popup.info', {
+                        codes: deleteData.join('<br/>')
+                    }
+                ),
+                'title'      : QUILocale.get(lg, 'controls.manager.delete.popup.title'),
+                'texticon'   : 'fa fa-trash',
+                text         : QUILocale.get(lg, 'controls.manager.delete.popup.title'),
+                'icon'       : 'fa fa-trash',
+
+                cancel_button: {
+                    text     : false,
+                    textimage: 'icon-remove fa fa-remove'
+                },
+                ok_button    : {
+                    text     : false,
+                    textimage: 'icon-ok fa fa-check'
+                },
+                events       : {
+                    onSubmit: function () {
+                        Popup.Loader.show();
+
+                        CouponCodes.delete(deleteIds).then(function (success) {
+                            if (!success) {
+                                Popup.Loader.hide();
+                                return;
+                            }
+
+                            Popup.close();
+                            self.refresh();
+                        });
+                    }
+                }
+            });
+
+            Popup.open();
+        },
+
+        /**
+         * Send CouponCodes via Mail
+         */
+        $sendMail: function () {
+            var self        = this;
+            var sendMailIds = [];
+            var rows        = this.$Grid.getSelectedData();
+
+            for (var i = 0, len = rows.length; i < len; i++) {
+                sendMailIds.push(rows[i].id);
+            }
+
+            // open popup
+            var Popup = new QUIConfirm({
+                'maxHeight': 300,
+                'autoclose': false,
+
+                'information': QUILocale.get(lg, 'controls.manager.sendmail.popup.info'),
+                'title'      : QUILocale.get(lg, 'controls.manager.sendmail.popup.title'),
+                'text'       : QUILocale.get(lg, 'controls.manager.sendmail.popup.title'),
+                'texticon'   : 'fa fa-envelope-o',
+                'icon'       : 'fa fa-envelope-o',
+
+                cancel_button: {
+                    text     : false,
+                    textimage: 'fa fa-remove'
+                },
+                ok_button    : {
+                    text     : false,
+                    textimage: 'fa fa-check'
+                },
+                events       : {
+                    onOpen  : function () {
+                        var Content = Popup.getContent();
+
+                        new Element('label', {
+                            'class': 'quiqqer-invitecode-manager-sendmail-resend',
+                            html   : '<span>' +
+                            QUILocale.get(lg, 'controls.manager.sendmail.popup.label.resend') +
+                            '</span>' +
+                            '<input type="checkbox" name="resend"/>'
+                        }).inject(Content);
+                    },
+                    onSubmit: function () {
+                        Popup.Loader.show();
+
+                        var SendMailInput = Popup.getContent().getElement(
+                            'input[name="resend"]'
+                        );
+
+                        CouponCodes.sendMail(sendMailIds, SendMailInput.checked).then(function (success) {
+                            if (!success) {
+                                Popup.Loader.hide();
+                                return;
+                            }
+
+                            Popup.close();
+                            self.refresh();
+                        });
+                    }
+                }
+            });
+
+            Popup.open();
+        },
+
+        /**
+         * Open user panel
+         *
+         * @param {Number} userId
+         */
+        $openUserPanel: function (userId) {
+            return new Promise(function (resolve, reject) {
+                require([
+                    'controls/users/User',
+                    'utils/Panels'
+                ], function (UserPanel, PanelUtils) {
+                    PanelUtils.openPanelInTasks(new UserPanel(userId)).then(resolve, reject);
+                }.bind(this));
+            });
+        }
+    });
+});
diff --git a/bin/controls/settings/CodeGeneratorSelect.css b/bin/controls/settings/CodeGeneratorSelect.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/bin/controls/settings/CodeGeneratorSelect.js b/bin/controls/settings/CodeGeneratorSelect.js
new file mode 100644
index 0000000000000000000000000000000000000000..3495cdac638fc51122c0588ef73e5517640f0c7f
--- /dev/null
+++ b/bin/controls/settings/CodeGeneratorSelect.js
@@ -0,0 +1,95 @@
+/**
+ * Select available CodeGenerators
+ *
+ * @module package/quiqqer/coupons/bin/controls/settings/CodeGeneratorSelect
+ * @author www.pcsg.de (Patrick Müller)
+ */
+define('package/quiqqer/coupons/bin/controls/settings/CodeGeneratorSelect', [
+
+    'qui/controls/buttons/Select',
+    'qui/controls/loader/Loader',
+
+    'Locale',
+    'Ajax',
+
+    'css!package/quiqqer/coupons/bin/controls/settings/CodeGeneratorSelect.css'
+
+], function (QUISelect, QUILoader, QUILocale, QUIAjax) {
+    "use strict";
+
+    var lg = 'quiqqer/coupons';
+
+    return new Class({
+        Extends: QUISelect,
+        Type   : 'package/quiqqer/coupons/bin/controls/settings/CodeGeneratorSelect',
+
+        Binds: [
+            '$onImport'
+        ],
+
+        options: {
+            showIcons: false
+        },
+
+        initialize: function (options) {
+            this.parent(options);
+
+            this.$Input = null;
+            this.Loader = new QUILoader();
+
+            this.addEvents({
+                onImport: this.$onImport
+            });
+        },
+
+        /**
+         * Event: onImport
+         */
+        $onImport: function () {
+            var self = this;
+
+            this.$Input        = this.getElm();
+            this.$Input.hidden = true;
+
+            var Elm = this.create().inject(this.$Input, 'after');
+
+            Elm.addClass('field-container-field');
+
+            this.Loader.inject(Elm);
+            this.Loader.show();
+
+            this.$getCodeGenerators().then(function(codeGenerators) {
+                self.Loader.hide();
+
+                for (var i = 0, len = codeGenerators.length; i < len; i++) {
+                    self.appendChild(
+                        codeGenerators[i],
+                        codeGenerators[i]
+                    );
+                }
+
+                if (self.$Input.value !== '') {
+                    self.setValue(self.$Input.value);
+                }
+
+                self.addEvent('onChange', function(value) {
+                    self.$Input.value = value;
+                });
+            });
+        },
+
+        /**
+         * Get list of all CodeGenerators
+         *
+         * @return {Promise}
+         */
+        $getCodeGenerators: function () {
+            return new Promise(function (resolve, reject) {
+                QUIAjax.get('package_quiqqer_invitecode_ajax_settings_getCodeGenerators', resolve, {
+                    'package': 'quiqqer/coupons',
+                    onError  : reject
+                });
+            });
+        }
+    });
+});
diff --git a/bin/images/Gitlab.jpg b/bin/images/Gitlab.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b959f7bc26112f57f570dd7e062220d8c345d831
Binary files /dev/null and b/bin/images/Gitlab.jpg differ
diff --git a/bin/images/Logo.jpg b/bin/images/Logo.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..06f1447ca11294bd13003de422d8194a323d6127
Binary files /dev/null and b/bin/images/Logo.jpg differ
diff --git a/bin/images/Readme.jpg b/bin/images/Readme.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..1359d785122e3f28e483978390055f9e30558ecb
Binary files /dev/null and b/bin/images/Readme.jpg differ
diff --git a/bin/images/preview/Backend_View.png b/bin/images/preview/Backend_View.png
new file mode 100644
index 0000000000000000000000000000000000000000..c9edf5f0dba2c167303cb1bfa89b4031069a1f94
Binary files /dev/null and b/bin/images/preview/Backend_View.png differ
diff --git a/bin/images/preview/Frontend_Registrar.png b/bin/images/preview/Frontend_Registrar.png
new file mode 100644
index 0000000000000000000000000000000000000000..0c6b74acb638f1edbd0ce24be8080ec01efe5747
Binary files /dev/null and b/bin/images/preview/Frontend_Registrar.png differ
diff --git a/bin/images/preview/Settings.png b/bin/images/preview/Settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..40d702994fdb1147ac467cca9cb399d75defd638
Binary files /dev/null and b/bin/images/preview/Settings.png differ
diff --git a/database.xml b/database.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fb3120b5f946c2b3505a2fe21826674a9b88e452
--- /dev/null
+++ b/database.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<database>
+
+    <global>
+        <table name="quiqqer_coupons">
+            <field type="BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY">id</field>
+            <field type="VARCHAR(255) NOT NULL">code</field>
+            <field type="VARCHAR(255) NULL">title</field>
+            <field type="VARCHAR(255) NULL">userIds</field>
+            <field type="VARCHAR(255) NULL">groupIds</field>
+            <field type="DATETIME NOT NULL">createDate</field>
+            <field type="DATETIME NULL">validUntilDate</field>
+            <field type="MEDIUMTEXT NULL">usages</field>
+            <field type="TINYINT(1) NOT NULL DEFAULT 0">isReusable</field>
+        </table>
+    </global>
+
+</database>
diff --git a/locale.xml b/locale.xml
index 297030e27a5efe21fba7c97c9d9c527e535b2c2b..3f5dae8f90d6da2a045750c413de26dfc9d1916a 100644
--- a/locale.xml
+++ b/locale.xml
@@ -9,5 +9,126 @@
             <de><![CDATA[Verwaltung von Coupon-Codes für die Verwendung im QUIQQER ERP Shop]]></de>
             <en><![CDATA[Management of coupon codes for usage in the QUIQQER ERP Shop]]></en>
         </locale>
+
+        <!-- Menu -->
+        <locale name="menu.erp.title">
+            <de><![CDATA[Shop]]></de>
+            <en><![CDATA[Shop]]></en>
+        </locale>
+        <locale name="menu.erp.couponcodes.title">
+            <de><![CDATA[Coupon-Codes]]></de>
+            <en><![CDATA[Coupon codes]]></en>
+        </locale>
+
+        <!-- Settings -->
+        <locale name="settings.title">
+            <de><![CDATA[Allgemeine Einstellungen]]></de>
+            <en><![CDATA[General settings]]></en>
+        </locale>
+        <locale name="settings.menu.title">
+            <de><![CDATA[Coupon-Codes]]></de>
+            <en><![CDATA[Coupon codes]]></en>
+        </locale>
+        <locale name="settings.category.title">
+            <de><![CDATA[Einstellungen]]></de>
+            <en><![CDATA[Settings]]></en>
+        </locale>
+        <locale name="settings.prefix.title">
+            <de><![CDATA[Coupon-Code Präfix]]></de>
+            <en><![CDATA[Coupon code prefix]]></en>
+        </locale>
+        <locale name="settings.prefix.description" html="true">
+            <de><![CDATA[Präfix, das generierten Coupon-Codes vorangestellt wird. <b>Betrifft nicht bereits erstellte oder eigenhändig festgelegte Coupon-Codes!</b>]]></de>
+            <en><![CDATA[Prefix that is prepended to generated Coupon codes. <b>Does not affect already created Coupon codes or codes set by the user!</b>]]></en>
+        </locale>
+        <locale name="settings.codeGenerator.title">
+            <de><![CDATA[Coupon-Code Generator]]></de>
+            <en><![CDATA[Coupon code Generator]]></en>
+        </locale>
+        <locale name="settings.codeGenerator.description">
+            <de><![CDATA[Bestimmt die Klasse, die zufällige Coupon-Codes generiert. Hat auch Auswirkungen auf das Format der Coupon-Codes.]]></de>
+            <en><![CDATA[Determines the class that generates random invitation codes. Also affects the format of Coupon codes.]]></en>
+        </locale>
+
+        <!-- Permissions -->
+        <locale name="permission.quiqqer.invitecode._header">
+            <de><![CDATA[Coupon-Codes]]></de>
+            <en><![CDATA[Coupon codes]]></en>
+        </locale>
+        <locale name="permission.quiqqer.invitecode.view">
+            <de><![CDATA[Darf Liste mit Coupon-Codes ansehen]]></de>
+            <en><![CDATA[Is allowed to view Coupon codes list]]></en>
+        </locale>
+        <locale name="permission.quiqqer.invitecode.create">
+            <de><![CDATA[Darf neue Coupon-Codes erstellen]]></de>
+            <en><![CDATA[Is allowed to create new Coupon codes]]></en>
+        </locale>
+        <locale name="permission.quiqqer.invitecode.delete">
+            <de><![CDATA[Darf Coupon-Codes löschen]]></de>
+            <en><![CDATA[Is allowed to delete Coupon codes]]></en>
+        </locale>
+    </groups>
+
+    <groups name="quiqqer/coupons" datatype="php">
+        <!-- Ajax -->
+        <locale name="message.ajax.general_error">
+            <de><![CDATA[Beim Verarbeiten der Anfrage ist ein unerwarteter Fehler aufgetreten. Bitte prüfen Sie die Fehler-Logs oder kontaktieren Sie einen Administrator.]]></de>
+            <en><![CDATA[An unexpected error occurred while processing the request. Please check the error logs or contact an administrator.]]></en>
+        </locale>
+        <locale name="message.ajax.create.error" html="true">
+            <de><![CDATA[Die Coupon-Codes konnten nicht erstellt werden:<br><br>[error]]]></de>
+            <en><![CDATA[The Coupon codes could not be created:<br><br>[error]]]></en>
+        </locale>
+        <locale name="message.ajax.create.success">
+            <de><![CDATA[Die Coupon-Codes wurden erfolgreich erstellt.]]></de>
+            <en><![CDATA[The Coupon codes have been successfully created.]]></en>
+        </locale>
+        <locale name="message.ajax.delete.error" html="true">
+            <de><![CDATA[Beim Löschen der Coupon-Codes ist ein Fehler aufgetreten:<br><br>[error]]]></de>
+            <en><![CDATA[An error occurred while deleting the Coupon codes:<br><br>[error].]]></en>
+        </locale>
+        <locale name="message.ajax.delete.success">
+            <de><![CDATA[Die Coupon-Codes wurden erfolgreich gelöscht.]]></de>
+            <en><![CDATA[The Coupon codes have been deleted successfully.]]></en>
+        </locale>
+
+        <!-- Class: CouponCode -->
+        <locale name="exception.CouponCode.not_found">
+            <de><![CDATA[Der Coupon-Code mit ID #[id] wurde nicht gefunden.]]></de>
+            <en><![CDATA[Coupon code with ID #[id] was not found.]]></en>
+        </locale>
+        <locale name="exception.CouponCode.no_longer_valid">
+            <de><![CDATA[Dieser Coupon-Code ist nicht mehr gültig.]]></de>
+            <en><![CDATA[This coupon code is not valid anymore.]]></en>
+        </locale>
+        <locale name="exception.CouponCode.already_used">
+            <de><![CDATA[Dieser Coupon-Code wurde bereits verwendet.]]></de>
+            <en><![CDATA[This coupon code has already been redeemed.]]></en>
+        </locale>
+        <locale name="exception.CouponCode.user_not_allowed">
+            <de><![CDATA[Dieser Coupon-Code ist nicht für diesen Benutzer gültig.]]></de>
+            <en><![CDATA[This coupon code is not valid for this user.]]></en>
+        </locale>
+        <locale name="exception.CouponCode.user_not_allowed_group">
+            <de><![CDATA[Dieser Coupon-Code ist nicht für diesen Benutzer gültig.]]></de>
+            <en><![CDATA[This coupon code is not valid for this user.]]></en>
+        </locale>
+
+        <!-- Class: Handler -->
+        <locale name="exception.Handler.user_already_exists">
+            <de><![CDATA[Es existiert bereits ein Benutzer mit der E-Mail-Adresse "[email]".]]></de>
+            <en><![CDATA[A user with e-mail address "[email]" already exists.]]></en>
+        </locale>
+        <locale name="exception.Handler.code_not_found">
+            <de><![CDATA[]]></de>
+            <en><![CDATA[]]></en>
+        </locale>
+        <locale name="exception.invitecode.no_registration_site">
+            <de><![CDATA[Es wurde noch keine Registrierungs-Seite festgelegt, auf die in der Invite-Code E-Mail verwiesen wird.]]></de>
+            <en><![CDATA[No registration page has yet been specified for the Invite Code email.]]></en>
+        </locale>
+    </groups>
+
+    <groups name="quiqqer/coupons" datatype="js">
     </groups>
 </locales>
diff --git a/menu.xml b/menu.xml
new file mode 100644
index 0000000000000000000000000000000000000000..98da8d98435c9844c1bc93bb8c435054c6be5c48
--- /dev/null
+++ b/menu.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<menu>
+    <item parent="/extras/"
+          name="erp"
+          icon="fa fa-sun-o"
+    >
+        <locale group="quiqqer/coupons" var="menu.erp.title" />
+    </item>
+    <item parent="/extras/erp/"
+          name="couponcodes"
+          require="package/quiqqer/coupons/bin/controls/Manager"
+          icon="fa fa-credit-card-alt"
+    >
+        <locale group="quiqqer/coupons" var="menu.erp.couponcodes.title" />
+    </item>
+
+</menu>
diff --git a/permissions.xml b/permissions.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9326e4cd0b81628ca643447d96ef34c19158507e
--- /dev/null
+++ b/permissions.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<permissions>
+
+    <permission name="quiqqer.coupons.view" type="bool">
+        <defaultvalue>0</defaultvalue>
+    </permission>
+    <permission name="quiqqer.coupons.create" type="bool">
+        <defaultvalue>0</defaultvalue>
+    </permission>
+    <permission name="quiqqer.coupons.delete" type="bool">
+        <defaultvalue>0</defaultvalue>
+    </permission>
+
+</permissions>
\ No newline at end of file
diff --git a/settings.xml b/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3630fcf2c822719adb1b79d8509f9ccf24b09a76
--- /dev/null
+++ b/settings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<quiqqer>
+    <settings>
+
+        <config>
+
+            <section name="settings">
+                <conf name="prefix">
+                    <type><![CDATA[string]]></type>
+                </conf>
+                <conf name="codeGenerator">
+                    <type><![CDATA[string]]></type>
+                    <defaultvalue><![CDATA[SimpleString]]></defaultvalue>
+                </conf>
+            </section>
+
+        </config>
+
+        <window>
+            <title>
+                <locale group="quiqqer/coupons" var="settings.menu.title"/>
+            </title>
+
+            <params>
+                <icon>fa fa-code</icon>
+            </params>
+
+            <categories>
+                <category name="settings">
+                    <title>
+                        <locale group="quiqqer/coupons" var="settings.category.title"/>
+                    </title>
+                    <settings title="settings" name="settings">
+                        <title>
+                            <locale group="quiqqer/coupons" var="settings.title"/>
+                        </title>
+
+                        <input conf="settings.prefix" type="text">
+                            <text>
+                                <locale group="quiqqer/coupons" var="settings.prefix.title"/>
+                            </text>
+                            <description>
+                                <locale group="quiqqer/coupons" var="settings.prefix.description"/>
+                            </description>
+                        </input>
+
+                        <input conf="settings.codeGenerator" type="text" data-qui="package/quiqqer/coupons/bin/controls/settings/CodeGeneratorSelect">
+                            <text>
+                                <locale group="quiqqer/coupons" var="settings.codeGenerator.title"/>
+                            </text>
+                            <description>
+                                <locale group="quiqqer/coupons" var="settings.codeGenerator.description"/>
+                            </description>
+                        </input>
+                    </settings>
+                </category>
+
+            </categories>
+        </window>
+
+    </settings>
+</quiqqer>
\ No newline at end of file
diff --git a/src/QUI/ERP/Coupons/CodeGenerator.php b/src/QUI/ERP/Coupons/CodeGenerator.php
new file mode 100644
index 0000000000000000000000000000000000000000..84df8687ab664d5463f2920d68502e8656635ee2
--- /dev/null
+++ b/src/QUI/ERP/Coupons/CodeGenerator.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace QUI\ERP\Coupons;
+
+use QUI;
+use QUI\Utils\System\File;
+
+/**
+ * Class CodeGenerator
+ *
+ * Generated unique, random Invite Codes
+ */
+class CodeGenerator
+{
+    /**
+     * Generate a new, random Invite Code
+     *
+     * @return string
+     * @throws \QUI\Exception
+     */
+    public static function generate()
+    {
+        $generator = '\\QUI\\ERP\\Coupons\\CodeGenerators\\' . self::getCurrentGenerator();
+
+        $Config = QUI::getPackage('quiqqer/coupons')->getConfig();
+        $prefix = $Config->get('settings', 'prefix');
+
+        if (empty($prefix)) {
+            $prefix = '';
+        }
+
+        return call_user_func($generator . '::generate', $prefix);
+    }
+
+    /**
+     * Get CodeGenerator that is currently set
+     *
+     * @return string - FQ class name
+     * @throws \QUI\Exception
+     */
+    protected static function getCurrentGenerator()
+    {
+        $Config           = QUI::getPackage('quiqqer/coupons')->getConfig();
+        $currentGenerator = $Config->get('settings', 'codeGenerator');
+
+        if (empty($currentGenerator)) {
+            return 'SimpleString';
+        }
+
+        return $currentGenerator;
+    }
+
+    /**
+     * Get list of all available CodeGenerators
+     *
+     * @return array
+     * @throws \QUI\Exception
+     */
+    public static function getList()
+    {
+        $dir        = QUI::getPackage('quiqqer/coupons')->getDir() . 'src/QUI/ERP/Coupons/CodeGenerators';
+        $generators = array();
+
+        foreach (File::readDir($dir, true) as $file) {
+            $generators[] = basename($file, '.php');
+        }
+
+        return $generators;
+    }
+}
diff --git a/src/QUI/ERP/Coupons/CodeGeneratorInterface.php b/src/QUI/ERP/Coupons/CodeGeneratorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..7918f69f1e75f5ad650a6738f8a453e8ff625ec4
--- /dev/null
+++ b/src/QUI/ERP/Coupons/CodeGeneratorInterface.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace QUI\ERP\Coupons;
+
+/**
+ * Interface CodeGeneratorInterface
+ *
+ * General interface for all code generators
+ */
+interface CodeGeneratorInterface
+{
+    /**
+     * Generate a random, unique Invite code
+     *
+     * @param string $prefix (optional)
+     * @return string
+     */
+    public static function generate($prefix = '');
+}
diff --git a/src/QUI/ERP/Coupons/CodeGenerators/SimpleString.php b/src/QUI/ERP/Coupons/CodeGenerators/SimpleString.php
new file mode 100644
index 0000000000000000000000000000000000000000..b7147fce886af4eec1d65ba7e21646b20c1032a9
--- /dev/null
+++ b/src/QUI/ERP/Coupons/CodeGenerators/SimpleString.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace QUI\ERP\Coupons\CodeGenerators;
+
+use QUI\InviteCode\CodeGeneratorInterface;
+use QUI\InviteCode\Handler;
+
+class SimpleString implements CodeGeneratorInterface
+{
+    const CODE_LENGTH = 8;
+
+    /**
+     * Generate a random, unique Invite code
+     *
+     * @param string $prefix (optional)
+     * @return string
+     */
+    public static function generate($prefix = '')
+    {
+        $characters = array_merge(
+            range('A', 'Z'),
+            range(0, 9)
+        );
+
+        $count = count($characters) - 1;
+
+        do {
+            $code = $prefix;
+
+            for ($i = 0; $i < self::CODE_LENGTH; $i++) {
+                $code .= $characters[random_int(0, $count)];
+            }
+        } while (Handler::existsCode($code));
+
+        return $code;
+    }
+}
diff --git a/src/QUI/ERP/Coupons/CodeGenerators/Uuid.php b/src/QUI/ERP/Coupons/CodeGenerators/Uuid.php
new file mode 100644
index 0000000000000000000000000000000000000000..e1b43b5cb8ce48d0c3559d4cf71faff9d2385216
--- /dev/null
+++ b/src/QUI/ERP/Coupons/CodeGenerators/Uuid.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace QUI\ERP\Coupons\CodeGenerators;
+
+use QUI\InviteCode\CodeGeneratorInterface;
+use Ramsey\Uuid\Uuid as UuidCreator;
+
+class Uuid implements CodeGeneratorInterface
+{
+    /**
+     * Generate a random, unique Invite code
+     *
+     * @param string $prefix (optional)
+     * @return string
+     */
+    public static function generate($prefix = '')
+    {
+        return $prefix . UuidCreator::uuid1()->toString();
+    }
+}
diff --git a/src/QUI/ERP/Coupons/CouponCode.php b/src/QUI/ERP/Coupons/CouponCode.php
index 4296cb05d59e916f345b5f641db36f3da5e5b581..14ac345c81ef04f38db65d50892b4a16481ff11f 100644
--- a/src/QUI/ERP/Coupons/CouponCode.php
+++ b/src/QUI/ERP/Coupons/CouponCode.php
@@ -3,17 +3,15 @@
 namespace QUI\ERP\Coupons;
 
 use QUI;
-use QUI\InviteCode\Exception\InviteCodeException;
-use QUI\InviteCode\Exception\InviteCodeMailException;
 use QUI\Permissions\Permission;
 
 /**
- * Class InviteCode
+ * Class CouponCode
  */
 class CouponCode
 {
     /**
-     * InviteCode ID
+     * CouponCode ID
      *
      * @var int
      */
@@ -27,122 +25,111 @@ class CouponCode
     protected $code;
 
     /**
-     * User that is assigned to this Code
+     * IDs of users that this CouponCode is restricted to
      *
-     * @var QUI\Users\User
+     * @var array
      */
-    protected $User = null;
+    protected $userIds = [];
 
     /**
-     * Email address that is asasigned to this Code
+     * IDs of groups that this CouponCode is restricted to
      *
-     * @var string
+     * @var array
      */
-    protected $email = null;
+    protected $groupIds = [];
 
     /**
-     * Creation Date
+     * List of usages of this CouponCode
      *
-     * @var \DateTime
+     * @var array
      */
-    protected $CreateDate;
+    protected $usages = [];
 
     /**
-     * Use Date
+     * Creation Date
      *
      * @var \DateTime
      */
-    protected $UseDate = null;
+    protected $CreateDate;
 
     /**
-     * Date until the Invite code is valid
+     * Date until the CouponCode is valid
      *
      * @var \DateTime
      */
     protected $ValidUntilDate = null;
 
     /**
-     * InviteCode title
+     * CouponCode title
      *
      * @var string
      */
-    protected $title;
+    protected $title = null;
 
     /**
-     * Flag if mail has been sent
+     * Flag - Is the CouponCode valid?
      *
      * @var bool
      */
-    protected $mailSent;
+    protected $valid;
 
     /**
+     * Flag - Is the CouponCode reusable?
+     *
      * @var bool
      */
-    protected $valid = true;
+    protected $reusable;
 
     /**
-     * InviteCode constructor.
+     * CouponCode constructor.
      *
      * @param int $id - Invite Code ID
-     * @throws InviteCodeException
+     * @throws \QUI\ERP\Coupons\CouponCodeException
      */
     public function __construct($id)
     {
-        $result = QUI::getDataBase()->fetch(array(
+        $result = QUI::getDataBase()->fetch([
             'from'  => Handler::getTable(),
-            'where' => array(
+            'where' => [
                 'id' => $id
-            )
-        ));
+            ]
+        ]);
 
         if (empty($result)) {
-            throw new InviteCodeException(array(
-                'quiqqer/invitecode',
-                'exception.invitecode.not_found',
-                array(
+            throw new CouponCodeException([
+                'quiqqer/coupons',
+                'exception.CouponCode.not_found',
+                [
                     'id' => $id
-                )
-            ), 404);
+                ]
+            ], 404);
         }
 
         $data = current($result);
 
-        $this->id       = (int)$data['id'];
-        $this->code     = $data['code'];
-        $this->title    = $data['title'];
-        $this->mailSent = boolval($data['mailSent']);
-
-        if (!empty($data['userId'])) {
-            try {
-                $this->User = QUI::getUsers()->get($data['userId']);
-            } catch (\Exception $Exception) {
-//                QUI\System\Log::addWarning(
-//                    'Could not find User #' . $data['userId'] . ' for Invite Code #' . $this->id . '.'
-//                );
-//
-//                QUI\System\Log::writeException($Exception);
-            }
-        }
+        $this->id    = (int)$data['id'];
+        $this->code  = $data['code'];
+        $this->title = $data['title'];
 
-        if (!empty($data['email'])) {
-            $this->email = $data['email'];
+        if (!empty($data['usages'])) {
+            $this->usages = json_decode($data['usages'], true);
         }
 
-        $this->CreateDate = new \DateTime($data['createDate']);
+        if (!empty($data['userIds'])) {
+            $this->userIds = json_decode($data['userIds'], true);
+        }
 
-        if (!empty($data['useDate'])) {
-            $this->UseDate = new \DateTime($data['useDate']);
+        if (!empty($data['groupIds'])) {
+            $this->groupIds = json_decode($data['groupIds'], true);
         }
 
+        $this->CreateDate = new \DateTime($data['createDate']);
+
         if (!empty($data['validUntilDate'])) {
             $this->ValidUntilDate = new \DateTime($data['validUntilDate']);
-
-            $Now = new \DateTime();
-
-            if (!$this->isRedeemed() && $Now > $this->ValidUntilDate) {
-                $this->valid = false;
-            }
         }
+
+        $this->checkValidity();
     }
 
     /**
@@ -161,38 +148,6 @@ public function getCode()
         return $this->code;
     }
 
-    /**
-     * @return QUI\Users\User|null
-     */
-    public function getUser()
-    {
-        return $this->User;
-    }
-
-    /**
-     * @param QUI\Users\User $User
-     */
-    public function setUser(QUI\Users\User $User)
-    {
-        $this->User = $User;
-    }
-
-    /**
-     * @return string
-     */
-    public function getEmail()
-    {
-        return $this->email;
-    }
-
-    /**
-     * @param string $email
-     */
-    public function setEmail($email)
-    {
-        $this->email = $email;
-    }
-
     /**
      * @return \DateTime
      */
@@ -202,11 +157,13 @@ public function getCreateDate()
     }
 
     /**
-     * @return \DateTime|null
+     * Get usage data
+     *
+     * @return []
      */
-    public function getUseDate()
+    public function getUsages()
     {
-        return $this->UseDate;
+        return $this->usages;
     }
 
     /**
@@ -226,157 +183,147 @@ public function getTitle()
     }
 
     /**
-     * @return bool
+     * Redeems this CouponCode
+     *
+     * Hint: This may invalidate the code for future use
+     *
+     * @param QUI\Users\User $User - The user that redeems the CouponCode [if omitted use Session User]
+     * @return void
+     * @throws CouponCodeException
      */
-    public function isMailSent()
+    public function redeem($User = null)
     {
-        return $this->mailSent;
+        if (is_null($User)) {
+            $User = QUI::getUserBySession();
+        }
+
+        $this->checkRedemption($User);
+
+        $Now            = new \DateTime();
+        $this->usages[] = [
+            'userId' => $User->getId(),
+            'date'   => $Now->format('Y-m-d H:i:s')
+        ];
+
+        QUI::getDataBase()->update(
+            Handler::getTable(),
+            [
+                'usages' => json_encode($this->usages)
+            ],
+            [
+                'id' => $this->id
+            ]
+        );
+
+        $this->checkValidity();
     }
 
     /**
-     * Redeems this InviteCode
+     * Check if the given User can redeem this CouponCode
      *
-     * Hint: This invalidates the code for future use
-     *
-     * @param QUI\Users\User $User
+     * @param QUI\Users\User $User - If omitted, use session user
      * @return void
-     * @throws InviteCodeException
+     * @throws CouponCodeException - Thrown if not redeemable by the given User
      */
-    public function redeem($User)
+    protected function checkRedemption($User)
     {
-        if ($this->isRedeemed()) {
-            throw new InviteCodeException(array(
-                'quiqqer/invitecode',
-                'exception.invitecode.already_used'
-            ));
+        if (!$this->isValid()) {
+            throw new CouponCodeException([
+                'quiqqer/coupons',
+                'exception.CouponCode.no_longer_valid'
+            ]);
         }
 
-        if (!$this->isValid()) {
-            throw new InviteCodeException(array(
-                'quiqqer/invitecode',
-                'exception.invitecode.no_longer_valid'
-            ));
+        if (!empty($this->userIds)) {
+            if (in_array($User->getId(), $this->userIds)) {
+                if (!$this->reusable && $this->hasUserRedeemed($User)) {
+                    throw new CouponCodeException([
+                        'quiqqer/coupons',
+                        'exception.CouponCode.already_used'
+                    ]);
+                }
+            } else {
+                throw new CouponCodeException([
+                    'quiqqer/coupons',
+                    'exception.CouponCode.user_not_allowed'
+                ]);
+            }
         }
 
-        $Now = new \DateTime();
+        if (!empty($this->groupIds)) {
+            $userInGroup = false;
 
-        QUI::getDataBase()->update(
-            Handler::getTable(),
-            array(
-                'useDate' => $Now->format('Y-m-d H:i:s'),
-                'userId'  => $User->getUniqueId()
-            ),
-            array(
-                'id' => $this->id
-            )
-        );
+            foreach ($this->groupIds as $groupId) {
+                if ($User->isInGroup($groupId)) {
+                    $userInGroup = true;
+                    break;
+                }
+            }
 
-        $this->UseDate = $Now;
+            if ($userInGroup) {
+                if (!$this->reusable && $this->hasUserRedeemed($User)) {
+                    throw new CouponCodeException([
+                        'quiqqer/coupons',
+                        'exception.CouponCode.already_used'
+                    ]);
+                }
+            } else {
+                throw new CouponCodeException([
+                    'quiqqer/coupons',
+                    'exception.CouponCode.user_not_allowed_group'
+                ]);
+            }
+        }
     }
 
     /**
-     * Check if this InviteCode is still valid
+     * Check if the given User can redeem this CouponCode
      *
+     * @param QUI\Users\User $User - If omitted, use session user
      * @return bool
      */
-    public function isValid()
+    public function isRedeemable($User = null)
     {
-        return $this->valid;
+        try {
+            $this->checkRedemption($User);
+        } catch (CouponCodeException $Exception) {
+            return false;
+        }
+
+        return true;
     }
 
     /**
-     * Checks if an InviteCode is redeemed
+     * Check if this CouponCode is still valid
      *
      * @return bool
      */
-    public function isRedeemed()
+    public function isValid()
     {
-        return !is_null($this->getUseDate());
+        return $this->valid;
     }
 
     /**
-     * Send this Invite Code via E-Mail
+     * Checks if an CouponCode has been redeemed by a user
      *
-     * @param bool $resend (optional) - Send again if already send [default: false]
-     * @return void
-     * @throws InviteCodeMailException
+     * @param QUI\Users\User $User
+     * @return bool
      */
-    public function sendViaMail($resend = false)
+    public function hasUserRedeemed($User)
     {
-        if (!$resend && $this->isMailSent()) {
-            return;
-        }
+        $userId = $User->getId();
 
-        $email = $this->getEmail();
-
-        if (empty($email)) {
-            throw new InviteCodeMailException(array(
-                'quiqqer/invitecode',
-                'exception.invitecode.no_email'
-            ));
-        }
-
-        $Mailer = new QUI\Mail\Mailer();
-
-        $Mailer->addRecipient($email);
-
-        $Engine         = QUI::getTemplateManager()->getEngine();
-        $dir            = QUI::getPackage('quiqqer/invitecode')->getDir() . 'templates/';
-        $validUntilDate = $this->getValidUntilDate();
-        $data           = array(
-            'code'           => $this->getCode(),
-            'validUntilDate' => empty($validUntilDate) ? '' : QUI::getLocale()->formatDate($validUntilDate->getTimestamp())
-        );
-
-        $RegistrationSite = Handler::getRegistrationSite();
-
-        if (empty($RegistrationSite)) {
-            throw new InviteCodeMailException(array(
-                'quiqqer/invitecode',
-                'exception.invitecode.no_registration_site'
-            ));
-        }
-
-        $data['registrationUrl'] = $RegistrationSite->getUrlRewrittenWithHost();
-        $data['email']           = $email;
-        $data['inviteCode']      = $this->getCode();
-
-        if (empty($validUntilDate)) {
-            $translationVar = 'mail.invite_code.body';
-        } else {
-            $translationVar = 'mail.invite_code.body_date';
+        foreach ($this->usages as $usage) {
+            if ($usage['userId'] === $userId) {
+                return true;
+            }
         }
 
-        $Engine->assign(array(
-            'body' => QUI::getLocale()->get(
-                'quiqqer/invitecode',
-                $translationVar,
-                $data
-            )
-        ));
-
-        $Mailer->setSubject(QUI::getLocale()->get(
-            'quiqqer/invitecode',
-            'mail.invite_code.subject'
-        ));
-
-        $Mailer->setBody($Engine->fetch($dir . 'mail.invite_code.html'));
-        $Mailer->send();
-
-        // update internal flag
-        QUI::getDataBase()->update(
-            Handler::getTable(),
-            array(
-                'mailSent' => 1
-            ),
-            array(
-                'id' => $this->getId()
-            )
-        );
+        return false;
     }
 
     /**
-     * Permanently delete this InviteCode
+     * Permanently delete this CouponCode
      *
      * @return void
      */
@@ -386,52 +333,82 @@ public function delete()
 
         QUI::getDataBase()->delete(
             Handler::getTable(),
-            array(
+            [
                 'id' => $this->id
-            )
+            ]
         );
     }
 
     /**
-     * Get InviteCode attributes as array
+     * Get CouponCode attributes as array
      *
      * @return array
      */
     public function toArray()
     {
-        $data = array(
+        $data = [
             'id'             => $this->getId(),
             'code'           => $this->getCode(),
-            'userId'         => false,
-            'username'       => false,
-            'email'          => $this->getEmail() ?: false,
+            'userIds'        => $this->userIds,
+            'groupIds'       => $this->groupIds,
             'createDate'     => $this->getCreateDate()->format('Y-m-d H:i:s'),
-            'useDate'        => false,
+            'usages'         => $this->usages,
             'validUntilDate' => false,
             'title'          => $this->getTitle() ?: false,
-            'mailSent'       => $this->isMailSent(),
-            'valid'          => $this->isValid()
-        );
+            'isValid'        => $this->isValid(),
+            'isReusable'     => $this->reusable
+        ];
 
-        $User = $this->getUser();
+        $ValidUntilDate = $this->getValidUntilDate();
 
-        if ($User) {
-            $data['userId']   = $User->getId();
-            $data['username'] = $User->getName();
+        if ($ValidUntilDate) {
+            $data['validUntilDate'] = $ValidUntilDate->format('Y-m-d H:i:s');
         }
 
-        $UseDate = $this->getUseDate();
+        return $data;
+    }
 
-        if ($UseDate) {
-            $data['useDate'] = $UseDate->format('Y-m-d H:i:s');
-        }
+    /**
+     * Checks if this CouponCode is still valid
+     *
+     * @return void
+     */
+    protected function checkValidity()
+    {
+        // Check if the expiration date has been reached
+        if (!empty($this->ValidUntilDate)) {
+            $Now = new \DateTime();
 
-        $ValidUntilDate = $this->getValidUntilDate();
+            if ($Now > $this->ValidUntilDate) {
+                $this->valid = false;
+                return;
+            }
+        }
 
-        if ($ValidUntilDate) {
-            $data['validUntilDate'] = $ValidUntilDate->format('Y-m-d H:i:s');
+        if ($this->reusable) {
+            return;
         }
 
-        return $data;
+        // If the CouponCode is restricted to certain users -> Check if all those
+        // users have already redeemed the code
+        if (!empty($this->userIds)) {
+            $usedByAllUsers = true;
+
+            foreach ($this->userIds as $userId) {
+                foreach ($this->usages as $usage) {
+                    if ($userId == $usage['userId']) {
+                        continue 2;
+                    }
+                }
+
+                $usedByAllUsers = false;
+                break;
+            }
+
+            if ($usedByAllUsers) {
+                $this->valid = false;
+                return;
+            }
+        }
     }
 }
diff --git a/src/QUI/ERP/Coupons/CouponCodeException.php b/src/QUI/ERP/Coupons/CouponCodeException.php
new file mode 100644
index 0000000000000000000000000000000000000000..818fb11e15af38de073e71ce153e03f69a25b3bf
--- /dev/null
+++ b/src/QUI/ERP/Coupons/CouponCodeException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace QUI\ERP\Coupons;
+
+use QUI;
+
+class CouponCodeException extends QUI\Exception
+{
+
+}
diff --git a/src/QUI/ERP/Coupons/Handler.php b/src/QUI/ERP/Coupons/Handler.php
index e2b97662641239071157e8a99018c4a73717cffa..405d99b3d7739ce74375de8ec2d9978106945530 100644
--- a/src/QUI/ERP/Coupons/Handler.php
+++ b/src/QUI/ERP/Coupons/Handler.php
@@ -5,7 +5,6 @@
 use QUI;
 use QUI\Utils\Grid;
 use QUI\Utils\Security\Orthos;
-use QUI\InviteCode\Exception\InviteCodeException;
 
 /**
  * Class Handler
@@ -17,44 +16,44 @@ class Handler
     /**
      * Permissions
      */
-    const PERMISSION_VIEW      = 'quiqqer.invitecode.view';
-    const PERMISSION_CREATE    = 'quiqqer.invitecode.create';
-    const PERMISSION_SEND_MAIL = 'quiqqer.invitecode.send_mail';
-    const PERMISSION_DELETE    = 'quiqqer.invitecode.delete';
+    const PERMISSION_VIEW      = 'quiqqer.couponcode.view';
+    const PERMISSION_CREATE    = 'quiqqer.couponcode.create';
+    const PERMISSION_DELETE    = 'quiqqer.couponcode.delete';
 
     /**
-     * InviteCode runtime cache
+     * CouponCode runtime cache
      *
-     * @var InviteCode[]
+     * @var CouponCode[]
      */
-    protected static $inviteCodes = array();
+    protected static $couponCodes = array();
 
     /**
-     * Get InviteCode
+     * Get CouponCode
      *
      * @param int $id
-     * @return InviteCode
+     * @return CouponCode
+     * @throws CouponCodeException
      */
-    public static function getInviteCode($id)
+    public static function getCouponCode($id)
     {
-        if (isset(self::$inviteCodes[$id])) {
-            return self::$inviteCodes[$id];
+        if (isset(self::$couponCodes[$id])) {
+            return self::$couponCodes[$id];
         }
 
-        self::$inviteCodes[$id] = new InviteCode($id);
+        self::$couponCodes[$id] = new CouponCode($id);
 
-        return self::$inviteCodes[$id];
+        return self::$couponCodes[$id];
     }
 
     /**
-     * Get InviteCode by its actual code
+     * Get CouponCode by its actual code
      *
      * @param string $code
-     * @return InviteCode
+     * @return CouponCode
      *
-     * @throws InviteCodeException
+     * @throws CouponCodeException
      */
-    public static function getInviteCodeByCode($code)
+    public static function getCouponCodeByCode($code)
     {
         $result = QUI::getDataBase()->fetch(array(
             'select' => array(
@@ -68,27 +67,27 @@ public static function getInviteCodeByCode($code)
         ));
 
         if (empty($result)) {
-            throw new InviteCodeException(array(
+            throw new CouponCodeException(array(
                 'quiqqer/invitecode',
-                'exception.handler.code_not_found',
+                'exception.Handler.code_not_found',
                 array(
                     'code' => $code
                 )
             ), 404);
         }
 
-        return self::getInviteCode($result[0]['id']);
+        return self::getCouponCode($result[0]['id']);
     }
 
     /**
-     * Create new InviteCode
+     * Create new CouponCode
      *
      * @param array $data
-     * @return InviteCode
+     * @return CouponCode
      *
      * @throws \Exception
      */
-    public static function createInviteCode($data)
+    public static function createCouponCode($data)
     {
         $Now = new \DateTime();
 
@@ -107,9 +106,9 @@ public static function createInviteCode($data)
             try {
                 QUI::getUsers()->getUserByMail($data['email']);
 
-                throw new InviteCodeException(array(
+                throw new CouponCodeException(array(
                     'quiqqer/invitecode',
-                    'exception.handler.user_already_exists',
+                    'exception.Handler.user_already_exists',
                     array(
                         'email' => $data['email']
                     )
@@ -128,19 +127,19 @@ public static function createInviteCode($data)
             $inviteCode
         );
 
-        return self::getInviteCode(QUI::getPDO()->lastInsertId());
+        return self::getCouponCode(QUI::getPDO()->lastInsertId());
     }
 
     /**
-     * Search InviteCodes
+     * Search CouponCodes
      *
      * @param array $searchParams
      * @param bool $countOnly (optional) - get result count only [default: false]
-     * @return InviteCode[]|int
+     * @return CouponCode[]|int
      */
     public static function search($searchParams, $countOnly = false)
     {
-        $inviteCodes = array();
+        $couponCodes = array();
         $Grid        = new Grid($searchParams);
         $gridParams  = $Grid->parseDBParams($searchParams);
 
@@ -236,10 +235,10 @@ public static function search($searchParams, $countOnly = false)
         }
 
         foreach ($result as $row) {
-            $inviteCodes[] = self::getInviteCode($row['id']);
+            $couponCodes[] = self::getCouponCode($row['id']);
         }
 
-        return $inviteCodes;
+        return $couponCodes;
     }
 
     /**
@@ -284,14 +283,14 @@ public static function getRegistrationSite()
     }
 
     /**
-     * Deletes all InviteCodes that are expired
+     * Deletes all CouponCodes that are expired
      *
      * @param int $days (optional) - Delete expired Codes that are older than X days [default: delete all]
      * @return void
      *
      * @throws \Exception
      */
-    public static function deleteExpiredInviteCodes($days = null)
+    public static function deleteExpiredCouponCodes($days = null)
     {
         $Now   = new \DateTime();
         $where = array(
@@ -319,14 +318,14 @@ public static function deleteExpiredInviteCodes($days = null)
     }
 
     /**
-     * Deletes all InviteCodes that have been redeemed
+     * Deletes all CouponCodes that have been redeemed
      *
      * @param int $days (optional) - Delete redeemed Codes that are older than X days [default: delete all]
      * @return void
      *
      * @throws \Exception
      */
-    public static function deleteRedeemedInviteCodes($days = null)
+    public static function deleteRedeemedCouponCodes($days = null)
     {
         $where = array(
             'useDate' => array(
@@ -353,12 +352,12 @@ public static function deleteRedeemedInviteCodes($days = null)
     }
 
     /**
-     * Get InviteCode table
+     * Get CouponCode table
      *
      * @return string
      */
     public static function getTable()
     {
-        return 'quiqqer_invitecodes';
+        return QUI::getDBTableName('quiqqer_coupons');
     }
 }