refactor: Заменить сущность Магазин (Shop) на Склад (Warehouse)

Упрощена логика системы путём замены отдельной сущности "Магазин"
на универсальную сущность "Склад", которая может использоваться
как точка самовывоза.

Изменения:
- Расширена модель Warehouse: добавлены адрес, контакты, флаг is_pickup_point
- Модель Order: поле pickup_shop заменено на pickup_warehouse
- Обновлены все формы, сервисы, views, admin для работы со складами
- Обновлены шаблоны HTML и JavaScript код
- Удалено приложение shops полностью
- Пересозданы миграции БД
- Обновлён навбар (удалена ссылка на магазины)

Преимущества:
- Упрощена архитектура системы
- Единая точка управления складами и точками самовывоза
- Интеграция с системой инвентаризации

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-14 23:50:30 +03:00
parent d3ac875a0e
commit 4a4bd437b9
37 changed files with 99 additions and 740 deletions

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.0.10 on 2025-11-13 13:12
# Generated by Django 5.0.10 on 2025-11-14 20:45
import django.db.models.deletion
import simple_history.models
@@ -12,7 +12,7 @@ class Migration(migrations.Migration):
dependencies = [
('customers', '0001_initial'),
('shops', '0001_initial'),
('inventory', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
@@ -25,8 +25,8 @@ class Migration(migrations.Migration):
('code', models.SlugField(help_text="Уникальный идентификатор (например: 'completed', 'cancelled')", unique=True, verbose_name='Код статуса')),
('label', models.CharField(blank=True, max_length=100, verbose_name='Метка для отображения')),
('is_system', models.BooleanField(default=False, help_text='True для встроенных статусов (draft, completed, cancelled)', verbose_name='Системный статус')),
('is_positive_end', models.BooleanField(default=False, help_text='True если это финальный успешный статус (Выполнен)', verbose_name='Положительный конец')),
('is_negative_end', models.BooleanField(default=False, help_text='True если это финальный отрицательный статус (Отменен)', verbose_name='Отрицательный конец')),
('is_positive_end', models.BooleanField(default=False, help_text='True если это финальный успешный статус (Выполнен)', verbose_name='Положительный исход сделки')),
('is_negative_end', models.BooleanField(default=False, help_text='True если это финальный отрицательный статус (Отменен)', verbose_name='Отрицательный исход сделки')),
('order', models.PositiveIntegerField(default=0, verbose_name='Порядок отображения')),
('color', models.CharField(blank=True, default='#808080', help_text='Например: #FF5733', max_length=7, verbose_name='Цвет (hex)')),
('description', models.TextField(blank=True, verbose_name='Описание')),
@@ -112,7 +112,7 @@ class Migration(migrations.Migration):
('delivery_address', models.ForeignKey(blank=True, db_constraint=False, help_text='Обязательно для курьерской доставки', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='orders.address', verbose_name='Адрес доставки')),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, db_constraint=False, help_text='Последний пользователь, изменивший заказ', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Изменен пользователем')),
('pickup_shop', models.ForeignKey(blank=True, db_constraint=False, help_text='Обязательно для самовывоза', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='shops.shop', verbose_name='Точка самовывоза')),
('pickup_warehouse', models.ForeignKey(blank=True, db_constraint=False, help_text='Обязательно для самовывоза', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='inventory.warehouse', verbose_name='Склад для самовывоза')),
],
options={
'verbose_name': 'historical Заказ',
@@ -151,7 +151,7 @@ class Migration(migrations.Migration):
('customer', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='orders', to='customers.customer', verbose_name='Клиент')),
('delivery_address', models.OneToOneField(blank=True, help_text='Обязательно для курьерской доставки', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='order', to='orders.address', verbose_name='Адрес доставки')),
('modified_by', models.ForeignKey(blank=True, help_text='Последний пользователь, изменивший заказ', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modified_orders', to=settings.AUTH_USER_MODEL, verbose_name='Изменен пользователем')),
('pickup_shop', models.ForeignKey(blank=True, help_text='Обязательно для самовывоза', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pickup_orders', to='shops.shop', verbose_name='Точка самовывоза')),
('pickup_warehouse', models.ForeignKey(blank=True, help_text='Обязательно для самовывоза', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pickup_orders', to='inventory.warehouse', verbose_name='Склад для самовывоза')),
],
options={
'verbose_name': 'Заказ',

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.0.10 on 2025-11-13 13:12
# Generated by Django 5.0.10 on 2025-11-14 20:45
import django.db.models.deletion
from django.conf import settings

View File

@@ -1,64 +0,0 @@
# Generated by Django 5.0.10 on 2025-11-13 14:19
from django.db import migrations
def update_status_names_to_russian(apps, schema_editor):
"""Обновляем названия статусов на русский язык"""
OrderStatus = apps.get_model('orders', 'OrderStatus')
status_translations = {
'draft': 'Черновик',
'new': 'Новый',
'confirmed': 'Подтвережден',
'in_assembly': 'В сборке',
'in_delivery': 'В доставке',
'completed': 'Выполнен',
'return': 'Возврат',
'cancelled': 'Отменен',
}
for code, russian_name in status_translations.items():
try:
status = OrderStatus.objects.get(code=code)
status.name = russian_name
status.label = russian_name
status.save()
except OrderStatus.DoesNotExist:
pass
def reverse_status_names(apps, schema_editor):
"""Откатываем названия статусов обратно на английский (для отката миграции)"""
OrderStatus = apps.get_model('orders', 'OrderStatus')
status_translations = {
'draft': 'Draft',
'new': 'New',
'confirmed': 'Confirmed',
'in_assembly': 'In Assembly',
'in_delivery': 'In Delivery',
'completed': 'Completed',
'return': 'Return',
'cancelled': 'Cancelled',
}
for code, english_name in status_translations.items():
try:
status = OrderStatus.objects.get(code=code)
status.name = english_name
status.label = english_name
status.save()
except OrderStatus.DoesNotExist:
pass
class Migration(migrations.Migration):
dependencies = [
('orders', '0002_initial'),
]
operations = [
migrations.RunPython(update_status_names_to_russian, reverse_status_names),
]

View File

@@ -1,23 +0,0 @@
# Generated by Django 5.0.10 on 2025-11-13 18:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('orders', '0003_update_status_names_to_russian'),
]
operations = [
migrations.AlterField(
model_name='orderstatus',
name='is_negative_end',
field=models.BooleanField(default=False, help_text='True если это финальный отрицательный статус (Отменен)', verbose_name='Отрицательный исход сделки'),
),
migrations.AlterField(
model_name='orderstatus',
name='is_positive_end',
field=models.BooleanField(default=False, help_text='True если это финальный успешный статус (Выполнен)', verbose_name='Положительный исход сделки'),
),
]