From e17b4a89c60c08ff7070873b350534b60bacead3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Patrick=20M=C3=BCller?= <p.mueller@pcsg.de>
Date: Fri, 2 Nov 2018 11:21:08 +0100
Subject: [PATCH] fix: Restrictions to group (label) #5 refactor: reusable
 status changed to maxUsages with 3 different options

---
 bin/backend/controls/Manager.Create.html | 14 ++++---
 bin/backend/controls/Manager.css         |  3 +-
 bin/backend/controls/Manager.js          | 35 +++++++----------
 database.xml                             |  2 +-
 locale.xml                               | 36 ++++++++++++++---
 src/QUI/ERP/Coupons/CouponCode.php       | 49 +++++++++++++++++++-----
 src/QUI/ERP/Coupons/Handler.php          | 27 +++++++++++--
 7 files changed, 118 insertions(+), 48 deletions(-)

diff --git a/bin/backend/controls/Manager.Create.html b/bin/backend/controls/Manager.Create.html
index 23c82ac..54ba00f 100644
--- a/bin/backend/controls/Manager.Create.html
+++ b/bin/backend/controls/Manager.Create.html
@@ -15,16 +15,20 @@
                 <input type="text" name="code"/>
             </label>
             <label>
-                <span>{{labelUsers}}</span>
-                <input type="hidden" name="userIds" data-qui="controls/users/Select"/>
+                <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="groupIds" data-qui="controls/groups/Select"/>
+                <input type="hidden" name="userIds" data-qui="controls/users/Select"/>
             </label>
             <label>
-                <span>{{labelReusable}}</span>
-                <input type="checkbox" name="reusable"/>
+                <span>{{labelGroups}}</span>
+                <input type="hidden" name="groupIds" data-qui="controls/groups/Select"/>
             </label>
             <label>
                 <span>{{labelDate}}</span>
diff --git a/bin/backend/controls/Manager.css b/bin/backend/controls/Manager.css
index e085853..7399dd2 100644
--- a/bin/backend/controls/Manager.css
+++ b/bin/backend/controls/Manager.css
@@ -36,7 +36,8 @@
 
 .quiqqer-coupons-manager-create input[type="text"],
 .quiqqer-coupons-manager-create input[type="number"],
-.quiqqer-coupons-manager-create input[type="date"] {
+.quiqqer-coupons-manager-create input[type="date"],
+.quiqqer-coupons-manager-create select {
     width: 100%;
 }
 
diff --git a/bin/backend/controls/Manager.js b/bin/backend/controls/Manager.js
index 0ffa025..13d3c8c 100644
--- a/bin/backend/controls/Manager.js
+++ b/bin/backend/controls/Manager.js
@@ -183,8 +183,8 @@ define('package/quiqqer/coupons/bin/backend/controls/Manager', [
                     width    : 150
                 }, {
                     header   : QUILocale.get(lg, 'controls.manager.tbl.header.reusable'),
-                    dataIndex: 'reusableStatus',
-                    dataType : 'node',
+                    dataIndex: 'maxUsageLabel',
+                    dataType : 'string',
                     width    : 150
                 }, {
                     header   : QUILocale.get(lg, 'controls.manager.tbl.header.createDate'),
@@ -319,17 +319,7 @@ define('package/quiqqer/coupons/bin/backend/controls/Manager', [
                     Row.title = '-';
                 }
 
-                var ReusableElm = new Element('span', {
-                    'class': 'fa'
-                });
-
-                if (!Row.reusable) {
-                    ReusableElm.addClass('fa-close');
-                } else {
-                    ReusableElm.addClass('fa-check');
-                }
-
-                Row.reusableStatus = ReusableElm;
+                Row.maxUsageLabel = QUILocale.get(lg, 'controls.manager.tbl.maxUsageLabel.' + Row.maxUsages);
             }
 
             this.$Grid.setData(GridData);
@@ -472,14 +462,17 @@ define('package/quiqqer/coupons/bin/backend/controls/Manager', [
                 },
                 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'),
-                    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')
                 })
             });
 
diff --git a/database.xml b/database.xml
index c2ca7ec..7d721f3 100644
--- a/database.xml
+++ b/database.xml
@@ -12,7 +12,7 @@
             <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>
+            <field type="VARCHAR(255) NOT NULL">maxUsages</field>
         </table>
     </global>
 
diff --git a/locale.xml b/locale.xml
index f55c3de..0cf4f9d 100644
--- a/locale.xml
+++ b/locale.xml
@@ -178,8 +178,8 @@
             <en><![CDATA[Usage (optional)]]></en>
         </locale>
         <locale name="controls.manager.create.template.labelAmount">
-            <de><![CDATA[Anzahl]]></de>
-            <en><![CDATA[Amount]]></en>
+            <de><![CDATA[Anzahl generierte Gutschein-Codes]]></de>
+            <en><![CDATA[Amount of generated coupon codes]]></en>
         </locale>
         <locale name="controls.manager.create.template.labelUsers">
             <de><![CDATA[Auf folgende Benutzer beschränken (optional)]]></de>
@@ -194,8 +194,20 @@
             <en><![CDATA[Coupon code (optional - is generated automatically if left empty)]]></en>
         </locale>
         <locale name="controls.manager.create.template.labelReusable">
-            <de><![CDATA[Wiederverwendbar (Gutschein-Code kann mehrfach verwendet werden)]]></de>
-            <en><![CDATA[Reusable (Coupon code can be used multiple times)]]></en>
+            <de><![CDATA[Erlaubte Verwendung]]></de>
+            <en><![CDATA[Permitted usage]]></en>
+        </locale>
+        <locale name="controls.manager.create.template.labelReusableOncePerUser">
+            <de><![CDATA[Einmal pro Benutzer einlösbar]]></de>
+            <en><![CDATA[Redeemable once per user]]></en>
+        </locale>
+        <locale name="controls.manager.create.template.labelReusableOnce">
+            <de><![CDATA[Insgesamt nur einmal einlösbar]]></de>
+            <en><![CDATA[Only redeemable once in total]]></en>
+        </locale>
+        <locale name="controls.manager.create.template.labelReusableUnlimited">
+            <de><![CDATA[Unbegrenzt einlösbar]]></de>
+            <en><![CDATA[Unlimited redeemable]]></en>
         </locale>
         <locale name="controls.manager.create.template.labelDiscount">
             <de><![CDATA[Verknüpfter Rabatt]]></de>
@@ -274,8 +286,8 @@
             <en><![CDATA[Usage]]></en>
         </locale>
         <locale name="controls.manager.tbl.header.reusable">
-            <de><![CDATA[Mehrfach verwendbar]]></de>
-            <en><![CDATA[Reusable]]></en>
+            <de><![CDATA[Erlaubte Verwendung]]></de>
+            <en><![CDATA[Permitted usage]]></en>
         </locale>
         <locale name="controls.manager.tbl.status.unused">
             <de><![CDATA[nicht eingelöst]]></de>
@@ -297,6 +309,18 @@
             <de><![CDATA[Benutzer existiert nicht mehr]]></de>
             <en><![CDATA[User does not exist anymore]]></en>
         </locale>
+        <locale name="controls.manager.tbl.maxUsageLabel.oncePerUser">
+            <de><![CDATA[Einmal pro Benutzer]]></de>
+            <en><![CDATA[Once per user]]></en>
+        </locale>
+        <locale name="controls.manager.tbl.maxUsageLabel.once">
+            <de><![CDATA[Einmal einlösbar]]></de>
+            <en><![CDATA[Redeemable once]]></en>
+        </locale>
+        <locale name="controls.manager.tbl.maxUsageLabel.unlimited">
+            <de><![CDATA[Unbegrenzt]]></de>
+            <en><![CDATA[Unlimited]]></en>
+        </locale>
         <locale name="controls.manager.delete.popup.info">
             <de>
                 <![CDATA[Sind Sie sicher, dass Sie die folgenden Gutschein-Codes unwiderruflich löschen wollen?<br><br>[codes]]]></de>
diff --git a/src/QUI/ERP/Coupons/CouponCode.php b/src/QUI/ERP/Coupons/CouponCode.php
index aec25c2..2cd5030 100644
--- a/src/QUI/ERP/Coupons/CouponCode.php
+++ b/src/QUI/ERP/Coupons/CouponCode.php
@@ -82,11 +82,11 @@ class CouponCode
     protected $valid = true;
 
     /**
-     * Flag - Is the CouponCode reusable?
+     * Max usages
      *
-     * @var bool
+     * @var string
      */
-    protected $reusable;
+    protected $maxUsages = Handler::MAX_USAGE_ONCE_PER_USER;
 
     /**
      * CouponCode constructor.
@@ -131,8 +131,8 @@ public function __construct($id)
             $this->groupIds = json_decode($data['groupIds'], true);
         }
 
-        if (!empty($data['isReusable'])) {
-            $this->reusable = true;
+        if (!empty($data['maxUsages'])) {
+            $this->maxUsages = $data['maxUsages'];
         }
 
         if (!empty($data['discountIds'])) {
@@ -333,9 +333,32 @@ public function checkRedemption($User)
             }
         }
 
+        // Max usage restrictions
+        switch ($this->maxUsages) {
+            case Handler::MAX_USAGE_ONCE_PER_USER:
+                if ($this->hasUserRedeemed($User)) {
+                    throw new CouponCodeException([
+                        'quiqqer/coupons',
+                        'exception.CouponCode.already_used'
+                    ]);
+                }
+                break;
+
+            case Handler::MAX_USAGE_ONCE:
+                if (!empty($this->usages)) {
+                    throw new CouponCodeException([
+                        'quiqqer/coupons',
+                        'exception.CouponCode.already_used'
+                    ]);
+                }
+                break;
+        }
+
+        // Restriction to QUIQQER user(s)
         if (!empty($this->userIds)) {
             if (in_array($User->getId(), $this->userIds)) {
-                if (!$this->reusable && $this->hasUserRedeemed($User)) {
+                if ($this->maxUsages !== Handler::MAX_USAGE_UNLIMITED
+                    && $this->hasUserRedeemed($User)) {
                     throw new CouponCodeException([
                         'quiqqer/coupons',
                         'exception.CouponCode.already_used'
@@ -349,6 +372,7 @@ public function checkRedemption($User)
             }
         }
 
+        // Restriction to QUIQQER group(s)
         if (!empty($this->groupIds)) {
             $userInGroup = false;
 
@@ -360,7 +384,8 @@ public function checkRedemption($User)
             }
 
             if ($userInGroup) {
-                if (!$this->reusable && $this->hasUserRedeemed($User)) {
+                if ($this->maxUsages !== Handler::MAX_USAGE_UNLIMITED
+                    && $this->hasUserRedeemed($User)) {
                     throw new CouponCodeException([
                         'quiqqer/coupons',
                         'exception.CouponCode.already_used'
@@ -456,7 +481,7 @@ public function toArray()
             'validUntilDate' => false,
             'title'          => $this->getTitle() ?: false,
             'isValid'        => $this->isValid(),
-            'reusable'       => $this->reusable,
+            'maxUsages'      => $this->maxUsages,
             'discountIds'    => $this->discountIds
         ];
 
@@ -482,12 +507,16 @@ protected function checkValidity()
 
             if ($Now > $this->ValidUntilDate) {
                 $this->valid = false;
-
                 return;
             }
         }
 
-        if ($this->reusable) {
+        if ($this->maxUsages === Handler::MAX_USAGE_UNLIMITED) {
+            return;
+        }
+
+        if ($this->maxUsages === Handler::MAX_USAGE_ONCE && !empty($this->usages)) {
+            $this->valid = false;
             return;
         }
 
diff --git a/src/QUI/ERP/Coupons/Handler.php b/src/QUI/ERP/Coupons/Handler.php
index 621dfc4..b8ab7a4 100644
--- a/src/QUI/ERP/Coupons/Handler.php
+++ b/src/QUI/ERP/Coupons/Handler.php
@@ -17,11 +17,18 @@ class Handler
     /**
      * Permissions
      */
-    const PERMISSION_VIEW = 'quiqqer.couponcode.view';
+    const PERMISSION_VIEW   = 'quiqqer.couponcode.view';
     const PERMISSION_CREATE = 'quiqqer.couponcode.create';
-    const PERMISSION_EDIT = 'quiqqer.couponcode.edit';
+    const PERMISSION_EDIT   = 'quiqqer.couponcode.edit';
     const PERMISSION_DELETE = 'quiqqer.couponcode.delete';
 
+    /**
+     * Valur for `maxUsage`
+     */
+    const MAX_USAGE_ONCE_PER_USER = 'oncePerUser';
+    const MAX_USAGE_ONCE          = 'once';
+    const MAX_USAGE_UNLIMITED     = 'unlimited';
+
     /**
      * CouponCode runtime cache
      *
@@ -135,11 +142,17 @@ public static function createCouponCode($discountIds, $settings = [])
             $code = CodeGenerator::generate();
         }
 
+        if (empty($settings['maxUsages'])) {
+            $maxUsages = self::MAX_USAGE_ONCE_PER_USER;
+        } else {
+            $maxUsages = $settings['maxUsages'];
+        }
+
         $couponCode = [
             'title'       => empty($settings['title']) ? null : $settings['title'],
             'createDate'  => $Now->format('Y-m-d H:i:s'),
             'code'        => $code,
-            'isReusable'  => empty($settings['reusable']) ? 0 : 1,
+            'maxUsages'   => $maxUsages,
             'discountIds' => json_encode($discountIds)
         ];
 
@@ -226,11 +239,17 @@ public static function editCouponCode($id, $discountIds, $settings = [])
             $code = CodeGenerator::generate();
         }
 
+        if (empty($settings['maxUsages'])) {
+            $maxUsages = self::MAX_USAGE_ONCE_PER_USER;
+        } else {
+            $maxUsages = $settings['maxUsages'];
+        }
+
         $couponCode = [
             'title'       => empty($settings['title']) ? null : $settings['title'],
             'createDate'  => $Now->format('Y-m-d H:i:s'),
             'code'        => $code,
-            'isReusable'  => !empty($settings['reusable']) ? 1 : 0,
+            'maxUsages'   => $maxUsages,
             'discountIds' => json_encode($discountIds)
         ];
 
-- 
GitLab