183 lines
7.6 KiB
Python
183 lines
7.6 KiB
Python
from django.views.generic import ListView, DetailView, DetailView
|
||
from programmer.mixins import MenuContextMixin, BreadcrumbMixin
|
||
from .models import Product, ProductCategory, Configuration
|
||
from django.shortcuts import get_object_or_404
|
||
from django.http import Http404
|
||
from programmer.forms import CallbackForm
|
||
from .forms import OrderForm
|
||
from django.contrib import messages
|
||
from django.shortcuts import redirect
|
||
from programmer.utils.email_notifications import send_order_notification, send_order_notification_async
|
||
from django.utils.decorators import method_decorator
|
||
from django.contrib.admin.views.decorators import staff_member_required
|
||
|
||
|
||
class ProductListView(MenuContextMixin, BreadcrumbMixin, ListView):
|
||
model = Product
|
||
template_name = 'products/product_list.html'
|
||
context_object_name = 'products'
|
||
paginate_by = 12
|
||
|
||
def get_breadcrumbs(self):
|
||
return [{'title': 'Продукты', 'url_name': None}]
|
||
|
||
def get_queryset(self):
|
||
qs = (
|
||
Product.objects
|
||
.filter(is_published=True)
|
||
.select_related('category')
|
||
.prefetch_related('configurations')
|
||
)
|
||
|
||
# Фильтр по категории
|
||
category_slug = self.request.GET.get('category')
|
||
if category_slug:
|
||
self.current_category = get_object_or_404(ProductCategory, slug=category_slug)
|
||
qs = qs.filter(category=self.current_category)
|
||
else:
|
||
self.current_category = None
|
||
|
||
# Фильтр по конфигурации
|
||
config_slug = self.request.GET.get('configuration')
|
||
if config_slug:
|
||
self.current_configuration = get_object_or_404(Configuration, slug=config_slug)
|
||
qs = qs.filter(configurations=self.current_configuration)
|
||
else:
|
||
self.current_configuration = None
|
||
|
||
# ordering уже задан в Meta, явный вызов order_by не нужен,
|
||
# но оставляем для явности и возможного расширения сортировки
|
||
return qs.order_by('-time_create')
|
||
|
||
def get_context_data(self, **kwargs):
|
||
context = super().get_context_data(**kwargs)
|
||
context['categories'] = ProductCategory.objects.all()
|
||
context['configurations'] = Configuration.objects.all()
|
||
context['current_category'] = self.current_category
|
||
context['current_configuration'] = self.current_configuration
|
||
context['title'] = "Продукты 1С | Модули и расширения | С.Н.А. Технологии"
|
||
context['meta_description'] = (
|
||
"Готовые модули, расширения и программные продукты для 1С: Предприятие. "
|
||
"Фильтрация по конфигурациям и категориям."
|
||
)
|
||
context['meta_keywords'] = "1С модули, расширения 1С, программы 1С, купить 1С, продукты 1С"
|
||
form = CallbackForm()
|
||
|
||
if self.request.user.is_authenticated:
|
||
if 'captcha' in form.fields:
|
||
del form.fields['captcha']
|
||
|
||
context.update({
|
||
'form': form,
|
||
})
|
||
|
||
|
||
return context
|
||
|
||
|
||
class ProductDetailView(MenuContextMixin, BreadcrumbMixin, DetailView):
|
||
model = Product
|
||
template_name = 'products/product_detail.html'
|
||
context_object_name = 'product'
|
||
|
||
def get_object(self, queryset=None):
|
||
product = super().get_object(queryset=queryset)
|
||
if not product.is_published:
|
||
raise Http404("Продукт не найден")
|
||
return product
|
||
|
||
def get_breadcrumbs(self):
|
||
product = self.object
|
||
return [
|
||
{'title': 'Продукты', 'url_name': 'products:product_list'},
|
||
{
|
||
'title': product.category.name,
|
||
'url_name': 'products:product_list',
|
||
'extra_params': f'?category={product.category.slug}',
|
||
},
|
||
{'title': product.title, 'url_name': None},
|
||
]
|
||
|
||
def get_context_data(self, **kwargs):
|
||
context = super().get_context_data(**kwargs)
|
||
product = self.object
|
||
context['title'] = product.get_seo_title()
|
||
context['meta_description'] = product.get_seo_description()
|
||
context['meta_keywords'] = (
|
||
f"1С, {product.category.name}, "
|
||
f"{', '.join(c.name for c in product.configurations.all())}"
|
||
)
|
||
|
||
# Форма заказа с предзаполнением для авторизованных
|
||
initial = {}
|
||
if self.request.user.is_authenticated:
|
||
user = self.request.user
|
||
initial['name'] = user.get_full_name() or user.username
|
||
initial['email'] = user.email
|
||
# Если есть профиль и телефон
|
||
if hasattr(user, 'profile') and user.profile.phone:
|
||
initial['phone'] = user.profile.phone
|
||
|
||
order_form = OrderForm(initial=initial)
|
||
order_form.fields['configuration'].queryset = product.configurations.all()
|
||
context['order_form'] = order_form
|
||
form = CallbackForm()
|
||
|
||
if self.request.user.is_authenticated:
|
||
if 'captcha' in form.fields:
|
||
del form.fields['captcha']
|
||
|
||
context.update({
|
||
'form': form,
|
||
})
|
||
|
||
return context
|
||
|
||
|
||
@method_decorator(staff_member_required, name='dispatch')
|
||
class ProductDraftPreviewView(DetailView):
|
||
model = Product
|
||
template_name = 'products/product_detail.html'
|
||
context_object_name = 'product'
|
||
|
||
def get_queryset(self):
|
||
# Показываем все продукты (включая неопубликованные) только для персонала
|
||
return Product.objects.all()
|
||
|
||
|
||
def order_create(request, slug):
|
||
product = get_object_or_404(Product, slug=slug, is_published=True)
|
||
|
||
if request.method == 'POST':
|
||
form = OrderForm(request.POST)
|
||
# Переопределяем queryset для поля configuration – только те, что связаны с продуктом
|
||
form.fields['configuration'].queryset = product.configurations.all()
|
||
|
||
if form.is_valid():
|
||
order = form.save(commit=False)
|
||
order.product = product
|
||
# Если пользователь авторизован, связываем заказ с ним
|
||
if request.user.is_authenticated:
|
||
order.user = request.user
|
||
# Можно предзаполнить имя и email из профиля, если они пустые в форме
|
||
if not order.name:
|
||
order.name = request.user.get_full_name() or request.user.username
|
||
if not order.email:
|
||
order.email = request.user.email
|
||
order.save()
|
||
|
||
# Сохраняем выбранную конфигурацию (уже сохранена в форме)
|
||
# success = send_order_notification(order)
|
||
# if success:
|
||
# order.notification_sent = True
|
||
# order.save(update_fields=['notification_sent'])
|
||
send_order_notification_async(order)
|
||
|
||
messages.success(request, '✅ Ваш заказ принят! Мы свяжемся с вами в ближайшее время.')
|
||
return redirect('products:product_detail', slug=slug)
|
||
else:
|
||
for field, errors in form.errors.items():
|
||
for error in errors:
|
||
messages.error(request, f'❌ Ошибка в поле "{field}": {error}')
|
||
return redirect('products:product_detail', slug=slug)
|