Files
octopus/myproject/discounts/migrations/0001_initial.py
Andrey Smakotin 241625eba7 feat(discounts): добавлено приложение скидок
Создано новое Django приложение для управления скидками:

Модели:
- BaseDiscount: абстрактный базовый класс с общими полями
- Discount: основная модель скидки (процент/фикс, на заказ/товар/категорию)
- PromoCode: промокоды для активации скидок
- DiscountApplication: история применения скидок

Сервисы:
- DiscountCalculator: расчёт скидок для корзины и заказов
- DiscountApplier: применение скидок к заказам (атомарно)
- DiscountValidator: валидация промокодов и условий

Админ-панель:
- DiscountAdmin: управление скидками
- PromoCodeAdmin: управление промокодами
- DiscountApplicationAdmin: история применения (только чтение)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 00:30:14 +03:00

133 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Generated by Django 5.0.10 on 2026-01-10 21:07
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('accounts', '0002_remove_customuser_first_name_and_more'),
('customers', '0002_initial'),
('orders', '0002_initial'),
('products', '0002_alter_configurableproduct_archived_by_and_more'),
]
operations = [
migrations.CreateModel(
name='Discount',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название скидки')),
('description', models.TextField(blank=True, verbose_name='Описание')),
('discount_type', models.CharField(choices=[('percentage', 'Процент'), ('fixed_amount', 'Фиксированная сумма')], max_length=20, verbose_name='Тип скидки')),
('value', models.DecimalField(decimal_places=2, help_text='Процент (0-100) или сумма в рублях', max_digits=10, verbose_name='Значение')),
('scope', models.CharField(choices=[('order', 'На весь заказ'), ('product', 'На товар'), ('category', 'На категорию товаров')], default='order', max_length=20, verbose_name='Уровень применения')),
('is_active', models.BooleanField(db_index=True, default=True, verbose_name='Активна')),
('start_date', models.DateTimeField(blank=True, null=True, verbose_name='Дата начала действия')),
('end_date', models.DateTimeField(blank=True, null=True, verbose_name='Дата окончания действия')),
('max_usage_count', models.PositiveIntegerField(blank=True, help_text='Оставьте пустым для безлимитного использования', null=True, verbose_name='Макс. количество использований')),
('current_usage_count', models.PositiveIntegerField(default=0, verbose_name='Текущее количество использований')),
('priority', models.PositiveIntegerField(default=0, help_text='Более высокий приоритет применяется первым', verbose_name='Приоритет')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('min_order_amount', models.DecimalField(blank=True, decimal_places=2, help_text='Скидка применяется только если сумма заказа >= этого значения', max_digits=10, null=True, verbose_name='Мин. сумма заказа')),
('is_auto', models.BooleanField(default=False, help_text='Применяется автоматически при выполнении условий', verbose_name='Автоматическая')),
('categories', models.ManyToManyField(blank=True, related_name='discounts', to='products.productcategory', verbose_name='Категории')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_discounts', to='accounts.customuser', verbose_name='Создал')),
('excluded_products', models.ManyToManyField(blank=True, related_name='excluded_from_discounts', to='products.product', verbose_name='Исключенные товары')),
('products', models.ManyToManyField(blank=True, related_name='discounts', to='products.product', verbose_name='Товары')),
],
options={
'verbose_name': 'Скидка',
'verbose_name_plural': 'Скидки',
},
),
migrations.CreateModel(
name='PromoCode',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(help_text='Уникальный код (например: SALE2025, WINTER10)', max_length=50, unique=True, verbose_name='Код промокода')),
('max_uses_per_user', models.PositiveIntegerField(blank=True, help_text='Оставьте пустым для безлимитного использования', null=True, verbose_name='Макс. использований на клиента')),
('max_total_uses', models.PositiveIntegerField(blank=True, null=True, verbose_name='Макс. общее количество использований')),
('current_uses', models.PositiveIntegerField(default=0, verbose_name='Текущее количество использований')),
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
('start_date', models.DateTimeField(blank=True, null=True, verbose_name='Дата начала действия')),
('end_date', models.DateTimeField(blank=True, null=True, verbose_name='Дата окончания действия')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_promo_codes', to='accounts.customuser', verbose_name='Создал')),
('discount', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='promo_codes', to='discounts.discount', verbose_name='Скидка')),
],
options={
'verbose_name': 'Промокод',
'verbose_name_plural': 'Промокоды',
},
),
migrations.CreateModel(
name='DiscountApplication',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('target', models.CharField(choices=[('order', 'Заказ'), ('order_item', 'Позиция заказа')], max_length=20, verbose_name='Объект применения')),
('base_amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Базовая сумма')),
('discount_amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Сумма скидки')),
('final_amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Итоговая сумма')),
('applied_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата применения')),
('applied_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='applied_discounts', to='accounts.customuser', verbose_name='Применен пользователем')),
('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='discount_applications', to='customers.customer', verbose_name='Клиент')),
('discount', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='applications', to='discounts.discount', verbose_name='Скидка')),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='discount_applications', to='orders.order', verbose_name='Заказ')),
('order_item', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='discount_applications', to='orders.orderitem', verbose_name='Позиция заказа')),
('promo_code', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='applications', to='discounts.promocode', verbose_name='Промокод')),
],
options={
'verbose_name': 'Применение скидки',
'verbose_name_plural': 'Применения скидок',
},
),
migrations.AddIndex(
model_name='discount',
index=models.Index(fields=['is_active'], name='discounts_d_is_acti_ae32b7_idx'),
),
migrations.AddIndex(
model_name='discount',
index=models.Index(fields=['scope'], name='discounts_d_scope_2c30a7_idx'),
),
migrations.AddIndex(
model_name='discount',
index=models.Index(fields=['discount_type'], name='discounts_d_discoun_f47d7f_idx'),
),
migrations.AddIndex(
model_name='discount',
index=models.Index(fields=['is_auto'], name='discounts_d_is_auto_a4fe48_idx'),
),
migrations.AddIndex(
model_name='promocode',
index=models.Index(fields=['code'], name='discounts_p_code_f0e5a6_idx'),
),
migrations.AddIndex(
model_name='promocode',
index=models.Index(fields=['is_active'], name='discounts_p_is_acti_25d05d_idx'),
),
migrations.AddIndex(
model_name='discountapplication',
index=models.Index(fields=['order'], name='discounts_d_order_i_2b0f24_idx'),
),
migrations.AddIndex(
model_name='discountapplication',
index=models.Index(fields=['discount'], name='discounts_d_discoun_c0cd4d_idx'),
),
migrations.AddIndex(
model_name='discountapplication',
index=models.Index(fields=['promo_code'], name='discounts_d_promo_c_9ce5dd_idx'),
),
migrations.AddIndex(
model_name='discountapplication',
index=models.Index(fields=['customer'], name='discounts_d_custome_d57e7c_idx'),
),
migrations.AddIndex(
model_name='discountapplication',
index=models.Index(fields=['applied_at'], name='discounts_d_applied_96adbb_idx'),
),
]