feat: Добавлена функциональность управления заказами и улучшен поиск товаров
Заказы: - Добавлены миграции для исторических записей с полями оплаты и получателя - Расширен admin для заказов с инлайнами товаров/комплектов - Реализованы представления списка, создания, редактирования и удаления заказов - Добавлен шаблон подтверждения удаления заказа - Настроены URL-маршруты для работы с заказами Клиенты: - Добавлена миграция с новыми полями адресов и подтверждений - Обновлена модель клиентов с дополнительными полями - Улучшен admin для работы с клиентами Товары: - Значительно улучшен API поиска товаров с поддержкой фильтрации - Добавлен Select2 виджет для динамического поиска товаров - Создан статический JS файл для интеграции Select2 - Оптимизирована обработка запросов и ответов API Прочее: - Добавлены новые настройки в settings.py - Обновлена навигация в navbar.html - Обновлены URL-маршруты проекта 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.contrib import admin
|
||||
from .models import Order, OrderItem
|
||||
from .models import Order, OrderItem, Payment
|
||||
|
||||
|
||||
class PaymentInline(admin.TabularInline):
|
||||
"""
|
||||
Inline для управления платежами по заказу.
|
||||
"""
|
||||
model = Payment
|
||||
extra = 1
|
||||
fields = ['amount', 'payment_method', 'payment_date', 'created_by', 'notes']
|
||||
readonly_fields = ['payment_date']
|
||||
|
||||
|
||||
class OrderItemInline(admin.TabularInline):
|
||||
@@ -27,18 +37,19 @@ class OrderAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
'order_number',
|
||||
'customer',
|
||||
'delivery_type',
|
||||
'is_delivery',
|
||||
'delivery_date',
|
||||
'status',
|
||||
'total_amount',
|
||||
'is_paid',
|
||||
'payment_status',
|
||||
'amount_paid',
|
||||
'created_at',
|
||||
]
|
||||
|
||||
list_filter = [
|
||||
'status',
|
||||
'delivery_type',
|
||||
'is_paid',
|
||||
'is_delivery',
|
||||
'payment_status',
|
||||
'delivery_date',
|
||||
'created_at',
|
||||
]
|
||||
@@ -58,6 +69,8 @@ class OrderAdmin(admin.ModelAdmin):
|
||||
'updated_at',
|
||||
'delivery_info',
|
||||
'delivery_time_window',
|
||||
'amount_due',
|
||||
'payment_status',
|
||||
]
|
||||
|
||||
fieldsets = (
|
||||
@@ -66,7 +79,8 @@ class OrderAdmin(admin.ModelAdmin):
|
||||
}),
|
||||
('Доставка', {
|
||||
'fields': (
|
||||
'delivery_type',
|
||||
'is_delivery',
|
||||
'customer_is_recipient',
|
||||
'delivery_address',
|
||||
'pickup_shop',
|
||||
'delivery_date',
|
||||
@@ -78,19 +92,26 @@ class OrderAdmin(admin.ModelAdmin):
|
||||
)
|
||||
}),
|
||||
('Оплата', {
|
||||
'fields': ('payment_method', 'is_paid', 'total_amount')
|
||||
'fields': (
|
||||
'payment_method',
|
||||
'total_amount',
|
||||
'discount_amount',
|
||||
'amount_paid',
|
||||
'amount_due',
|
||||
'payment_status',
|
||||
)
|
||||
}),
|
||||
('Дополнительно', {
|
||||
'fields': ('is_anonymous', 'special_instructions'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
('Системная информация', {
|
||||
'fields': ('created_at', 'updated_at'),
|
||||
'fields': ('created_at', 'updated_at', 'modified_by'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
)
|
||||
|
||||
inlines = [OrderItemInline]
|
||||
inlines = [OrderItemInline, PaymentInline]
|
||||
|
||||
actions = [
|
||||
'mark_as_confirmed',
|
||||
@@ -131,6 +152,41 @@ class OrderAdmin(admin.ModelAdmin):
|
||||
mark_as_paid.short_description = 'Отметить как оплаченные'
|
||||
|
||||
|
||||
@admin.register(Payment)
|
||||
class PaymentAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Админ-панель для управления платежами.
|
||||
"""
|
||||
list_display = [
|
||||
'order',
|
||||
'amount',
|
||||
'payment_method',
|
||||
'payment_date',
|
||||
'created_by',
|
||||
]
|
||||
|
||||
list_filter = [
|
||||
'payment_method',
|
||||
'payment_date',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
'order__order_number',
|
||||
'notes',
|
||||
]
|
||||
|
||||
readonly_fields = ['payment_date']
|
||||
|
||||
fieldsets = (
|
||||
('Информация о платеже', {
|
||||
'fields': ('order', 'amount', 'payment_method', 'payment_date')
|
||||
}),
|
||||
('Дополнительно', {
|
||||
'fields': ('created_by', 'notes')
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(OrderItem)
|
||||
class OrderItemAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
# Generated by Django 5.0.10 on 2025-11-06 20:54
|
||||
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('customers', '0002_address_confirm_address_with_recipient_and_more'),
|
||||
('orders', '0001_initial'),
|
||||
('shops', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HistoricalOrder',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('order_number', models.CharField(db_index=True, editable=False, help_text='Уникальный номер заказа для отображения клиенту', max_length=50, verbose_name='Номер заказа')),
|
||||
('is_delivery', models.BooleanField(default=True, help_text='True - доставка курьером, False - самовывоз', verbose_name='С доставкой')),
|
||||
('delivery_date', models.DateField(blank=True, help_text='Может быть заполнено позже', null=True, verbose_name='Дата доставки/самовывоза')),
|
||||
('delivery_time_start', models.TimeField(blank=True, help_text='Начало временного интервала', null=True, verbose_name='Время от')),
|
||||
('delivery_time_end', models.TimeField(blank=True, help_text='Конец временного интервала', null=True, verbose_name='Время до')),
|
||||
('delivery_cost', models.DecimalField(decimal_places=2, default=0, help_text='0 для самовывоза', max_digits=10, verbose_name='Стоимость доставки')),
|
||||
('status', models.CharField(choices=[('new', 'Новый'), ('confirmed', 'Подтвержден'), ('in_assembly', 'В сборке'), ('in_delivery', 'В доставке'), ('delivered', 'Доставлен'), ('cancelled', 'Отменен')], default='new', max_length=20, verbose_name='Статус заказа')),
|
||||
('payment_method', models.CharField(choices=[('cash_to_courier', 'Наличные курьеру'), ('card_to_courier', 'Карта курьеру'), ('online', 'Онлайн оплата'), ('bank_transfer', 'Банковский перевод')], default='cash_to_courier', max_length=20, verbose_name='Способ оплаты')),
|
||||
('is_paid', models.BooleanField(default=False, verbose_name='Оплачен')),
|
||||
('total_amount', models.DecimalField(decimal_places=2, default=0, help_text='Общая сумма заказа включая доставку', max_digits=10, verbose_name='Итоговая сумма заказа')),
|
||||
('discount_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='Особые пожелания')),
|
||||
('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)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical Заказ',
|
||||
'verbose_name_plural': 'historical Заказы',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Payment',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Сумма платежа')),
|
||||
('payment_method', models.CharField(choices=[('cash_to_courier', 'Наличные курьеру'), ('card_to_courier', 'Карта курьеру'), ('online', 'Онлайн оплата'), ('bank_transfer', 'Банковский перевод')], max_length=20, verbose_name='Способ оплаты')),
|
||||
('payment_date', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время платежа')),
|
||||
('notes', models.TextField(blank=True, help_text='Дополнительная информация о платеже', null=True, verbose_name='Примечания')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Платеж',
|
||||
'verbose_name_plural': 'Платежи',
|
||||
'ordering': ['-payment_date'],
|
||||
},
|
||||
),
|
||||
migrations.RemoveIndex(
|
||||
model_name='order',
|
||||
name='orders_orde_deliver_f68568_idx',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='order',
|
||||
name='delivery_type',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='amount_paid',
|
||||
field=models.DecimalField(decimal_places=2, default=0, help_text='Сумма, внесенная клиентом', max_digits=10, verbose_name='Оплачено'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='customer_is_recipient',
|
||||
field=models.BooleanField(default=True, help_text='Если отмечено, данные получателя не требуются отдельно', verbose_name='Покупатель является получателем'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='discount_amount',
|
||||
field=models.DecimalField(decimal_places=2, default=0, help_text='Применяется вручную или через систему скидок', max_digits=10, verbose_name='Сумма скидки'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='is_delivery',
|
||||
field=models.BooleanField(default=True, help_text='True - доставка курьером, False - самовывоз', verbose_name='С доставкой'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='modified_by',
|
||||
field=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='Изменен пользователем'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='payment_status',
|
||||
field=models.CharField(choices=[('unpaid', 'Не оплачен'), ('partial', 'Частично оплачен'), ('paid', 'Оплачен полностью')], default='unpaid', help_text='Обновляется автоматически при добавлении платежей', max_length=20, verbose_name='Статус оплаты'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='delivery_date',
|
||||
field=models.DateField(blank=True, help_text='Может быть заполнено позже', null=True, verbose_name='Дата доставки/самовывоза'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='delivery_time_end',
|
||||
field=models.TimeField(blank=True, help_text='Конец временного интервала', null=True, verbose_name='Время до'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='delivery_time_start',
|
||||
field=models.TimeField(blank=True, help_text='Начало временного интервала', null=True, verbose_name='Время от'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='order',
|
||||
index=models.Index(fields=['is_delivery'], name='orders_orde_is_deli_07c9c0_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='order',
|
||||
index=models.Index(fields=['payment_status'], name='orders_orde_payment_bc131d_idx'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalorder',
|
||||
name='customer',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='customers.customer', verbose_name='Клиент'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalorder',
|
||||
name='delivery_address',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, help_text='Обязательно для курьерской доставки', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='customers.address', verbose_name='Адрес доставки'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalorder',
|
||||
name='history_user',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalorder',
|
||||
name='modified_by',
|
||||
field=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='Изменен пользователем'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalorder',
|
||||
name='pickup_shop',
|
||||
field=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='Точка самовывоза'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='payment',
|
||||
name='created_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='payments_created', to=settings.AUTH_USER_MODEL, verbose_name='Принял платеж'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='payment',
|
||||
name='order',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='orders.order', verbose_name='Заказ'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='payment',
|
||||
index=models.Index(fields=['order'], name='orders_paym_order_i_8c8d98_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='payment',
|
||||
index=models.Index(fields=['payment_date'], name='orders_paym_payment_9e5ac0_idx'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 5.0.10 on 2025-11-06 21:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orders', '0002_historicalorder_payment_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='historicalorder',
|
||||
name='recipient_name',
|
||||
field=models.CharField(blank=True, help_text='Заполняется, если покупатель не является получателем', max_length=200, null=True, verbose_name='Имя получателя'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalorder',
|
||||
name='recipient_phone',
|
||||
field=models.CharField(blank=True, help_text='Контактный телефон получателя', max_length=20, null=True, verbose_name='Телефон получателя'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='recipient_name',
|
||||
field=models.CharField(blank=True, help_text='Заполняется, если покупатель не является получателем', max_length=200, null=True, verbose_name='Имя получателя'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='recipient_phone',
|
||||
field=models.CharField(blank=True, help_text='Контактный телефон получателя', max_length=20, null=True, verbose_name='Телефон получателя'),
|
||||
),
|
||||
]
|
||||
66
myproject/orders/templates/orders/order_confirm_delete.html
Normal file
66
myproject/orders/templates/orders/order_confirm_delete.html
Normal file
@@ -0,0 +1,66 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Подтверждение удаления заказа{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card mt-5">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h4 class="mb-0">
|
||||
<i class="bi bi-exclamation-triangle"></i> Подтверждение удаления
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-warning">
|
||||
<i class="bi bi-exclamation-circle"></i>
|
||||
<strong>Внимание!</strong> Вы собираетесь удалить заказ.
|
||||
</div>
|
||||
|
||||
<h5>Заказ {{ order.order_number }}</h5>
|
||||
|
||||
<dl class="row mt-3">
|
||||
<dt class="col-sm-4">Клиент:</dt>
|
||||
<dd class="col-sm-8">{{ order.customer.name }}</dd>
|
||||
|
||||
<dt class="col-sm-4">Дата создания:</dt>
|
||||
<dd class="col-sm-8">{{ order.created_at|date:"d.m.Y H:i" }}</dd>
|
||||
|
||||
<dt class="col-sm-4">Статус:</dt>
|
||||
<dd class="col-sm-8">{{ order.get_status_display }}</dd>
|
||||
|
||||
<dt class="col-sm-4">Сумма заказа:</dt>
|
||||
<dd class="col-sm-8"><strong>{{ order.total_amount }} руб.</strong></dd>
|
||||
|
||||
<dt class="col-sm-4">Товаров в заказе:</dt>
|
||||
<dd class="col-sm-8">{{ order.items.count }}</dd>
|
||||
</dl>
|
||||
|
||||
<div class="alert alert-danger mt-3">
|
||||
<strong>Это действие нельзя отменить!</strong><br>
|
||||
Будут удалены все связанные данные:
|
||||
<ul class="mb-0 mt-2">
|
||||
<li>Все позиции заказа ({{ order.items.count }} шт.)</li>
|
||||
<li>История платежей ({{ order.payments.count }} записей)</li>
|
||||
<li>История изменений заказа</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form method="post" class="mt-4">
|
||||
{% csrf_token %}
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="{% url 'orders:order-detail' order.pk %}" class="btn btn-secondary btn-lg">
|
||||
<i class="bi bi-arrow-left"></i> Отмена
|
||||
</a>
|
||||
<button type="submit" class="btn btn-danger btn-lg">
|
||||
<i class="bi bi-trash"></i> Да, удалить заказ
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
13
myproject/orders/urls.py
Normal file
13
myproject/orders/urls.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
app_name = 'orders'
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.order_list, name='order-list'),
|
||||
path('create/', views.order_create, name='order-create'),
|
||||
path('<int:pk>/', views.order_detail, name='order-detail'),
|
||||
path('<int:pk>/edit/', views.order_update, name='order-update'),
|
||||
path('<int:pk>/delete/', views.order_delete, name='order-delete'),
|
||||
]
|
||||
@@ -1,3 +1,155 @@
|
||||
from django.shortcuts import render
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib import messages
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Q
|
||||
from .models import Order, OrderItem
|
||||
from .forms import OrderForm, OrderItemFormSet
|
||||
|
||||
# Create your views here.
|
||||
|
||||
def order_list(request):
|
||||
"""Список всех заказов с фильтрацией и поиском"""
|
||||
orders = Order.objects.select_related('customer', 'delivery_address', 'pickup_shop').all()
|
||||
|
||||
# Поиск
|
||||
search_query = request.GET.get('search', '')
|
||||
if search_query:
|
||||
orders = orders.filter(
|
||||
Q(order_number__icontains=search_query) |
|
||||
Q(customer__name__icontains=search_query) |
|
||||
Q(customer__phone__icontains=search_query) |
|
||||
Q(customer__email__icontains=search_query)
|
||||
)
|
||||
|
||||
# Фильтр по статусу
|
||||
status_filter = request.GET.get('status', '')
|
||||
if status_filter:
|
||||
orders = orders.filter(status=status_filter)
|
||||
|
||||
# Фильтр по типу доставки
|
||||
delivery_filter = request.GET.get('delivery_type', '')
|
||||
if delivery_filter == 'delivery':
|
||||
orders = orders.filter(is_delivery=True)
|
||||
elif delivery_filter == 'pickup':
|
||||
orders = orders.filter(is_delivery=False)
|
||||
|
||||
# Сортировка
|
||||
orders = orders.order_by('-created_at')
|
||||
|
||||
# Пагинация
|
||||
paginator = Paginator(orders, 25)
|
||||
page_number = request.GET.get('page')
|
||||
page_obj = paginator.get_page(page_number)
|
||||
|
||||
context = {
|
||||
'page_obj': page_obj,
|
||||
'search_query': search_query,
|
||||
'status_filter': status_filter,
|
||||
'delivery_filter': delivery_filter,
|
||||
'status_choices': Order.STATUS_CHOICES,
|
||||
}
|
||||
|
||||
return render(request, 'orders/order_list.html', context)
|
||||
|
||||
|
||||
def order_detail(request, pk):
|
||||
"""Детальная информация о заказе"""
|
||||
order = get_object_or_404(
|
||||
Order.objects.select_related('customer', 'delivery_address', 'pickup_shop', 'modified_by')
|
||||
.prefetch_related('items__product', 'items__product_kit', 'payments__created_by'),
|
||||
pk=pk
|
||||
)
|
||||
|
||||
context = {
|
||||
'order': order,
|
||||
}
|
||||
|
||||
return render(request, 'orders/order_detail.html', context)
|
||||
|
||||
|
||||
def order_create(request):
|
||||
"""Создание нового заказа"""
|
||||
if request.method == 'POST':
|
||||
form = OrderForm(request.POST)
|
||||
formset = OrderItemFormSet(request.POST)
|
||||
|
||||
if form.is_valid() and formset.is_valid():
|
||||
order = form.save(commit=False)
|
||||
order.save()
|
||||
|
||||
# Сохраняем позиции заказа
|
||||
formset.instance = order
|
||||
formset.save()
|
||||
|
||||
# Пересчитываем итоговую сумму
|
||||
order.calculate_total()
|
||||
order.save()
|
||||
|
||||
messages.success(request, f'Заказ #{order.order_number} успешно создан!')
|
||||
return redirect('orders:order-detail', pk=order.pk)
|
||||
else:
|
||||
messages.error(request, 'Пожалуйста, исправьте ошибки в форме.')
|
||||
else:
|
||||
form = OrderForm()
|
||||
formset = OrderItemFormSet()
|
||||
|
||||
context = {
|
||||
'form': form,
|
||||
'formset': formset,
|
||||
'title': 'Создание заказа',
|
||||
'button_text': 'Создать заказ',
|
||||
}
|
||||
|
||||
return render(request, 'orders/order_form.html', context)
|
||||
|
||||
|
||||
def order_update(request, pk):
|
||||
"""Редактирование заказа"""
|
||||
order = get_object_or_404(Order, pk=pk)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = OrderForm(request.POST, instance=order)
|
||||
formset = OrderItemFormSet(request.POST, instance=order)
|
||||
|
||||
if form.is_valid() and formset.is_valid():
|
||||
order = form.save()
|
||||
formset.save()
|
||||
|
||||
# Пересчитываем итоговую сумму
|
||||
order.calculate_total()
|
||||
order.save()
|
||||
|
||||
messages.success(request, f'Заказ #{order.order_number} успешно обновлен!')
|
||||
return redirect('orders:order-detail', pk=order.pk)
|
||||
else:
|
||||
messages.error(request, 'Пожалуйста, исправьте ошибки в форме.')
|
||||
else:
|
||||
form = OrderForm(instance=order)
|
||||
formset = OrderItemFormSet(instance=order)
|
||||
|
||||
context = {
|
||||
'form': form,
|
||||
'formset': formset,
|
||||
'order': order,
|
||||
'title': f'Редактирование заказа #{order.order_number}',
|
||||
'button_text': 'Сохранить изменения',
|
||||
}
|
||||
|
||||
return render(request, 'orders/order_form.html', context)
|
||||
|
||||
|
||||
def order_delete(request, pk):
|
||||
"""Удаление заказа с подтверждением"""
|
||||
order = get_object_or_404(Order, pk=pk)
|
||||
|
||||
if request.method == 'POST':
|
||||
order_number = order.order_number
|
||||
order.delete()
|
||||
messages.success(request, f'Заказ #{order_number} успешно удален.')
|
||||
return redirect('orders:order-list')
|
||||
|
||||
context = {
|
||||
'order': order,
|
||||
}
|
||||
|
||||
return render(request, 'orders/order_confirm_delete.html', context)
|
||||
|
||||
Reference in New Issue
Block a user