feat: Добавлена возможность ручного изменения цены товаров/комплектов в заказе
- Добавлено поле is_custom_price в модель OrderItem для отслеживания ручных изменений - Добавлены свойства original_price и price_difference для отображения оригинальной цены и разницы - Поле цены теперь редактируемое (убран атрибут readonly) - Добавлены визуальные индикаторы: бейдж "Изменена" и информация об оригинальной цене - JavaScript автоматически отслеживает изменения цены и устанавливает флаг is_custom_price - В детальном просмотре заказа показывается информация о кастомных ценах с разницей - Цена товара в каталоге не изменяется - изменения только для конкретного заказа 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
156
myproject/orders/forms.py
Normal file
156
myproject/orders/forms.py
Normal file
@@ -0,0 +1,156 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django import forms
|
||||
from django.forms import inlineformset_factory
|
||||
from .models import Order, OrderItem
|
||||
from customers.models import Customer, Address
|
||||
from shops.models import Shop
|
||||
from products.models import Product, ProductKit
|
||||
|
||||
|
||||
class OrderForm(forms.ModelForm):
|
||||
"""Форма для создания и редактирования заказа"""
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = [
|
||||
'customer',
|
||||
'is_delivery',
|
||||
'delivery_address',
|
||||
'pickup_shop',
|
||||
'delivery_date',
|
||||
'delivery_time_start',
|
||||
'delivery_time_end',
|
||||
'delivery_cost',
|
||||
'customer_is_recipient',
|
||||
'recipient_name',
|
||||
'recipient_phone',
|
||||
'status',
|
||||
'payment_method',
|
||||
'discount_amount',
|
||||
'is_anonymous',
|
||||
'special_instructions',
|
||||
]
|
||||
widgets = {
|
||||
'delivery_date': forms.DateInput(attrs={'type': 'date'}),
|
||||
'delivery_time_start': forms.TimeInput(attrs={'type': 'time'}),
|
||||
'delivery_time_end': forms.TimeInput(attrs={'type': 'time'}),
|
||||
'special_instructions': forms.Textarea(attrs={'rows': 3}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Добавляем Bootstrap классы ко всем полям
|
||||
for field_name, field in self.fields.items():
|
||||
if isinstance(field.widget, forms.CheckboxInput):
|
||||
field.widget.attrs.update({'class': 'form-check-input'})
|
||||
elif isinstance(field.widget, forms.Textarea):
|
||||
field.widget.attrs.update({'class': 'form-control', 'rows': 3})
|
||||
else:
|
||||
field.widget.attrs.update({'class': 'form-control'})
|
||||
|
||||
# Select2 для выпадающих списков
|
||||
self.fields['customer'].widget.attrs.update({
|
||||
'class': 'form-select select2',
|
||||
'data-placeholder': 'Выберите клиента'
|
||||
})
|
||||
|
||||
self.fields['delivery_address'].widget.attrs.update({
|
||||
'class': 'form-select select2',
|
||||
'data-placeholder': 'Выберите адрес доставки'
|
||||
})
|
||||
self.fields['delivery_address'].required = False
|
||||
|
||||
self.fields['pickup_shop'].widget.attrs.update({
|
||||
'class': 'form-select select2',
|
||||
'data-placeholder': 'Выберите точку самовывоза'
|
||||
})
|
||||
self.fields['pickup_shop'].required = False
|
||||
|
||||
# Опциональные поля даты/времени
|
||||
self.fields['delivery_date'].required = False
|
||||
self.fields['delivery_time_start'].required = False
|
||||
self.fields['delivery_time_end'].required = False
|
||||
|
||||
# Подсказки
|
||||
self.fields['is_delivery'].label = 'С доставкой'
|
||||
self.fields['customer_is_recipient'].label = 'Покупатель = получатель'
|
||||
|
||||
# Поля получателя опциональны
|
||||
self.fields['recipient_name'].required = False
|
||||
self.fields['recipient_phone'].required = False
|
||||
|
||||
|
||||
class OrderItemForm(forms.ModelForm):
|
||||
"""Форма для позиции заказа"""
|
||||
|
||||
class Meta:
|
||||
model = OrderItem
|
||||
fields = ['product', 'product_kit', 'quantity', 'price', 'is_custom_price']
|
||||
widgets = {
|
||||
'quantity': forms.NumberInput(attrs={'min': 1, 'value': 1}),
|
||||
# Скрываем поля product и product_kit - они будут заполняться через JS
|
||||
'product': forms.HiddenInput(),
|
||||
'product_kit': forms.HiddenInput(),
|
||||
'is_custom_price': forms.HiddenInput(),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Bootstrap классы
|
||||
for field_name, field in self.fields.items():
|
||||
if not isinstance(field.widget, forms.HiddenInput):
|
||||
field.widget.attrs.update({'class': 'form-control'})
|
||||
|
||||
# Поля product и product_kit опциональны
|
||||
self.fields['product'].required = False
|
||||
self.fields['product_kit'].required = False
|
||||
|
||||
# Поле цены заполняется автоматически, но можно редактировать вручную
|
||||
self.fields['price'].widget.attrs.update({
|
||||
'placeholder': 'Цена',
|
||||
'step': '0.01'
|
||||
})
|
||||
self.fields['price'].required = False
|
||||
|
||||
# Поле is_custom_price устанавливается через JS
|
||||
self.fields['is_custom_price'].required = False
|
||||
|
||||
def clean(self):
|
||||
"""Валидация: должен быть выбран либо товар, либо комплект (не оба, не ни один)"""
|
||||
cleaned_data = super().clean()
|
||||
product = cleaned_data.get('product')
|
||||
product_kit = cleaned_data.get('product_kit')
|
||||
quantity = cleaned_data.get('quantity')
|
||||
|
||||
# Пустая форма - это нормально (будет удалена)
|
||||
if not product and not product_kit:
|
||||
# Обнуляем количество для пустых форм
|
||||
cleaned_data['quantity'] = None
|
||||
return cleaned_data
|
||||
|
||||
# Проверка: нельзя выбрать оба одновременно
|
||||
if product and product_kit:
|
||||
raise forms.ValidationError(
|
||||
'Нельзя указывать одновременно товар и комплект. Выберите что-то одно.'
|
||||
)
|
||||
|
||||
# Проверка: если выбрано что-то, количество обязательно
|
||||
if (product or product_kit):
|
||||
if not quantity or quantity <= 0:
|
||||
raise forms.ValidationError('Необходимо указать количество больше 0')
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
# Formset для inline добавления товаров в заказ
|
||||
OrderItemFormSet = inlineformset_factory(
|
||||
Order,
|
||||
OrderItem,
|
||||
form=OrderItemForm,
|
||||
extra=1, # Одна пустая форма для добавления
|
||||
can_delete=True,
|
||||
min_num=1, # Минимум 1 товар в заказе
|
||||
validate_min=True,
|
||||
)
|
||||
Reference in New Issue
Block a user