]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
feat(BR-132): add alphanumeric promo code format PROMO-XXXX-XXXX
authorAleksey Filippov <Aleksey.Filippov@erp-flowers.ru>
Fri, 20 Feb 2026 13:33:10 +0000 (16:33 +0300)
committerAleksey Filippov <Aleksey.Filippov@erp-flowers.ru>
Fri, 20 Feb 2026 13:33:10 +0000 (16:33 +0300)
Add format switcher for promo code generation: legacy digits (CODE123)
and new alphanumeric (CODE-XXXX-XXXX) without confusing chars (0/O/1/l/I).
Expand code column from VARCHAR(13) to VARCHAR(20).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
erp24/migrations/m260220_120000_alter_promocode_code_length.php [new file with mode: 0644]
erp24/records/Promocode.php
erp24/services/PromocodeService.php
erp24/views/promocode/edit.php

diff --git a/erp24/migrations/m260220_120000_alter_promocode_code_length.php b/erp24/migrations/m260220_120000_alter_promocode_code_length.php
new file mode 100644 (file)
index 0000000..b57d31b
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+
+use yii\db\Migration;
+
+/**
+ * BR-132: Расширение поля code до 20 символов для формата PROMO-XXXX-XXXX.
+ */
+class m260220_120000_alter_promocode_code_length extends Migration
+{
+    public function safeUp()
+    {
+        $this->alterColumn('{{%erp24.promocode}}', 'code', $this->string(20)->notNull()->comment('Промокод'));
+    }
+
+    public function safeDown()
+    {
+        $this->alterColumn('{{%erp24.promocode}}', 'code', $this->string(13)->notNull()->comment('Промокод'));
+    }
+}
index 9e64a7ba2f3d2f22a4f88eedb4d43db2eda2a620..93a7b2fc903c6cb0238ef2268dc806897071d886 100644 (file)
@@ -32,17 +32,22 @@ class Promocode extends \yii\db\ActiveRecord
     const BASE_BASE = 1;
     const BASE_SINGLE_USE = 2;
 
+    const FORMAT_DIGITS = 'digits';
+    const FORMAT_ALPHANUMERIC = 'alphanumeric';
+
     public $generatePromocodeCount;
+    public $generateFormat = self::FORMAT_DIGITS;
 
     public static function tableName() { return 'promocode'; }
 
     public function rules() {
         return [
             [['code', 'bonus', 'duration', 'active', 'date_start', 'date_end', 'created_by', 'created_at'], 'required'],
-            [['code'], 'string', 'max' => 13],
+            [['code'], 'string', 'max' => 20],
             [['bonus', 'duration', 'active', 'used', 'base', 'parent_id', 'created_by', 'updated_by'], 'integer'],
             [['date_start', 'date_end', 'created_at', 'updated_at'], 'datetime', 'format' => 'php:Y-m-d H:i:s'],
-            [['updated_by', 'updated_at', 'generatePromocodeCount'], 'safe'],
+            [['updated_by', 'updated_at', 'generatePromocodeCount', 'generateFormat'], 'safe'],
+            ['generateFormat', 'in', 'range' => [self::FORMAT_DIGITS, self::FORMAT_ALPHANUMERIC]],
         ];
     }
 
index fa3fd61810dc09b28aae6c9657340717d29a9d44..5f536a463b6a28288c651e302f51fedbe5c3f845 100644 (file)
@@ -7,18 +7,44 @@ use yii_app\records\Promocode;
 
 class PromocodeService
 {
+    /** Без 0, O, 1, l, I — исключают путаницу при ручном вводе */
+    private const SAFE_CHARSET = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
+
     private static function generateThreeNums() {
         return (rand() % 10) . (rand() % 10) . (rand() % 10);
     }
 
+    private static function generateAlphanumericGroup(int $length = 4): string
+    {
+        $charset = self::SAFE_CHARSET;
+        $max = strlen($charset) - 1;
+        $group = '';
+        for ($i = 0; $i < $length; $i++) {
+            $group .= $charset[random_int(0, $max)];
+        }
+        return $group;
+    }
+
+    private static function generateSuffix(string $format): string
+    {
+        if ($format === Promocode::FORMAT_ALPHANUMERIC) {
+            return '-' . self::generateAlphanumericGroup(4) . '-' . self::generateAlphanumericGroup(4);
+        }
+        return self::generateThreeNums();
+    }
+
     public static function generateSingleUsePromocodes(Promocode $basePromocode) {
+        $format = $basePromocode->generateFormat ?: Promocode::FORMAT_DIGITS;
+
         foreach (range(1, $basePromocode->generatePromocodeCount) as $num) {
-            $word = self::generateThreeNums();
-            while (Promocode::find()->where(['code' => $basePromocode->code . $word])->one()) {
-                $word = self::generateThreeNums();
+            $suffix = self::generateSuffix($format);
+            $code = mb_strtoupper($basePromocode->code . $suffix, 'UTF-8');
+            while (Promocode::find()->where(['code' => $code])->exists()) {
+                $suffix = self::generateSuffix($format);
+                $code = mb_strtoupper($basePromocode->code . $suffix, 'UTF-8');
             }
             $singleUsePromocode = new Promocode;
-            $singleUsePromocode->code = mb_strtoupper($basePromocode->code . $word, 'UTF-8');
+            $singleUsePromocode->code = $code;
             $singleUsePromocode->bonus = $basePromocode->bonus;
             $singleUsePromocode->duration = $basePromocode->duration;
             $singleUsePromocode->active = $basePromocode->active;
index c85475dda6a5961b6cd59903ebce0e8ac4d9ff97..2fbbbeac38baba27dadc42f1cef965e27fe6302e 100644 (file)
@@ -57,6 +57,10 @@ $this->registerJsFile('/js/xlsx.full.min.js', ['position' => \yii\web\View::POS_
 
     <?php if ($model->base < 2): ?>
         <?php PrintBlockHelper::printBlock('Количество дополнительных одноразовых промокодов', $form->field($model, 'generatePromocodeCount')->textInput(['type' => 'number'])->label(false)) ?>
+        <?php PrintBlockHelper::printBlock('Формат генерации кодов', $form->field($model, 'generateFormat')->dropDownList([
+            Promocode::FORMAT_DIGITS => 'Цифровой (CODE123)',
+            Promocode::FORMAT_ALPHANUMERIC => 'Буквенно-цифровой (CODE-XXXX-XXXX, без 0/O/1/l)',
+        ])->label(false)) ?>
     <?php endif; ?>
 
     <div class="form-group">