fix: Исправлена работа черновиков заказов и добавлено автосохранение статуса
Проблема 1: Ошибка 500 при создании черновика заказа - Поле status в модели Order является ForeignKey на OrderStatus - В коде использовались строковые значения 'draft' и 'new' вместо объектов - Это приводило к TypeError при создании/обновлении заказов Решение: - В DraftOrderService.create_draft: добавлен get_or_create для статуса 'draft' - В DraftOrderService.finalize_draft: добавлен get_or_create для статуса 'new' - В DraftOrderService.get_user_drafts: заменен фильтр status='draft' на status__code='draft' - В DraftOrderService.delete_old_drafts: заменен фильтр status='draft' на status__code='draft' - В cleanup_draft_orders.py: исправлен фильтр в режиме dry-run Проблема 2: Отсутствие автосохранения при изменении статуса - Поле status не отслеживалось в autosave.js - При смене статуса заказ не сохранялся автоматически Решение: - Добавлено поле 'select[name="status"]' в список отслеживаемых полей - Добавлен сбор значения статуса в функции collectFormData - Добавлено 'status': 'orders.OrderStatus' в fk_fields для обработки на сервере Дополнительно: - Добавлено автосохранение полей адреса доставки (улица, дом, квартира и т.д.) - Добавлено автосохранение полей получателя (имя, телефон) - Добавлена автоматическая установка address_mode='new' при наличии адреса Файлы: - orders/services/draft_service.py - orders/management/commands/cleanup_draft_orders.py - orders/static/orders/js/autosave.js 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -50,7 +50,7 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
cutoff_date = timezone.now() - timedelta(days=days)
|
cutoff_date = timezone.now() - timedelta(days=days)
|
||||||
old_drafts = Order.objects.filter(
|
old_drafts = Order.objects.filter(
|
||||||
status='draft',
|
status__code='draft',
|
||||||
last_autosave_at__lt=cutoff_date
|
last_autosave_at__lt=cutoff_date
|
||||||
)
|
)
|
||||||
count = old_drafts.count()
|
count = old_drafts.count()
|
||||||
|
|||||||
@@ -39,9 +39,21 @@ class DraftOrderService:
|
|||||||
data = data or {}
|
data = data or {}
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
|
# Получаем или создаем статус 'draft'
|
||||||
|
from ..models import OrderStatus
|
||||||
|
draft_status, _ = OrderStatus.objects.get_or_create(
|
||||||
|
code='draft',
|
||||||
|
defaults={
|
||||||
|
'name': 'Черновик',
|
||||||
|
'label': 'Черновик',
|
||||||
|
'is_system': True,
|
||||||
|
'color': '#808080',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
order = Order.objects.create(
|
order = Order.objects.create(
|
||||||
customer=customer,
|
customer=customer,
|
||||||
status='draft',
|
status=draft_status,
|
||||||
modified_by=user,
|
modified_by=user,
|
||||||
is_delivery=data.get('is_delivery', True),
|
is_delivery=data.get('is_delivery', True),
|
||||||
delivery_address=data.get('delivery_address'),
|
delivery_address=data.get('delivery_address'),
|
||||||
@@ -86,6 +98,7 @@ class DraftOrderService:
|
|||||||
fk_fields = {
|
fk_fields = {
|
||||||
'customer': 'customers.Customer',
|
'customer': 'customers.Customer',
|
||||||
'pickup_shop': 'shops.Shop',
|
'pickup_shop': 'shops.Shop',
|
||||||
|
'status': 'orders.OrderStatus',
|
||||||
}
|
}
|
||||||
|
|
||||||
simple_fields = [
|
simple_fields = [
|
||||||
@@ -308,8 +321,20 @@ class DraftOrderService:
|
|||||||
# Выполняем полную валидацию модели
|
# Выполняем полную валидацию модели
|
||||||
order.full_clean()
|
order.full_clean()
|
||||||
|
|
||||||
|
# Получаем или создаем статус 'new'
|
||||||
|
from ..models import OrderStatus
|
||||||
|
new_status, _ = OrderStatus.objects.get_or_create(
|
||||||
|
code='new',
|
||||||
|
defaults={
|
||||||
|
'name': 'Новый',
|
||||||
|
'label': 'Новый',
|
||||||
|
'is_system': True,
|
||||||
|
'color': '#0d6efd',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Изменяем статус на 'new'
|
# Изменяем статус на 'new'
|
||||||
order.status = 'new'
|
order.status = new_status
|
||||||
order.modified_by = user
|
order.modified_by = user
|
||||||
order.last_autosave_at = None # Очищаем, т.к. заказ больше не черновик
|
order.last_autosave_at = None # Очищаем, т.к. заказ больше не черновик
|
||||||
order.save()
|
order.save()
|
||||||
@@ -335,7 +360,7 @@ class DraftOrderService:
|
|||||||
QuerySet: Черновики заказов
|
QuerySet: Черновики заказов
|
||||||
"""
|
"""
|
||||||
drafts = Order.objects.filter(
|
drafts = Order.objects.filter(
|
||||||
status='draft',
|
status__code='draft',
|
||||||
modified_by=user
|
modified_by=user
|
||||||
).select_related('customer', 'delivery_address', 'pickup_shop')
|
).select_related('customer', 'delivery_address', 'pickup_shop')
|
||||||
|
|
||||||
@@ -361,7 +386,7 @@ class DraftOrderService:
|
|||||||
|
|
||||||
# Находим старые черновики
|
# Находим старые черновики
|
||||||
old_drafts = Order.objects.filter(
|
old_drafts = Order.objects.filter(
|
||||||
status='draft',
|
status__code='draft',
|
||||||
last_autosave_at__lt=cutoff_date
|
last_autosave_at__lt=cutoff_date
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,7 @@
|
|||||||
// Слушаем изменения в основных полях заказа
|
// Слушаем изменения в основных полях заказа
|
||||||
const fieldsToWatch = [
|
const fieldsToWatch = [
|
||||||
'select[name="customer"]',
|
'select[name="customer"]',
|
||||||
|
'select[name="status"]',
|
||||||
'input[name="delivery_date"]',
|
'input[name="delivery_date"]',
|
||||||
'input[name="delivery_time_start"]',
|
'input[name="delivery_time_start"]',
|
||||||
'input[name="delivery_time_end"]',
|
'input[name="delivery_time_end"]',
|
||||||
@@ -147,6 +148,17 @@
|
|||||||
'input[type="radio"]',
|
'input[type="radio"]',
|
||||||
'select[name="delivery_address"]',
|
'select[name="delivery_address"]',
|
||||||
'select[name="pickup_shop"]',
|
'select[name="pickup_shop"]',
|
||||||
|
// Поля адреса доставки
|
||||||
|
'input[name="address_street"]',
|
||||||
|
'input[name="address_building_number"]',
|
||||||
|
'input[name="address_apartment_number"]',
|
||||||
|
'input[name="address_entrance"]',
|
||||||
|
'input[name="address_floor"]',
|
||||||
|
'input[name="address_intercom_code"]',
|
||||||
|
'textarea[name="address_delivery_instructions"]',
|
||||||
|
// Поля получателя
|
||||||
|
'input[name="recipient_name"]',
|
||||||
|
'input[name="recipient_phone"]',
|
||||||
];
|
];
|
||||||
|
|
||||||
fieldsToWatch.forEach(selector => {
|
fieldsToWatch.forEach(selector => {
|
||||||
@@ -290,6 +302,11 @@
|
|||||||
data.customer = parseInt(customerField.value);
|
data.customer = parseInt(customerField.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const statusField = form.querySelector('select[name="status"]');
|
||||||
|
if (statusField && statusField.value) {
|
||||||
|
data.status = parseInt(statusField.value);
|
||||||
|
}
|
||||||
|
|
||||||
const deliveryDateField = form.querySelector('input[name="delivery_date"]');
|
const deliveryDateField = form.querySelector('input[name="delivery_date"]');
|
||||||
if (deliveryDateField && deliveryDateField.value) {
|
if (deliveryDateField && deliveryDateField.value) {
|
||||||
data.delivery_date = deliveryDateField.value;
|
data.delivery_date = deliveryDateField.value;
|
||||||
@@ -352,6 +369,60 @@
|
|||||||
data.pickup_shop = parseInt(pickupShopField.value);
|
data.pickup_shop = parseInt(pickupShopField.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Поля адреса доставки (новая логика с прямым вводом)
|
||||||
|
const addressStreetField = form.querySelector('input[name="address_street"]');
|
||||||
|
if (addressStreetField && addressStreetField.value) {
|
||||||
|
data.address_street = addressStreetField.value;
|
||||||
|
// Указываем режим "новый адрес" если есть улица
|
||||||
|
data.address_mode = 'new';
|
||||||
|
}
|
||||||
|
|
||||||
|
const addressBuildingField = form.querySelector('input[name="address_building_number"]');
|
||||||
|
if (addressBuildingField && addressBuildingField.value) {
|
||||||
|
data.address_building_number = addressBuildingField.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addressApartmentField = form.querySelector('input[name="address_apartment_number"]');
|
||||||
|
if (addressApartmentField && addressApartmentField.value) {
|
||||||
|
data.address_apartment_number = addressApartmentField.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addressEntranceField = form.querySelector('input[name="address_entrance"]');
|
||||||
|
if (addressEntranceField && addressEntranceField.value) {
|
||||||
|
data.address_entrance = addressEntranceField.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addressFloorField = form.querySelector('input[name="address_floor"]');
|
||||||
|
if (addressFloorField && addressFloorField.value) {
|
||||||
|
data.address_floor = addressFloorField.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addressIntercomField = form.querySelector('input[name="address_intercom_code"]');
|
||||||
|
if (addressIntercomField && addressIntercomField.value) {
|
||||||
|
data.address_intercom_code = addressIntercomField.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addressInstructionsField = form.querySelector('textarea[name="address_delivery_instructions"]');
|
||||||
|
if (addressInstructionsField && addressInstructionsField.value) {
|
||||||
|
data.address_delivery_instructions = addressInstructionsField.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addressConfirmField = form.querySelector('input[name="address_confirm_with_recipient"]');
|
||||||
|
if (addressConfirmField) {
|
||||||
|
data.address_confirm_with_recipient = addressConfirmField.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Поля получателя
|
||||||
|
const recipientNameField = form.querySelector('input[name="recipient_name"]');
|
||||||
|
if (recipientNameField && recipientNameField.value) {
|
||||||
|
data.recipient_name = recipientNameField.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recipientPhoneField = form.querySelector('input[name="recipient_phone"]');
|
||||||
|
if (recipientPhoneField && recipientPhoneField.value) {
|
||||||
|
data.recipient_phone = recipientPhoneField.value;
|
||||||
|
}
|
||||||
|
|
||||||
// Собираем позиции заказа
|
// Собираем позиции заказа
|
||||||
data.items = collectOrderItems();
|
data.items = collectOrderItems();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user