Рефакторинг системы вариативных товаров и справочник атрибутов
Основные изменения: - Переименование ConfigurableKitProduct → ConfigurableProduct - Добавлена поддержка Product как варианта (не только ProductKit) - Создан справочник атрибутов (ProductAttribute, ProductAttributeValue) - CRUD для управления атрибутами с inline редактированием значений - Пересозданы миграции с нуля для всех приложений - Добавлена ссылка на атрибуты в навигацию 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# Generated by Django 5.0.10 on 2025-12-23 20:38
|
||||
# Generated by Django 5.0.10 on 2025-12-29 22:19
|
||||
|
||||
import django.db.models.deletion
|
||||
import phonenumber_field.modelfields
|
||||
import simple_history.models
|
||||
from decimal import Decimal
|
||||
from django.conf import settings
|
||||
@@ -65,9 +66,10 @@ class Migration(migrations.Migration):
|
||||
('total_amount', models.DecimalField(decimal_places=2, default=0, help_text='Общая сумма заказа', max_digits=10, verbose_name='Итоговая сумма заказа')),
|
||||
('amount_paid', models.DecimalField(decimal_places=2, default=0, help_text='Сумма, внесенная клиентом', max_digits=10, verbose_name='Оплачено')),
|
||||
('payment_status', models.CharField(choices=[('unpaid', 'Не оплачен'), ('partial', 'Частично оплачен'), ('paid', 'Оплачен полностью')], default='unpaid', help_text='Обновляется автоматически при добавлении платежей', max_length=20, verbose_name='Статус оплаты')),
|
||||
('customer_is_recipient', models.BooleanField(default=True, help_text='Если отмечено, данные получателя не требуются отдельно', verbose_name='Покупатель является получателем')),
|
||||
('is_anonymous', models.BooleanField(default=False, help_text='Не сообщать получателю имя отправителя', verbose_name='Анонимная доставка')),
|
||||
('special_instructions', models.TextField(blank=True, help_text='Комментарии и пожелания к заказу', null=True, verbose_name='Особые пожелания')),
|
||||
('needs_product_photo', models.BooleanField(default=False, help_text='Требуется фотография товара перед отправкой', verbose_name='Необходимо фото товара')),
|
||||
('needs_delivery_photo', models.BooleanField(default=False, help_text='Требуется фотография процесса вручения заказа', verbose_name='Необходимо фото вручения')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
|
||||
],
|
||||
@@ -140,7 +142,8 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(help_text='ФИО или название организации получателя', max_length=200, verbose_name='Имя получателя')),
|
||||
('phone', models.CharField(help_text='Контактный телефон для связи с получателем', max_length=20, verbose_name='Телефон получателя')),
|
||||
('phone', phonenumber_field.modelfields.PhoneNumberField(help_text='Контактный телефон для связи с получателем. Введите в любом формате, будет автоматически преобразован', max_length=128, region=None, verbose_name='Телефон получателя')),
|
||||
('notes', models.CharField(blank=True, help_text='Мессенджер, соцсеть или другая информация о получателе (необязательно)', max_length=200, null=True, verbose_name='Дополнительная информация')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
|
||||
],
|
||||
@@ -194,8 +197,8 @@ class Migration(migrations.Migration):
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('delivery_type', models.CharField(choices=[('courier', 'Доставка курьером'), ('pickup', 'Самовывоз')], db_index=True, default='courier', max_length=20, verbose_name='Способ доставки')),
|
||||
('delivery_date', models.DateField(help_text='Дата, когда должна быть выполнена доставка', verbose_name='Дата доставки')),
|
||||
('time_from', models.TimeField(help_text='Начальное время временного интервала доставки', verbose_name='Время доставки от')),
|
||||
('time_to', models.TimeField(help_text='Конечное время временного интервала доставки', verbose_name='Время доставки до')),
|
||||
('time_from', models.TimeField(blank=True, help_text='Начальное время временного интервала доставки (необязательно)', null=True, verbose_name='Время доставки от')),
|
||||
('time_to', models.TimeField(blank=True, help_text='Конечное время временного интервала доставки (необязательно)', null=True, verbose_name='Время доставки до')),
|
||||
('cost', models.DecimalField(decimal_places=2, default=0, help_text='Стоимость доставки в рублях. 0 для бесплатной доставки/самовывоза', max_digits=10, verbose_name='Стоимость доставки')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
|
||||
@@ -219,9 +222,10 @@ class Migration(migrations.Migration):
|
||||
('total_amount', models.DecimalField(decimal_places=2, default=0, help_text='Общая сумма заказа', max_digits=10, verbose_name='Итоговая сумма заказа')),
|
||||
('amount_paid', models.DecimalField(decimal_places=2, default=0, help_text='Сумма, внесенная клиентом', max_digits=10, verbose_name='Оплачено')),
|
||||
('payment_status', models.CharField(choices=[('unpaid', 'Не оплачен'), ('partial', 'Частично оплачен'), ('paid', 'Оплачен полностью')], default='unpaid', help_text='Обновляется автоматически при добавлении платежей', max_length=20, verbose_name='Статус оплаты')),
|
||||
('customer_is_recipient', models.BooleanField(default=True, help_text='Если отмечено, данные получателя не требуются отдельно', verbose_name='Покупатель является получателем')),
|
||||
('is_anonymous', models.BooleanField(default=False, help_text='Не сообщать получателю имя отправителя', verbose_name='Анонимная доставка')),
|
||||
('special_instructions', models.TextField(blank=True, help_text='Комментарии и пожелания к заказу', null=True, verbose_name='Особые пожелания')),
|
||||
('needs_product_photo', models.BooleanField(default=False, help_text='Требуется фотография товара перед отправкой', verbose_name='Необходимо фото товара')),
|
||||
('needs_delivery_photo', models.BooleanField(default=False, help_text='Требуется фотография процесса вручения заказа', verbose_name='Необходимо фото вручения')),
|
||||
('created_at', models.DateTimeField(blank=True, editable=False, verbose_name='Дата создания')),
|
||||
('updated_at', models.DateTimeField(blank=True, editable=False, verbose_name='Дата обновления')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.0.10 on 2025-12-23 20:38
|
||||
# Generated by Django 5.0.10 on 2025-12-29 22:19
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# Generated by Django 5.0.10 on 2025-12-24 10:51
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orders', '0002_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='historicalorder',
|
||||
name='customer_is_recipient',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='order',
|
||||
name='customer_is_recipient',
|
||||
),
|
||||
]
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 5.0.10 on 2025-12-24 15:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orders', '0003_remove_customer_is_recipient'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='delivery',
|
||||
name='time_from',
|
||||
field=models.TimeField(blank=True, help_text='Начальное время временного интервала доставки (необязательно)', null=True, verbose_name='Время доставки от'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='delivery',
|
||||
name='time_to',
|
||||
field=models.TimeField(blank=True, help_text='Конечное время временного интервала доставки (необязательно)', null=True, verbose_name='Время доставки до'),
|
||||
),
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
# Generated by Django 5.0.10 on 2025-12-24 21:48
|
||||
|
||||
import phonenumber_field.modelfields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orders', '0004_make_delivery_time_optional'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='recipient',
|
||||
name='additional_contact',
|
||||
field=models.CharField(blank=True, help_text='Мессенджер, соцсеть или другая контактная информация (необязательно)', max_length=200, null=True, verbose_name='Дополнительный контакт'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='recipient',
|
||||
name='phone',
|
||||
field=phonenumber_field.modelfields.PhoneNumberField(help_text='Контактный телефон для связи с получателем. Введите в любом формате, будет автоматически преобразован', max_length=128, region='BY', verbose_name='Телефон получателя'),
|
||||
),
|
||||
]
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 5.0.10 on 2025-12-24 21:52
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orders', '0005_recipient_additional_contact_alter_recipient_phone'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='recipient',
|
||||
old_name='additional_contact',
|
||||
new_name='notes',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='recipient',
|
||||
name='notes',
|
||||
field=models.CharField(blank=True, help_text='Мессенджер, соцсеть или другая информация о получателе (необязательно)', max_length=200, null=True, verbose_name='Дополнительная информация'),
|
||||
),
|
||||
]
|
||||
@@ -1,19 +0,0 @@
|
||||
# Generated by Django 5.0.10 on 2025-12-24 21:56
|
||||
|
||||
import phonenumber_field.modelfields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orders', '0006_rename_additional_contact_to_notes'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='recipient',
|
||||
name='phone',
|
||||
field=phonenumber_field.modelfields.PhoneNumberField(help_text='Контактный телефон для связи с получателем. Введите в любом формате, будет автоматически преобразован', max_length=128, region=None, verbose_name='Телефон получателя'),
|
||||
),
|
||||
]
|
||||
@@ -1,33 +0,0 @@
|
||||
# Generated by Django 5.0.10 on 2025-12-25 08:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orders', '0007_remove_region_from_recipient_phone'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='historicalorder',
|
||||
name='needs_delivery_photo',
|
||||
field=models.BooleanField(default=False, help_text='Требуется фотография процесса вручения заказа', verbose_name='Необходимо фото вручения'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalorder',
|
||||
name='needs_product_photo',
|
||||
field=models.BooleanField(default=False, help_text='Требуется фотография товара перед отправкой', verbose_name='Необходимо фото товара'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='needs_delivery_photo',
|
||||
field=models.BooleanField(default=False, help_text='Требуется фотография процесса вручения заказа', verbose_name='Необходимо фото вручения'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='needs_product_photo',
|
||||
field=models.BooleanField(default=False, help_text='Требуется фотография товара перед отправкой', verbose_name='Необходимо фото товара'),
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user