Files
octopus/myproject/orders/forms.py
Andrey Smakotin 2bf2afb56f 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>
2025-11-07 10:44:46 +03:00

157 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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,
)