Implement flexible order status management system

Features:
- Created OrderStatus model for managing statuses per tenant
- Added system-level statuses: draft, new, confirmed, in_assembly, in_delivery, completed, return, cancelled
- Implemented CRUD views for managing order statuses
- Created OrderStatusService with status transitions and business logic hooks
- Updated Order model to use ForeignKey to OrderStatus
- Added is_returned flag for tracking returned orders
- Updated filters to work with new OrderStatus model
- Created management command for status initialization
- Added HTML templates for status list, form, and confirmation
- Fixed views.py to use OrderStatus instead of removed STATUS_CHOICES

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-13 16:29:50 +03:00
parent 0d5f0d2015
commit c7875f147c
28 changed files with 1337 additions and 390 deletions

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.0.10 on 2025-11-09 22:18
# Generated by Django 5.0.10 on 2025-11-13 13:12
import phonenumber_field.modelfields
from django.db import migrations, models
@@ -17,20 +17,13 @@ class Migration(migrations.Migration):
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название магазина')),
('street', models.CharField(max_length=255, verbose_name='Улица')),
('building_number', models.CharField(max_length=20, verbose_name='Номер здания')),
('district', models.CharField(blank=True, help_text='Район в Минске', max_length=100, null=True, verbose_name='Район')),
('phone', phonenumber_field.modelfields.PhoneNumberField(help_text='Контактный телефон магазина', max_length=128, region=None, verbose_name='Телефон')),
('description', models.TextField(blank=True, help_text='Дополнительная информация о магазине', null=True, verbose_name='Описание')),
('street', models.CharField(blank=True, max_length=255, null=True, verbose_name='Улица')),
('building_number', models.CharField(blank=True, max_length=20, null=True, verbose_name='Номер здания')),
('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, help_text='Контактный телефон магазина', max_length=128, null=True, region=None, verbose_name='Телефон')),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email')),
('opening_time', models.TimeField(help_text='Время начала работы магазина', verbose_name='Время открытия')),
('closing_time', models.TimeField(help_text='Время окончания работы магазина', verbose_name='Время закрытия')),
('working_days', models.CharField(default='Пн-Вс', help_text='Например: Пн-Пт, Пн-Вс, Пн-Сб', max_length=100, verbose_name='Рабочие дни')),
('is_active', models.BooleanField(default=True, help_text='Работает ли магазин в данный момент', verbose_name='Активен')),
('is_pickup_point', models.BooleanField(default=True, help_text='Доступен ли магазин для самовывоза заказов', verbose_name='Пункт самовывоза')),
('description', models.TextField(blank=True, help_text='Дополнительная информация о магазине', null=True, verbose_name='Описание')),
('delivery_instructions', models.TextField(blank=True, help_text='Как найти магазин, где припарковаться и т.д.', null=True, verbose_name='Инструкции для клиентов')),
('latitude', models.DecimalField(blank=True, decimal_places=6, help_text='Координаты для отображения на карте', max_digits=9, null=True, verbose_name='Широта')),
('longitude', models.DecimalField(blank=True, decimal_places=6, help_text='Координаты для отображения на карте', max_digits=9, null=True, verbose_name='Долгота')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
],
@@ -38,7 +31,7 @@ class Migration(migrations.Migration):
'verbose_name': 'Магазин',
'verbose_name_plural': 'Магазины',
'ordering': ['name'],
'indexes': [models.Index(fields=['is_active'], name='shops_shop_is_acti_bbb154_idx'), models.Index(fields=['is_pickup_point'], name='shops_shop_is_pick_a04981_idx'), models.Index(fields=['district'], name='shops_shop_distric_04626c_idx')],
'indexes': [models.Index(fields=['is_active'], name='shops_shop_is_acti_bbb154_idx'), models.Index(fields=['is_pickup_point'], name='shops_shop_is_pick_a04981_idx')],
},
),
]

View File

@@ -1,61 +0,0 @@
# Generated by Django 5.0.10 on 2025-11-11 20:55
import phonenumber_field.modelfields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shops', '0001_initial'),
]
operations = [
migrations.RemoveIndex(
model_name='shop',
name='shops_shop_distric_04626c_idx',
),
migrations.RemoveField(
model_name='shop',
name='closing_time',
),
migrations.RemoveField(
model_name='shop',
name='delivery_instructions',
),
migrations.RemoveField(
model_name='shop',
name='district',
),
migrations.RemoveField(
model_name='shop',
name='latitude',
),
migrations.RemoveField(
model_name='shop',
name='longitude',
),
migrations.RemoveField(
model_name='shop',
name='opening_time',
),
migrations.RemoveField(
model_name='shop',
name='working_days',
),
migrations.AlterField(
model_name='shop',
name='building_number',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Номер здания'),
),
migrations.AlterField(
model_name='shop',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, help_text='Контактный телефон магазина', max_length=128, null=True, region=None, verbose_name='Телефон'),
),
migrations.AlterField(
model_name='shop',
name='street',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Улица'),
),
]