From 6e05e4a52b0b263c6172806629dc54b0539602c8 Mon Sep 17 00:00:00 2001 From: Aleksey Filippov Date: Fri, 20 Feb 2026 16:33:10 +0300 Subject: [PATCH] feat(BR-132): add alphanumeric promo code format PROMO-XXXX-XXXX 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 --- ...220_120000_alter_promocode_code_length.php | 19 +++++++++++ erp24/records/Promocode.php | 9 +++-- erp24/services/PromocodeService.php | 34 ++++++++++++++++--- erp24/views/promocode/edit.php | 4 +++ 4 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 erp24/migrations/m260220_120000_alter_promocode_code_length.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 index 00000000..b57d31bc --- /dev/null +++ b/erp24/migrations/m260220_120000_alter_promocode_code_length.php @@ -0,0 +1,19 @@ +alterColumn('{{%erp24.promocode}}', 'code', $this->string(20)->notNull()->comment('Промокод')); + } + + public function safeDown() + { + $this->alterColumn('{{%erp24.promocode}}', 'code', $this->string(13)->notNull()->comment('Промокод')); + } +} diff --git a/erp24/records/Promocode.php b/erp24/records/Promocode.php index 9e64a7ba..93a7b2fc 100644 --- a/erp24/records/Promocode.php +++ b/erp24/records/Promocode.php @@ -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]], ]; } diff --git a/erp24/services/PromocodeService.php b/erp24/services/PromocodeService.php index fa3fd618..5f536a46 100644 --- a/erp24/services/PromocodeService.php +++ b/erp24/services/PromocodeService.php @@ -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; diff --git a/erp24/views/promocode/edit.php b/erp24/views/promocode/edit.php index c85475dd..2fbbbeac 100644 --- a/erp24/views/promocode/edit.php +++ b/erp24/views/promocode/edit.php @@ -57,6 +57,10 @@ $this->registerJsFile('/js/xlsx.full.min.js', ['position' => \yii\web\View::POS_ base < 2): ?> field($model, 'generatePromocodeCount')->textInput(['type' => 'number'])->label(false)) ?> + field($model, 'generateFormat')->dropDownList([ + Promocode::FORMAT_DIGITS => 'Цифровой (CODE123)', + Promocode::FORMAT_ALPHANUMERIC => 'Буквенно-цифровой (CODE-XXXX-XXXX, без 0/O/1/l)', + ])->label(false)) ?>
-- 2.39.5