Files
octopus/myproject/inventory/views/showcase.py
Andrey Smakotin dd184265ee Add default showcase selection per warehouse
- Add is_default field to Showcase model with unique constraint per warehouse
- Implement Showcase.save() to ensure only one default per warehouse
- Add SetDefaultShowcaseView for AJAX-based default selection
- Update ShowcaseForm to include is_default checkbox
- Add interactive checkbox UI in showcase list with AJAX functionality
- Update POS API to return showcase.is_default instead of warehouse.is_default
- Update terminal.js to auto-select showcase based on its is_default flag
- Add migration for is_default field

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 11:40:08 +03:00

217 lines
8.6 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.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib import messages
from django.shortcuts import render, redirect, get_object_or_404
from django.db.models import Count, Q
from django.views.generic import ListView, CreateView, UpdateView, DeleteView, View
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator
from django.http import JsonResponse
from inventory.models import Showcase, Reservation
from inventory.forms_showcase import ShowcaseForm
@method_decorator(login_required, name='dispatch')
class ShowcaseListView(ListView):
"""
Список всех витрин с группировкой по складам.
Отображает информацию о количестве активных резервов на каждой витрине.
"""
model = Showcase
template_name = 'inventory/showcase/list.html'
context_object_name = 'showcases'
def get_queryset(self):
"""
Получаем витрины с аннотацией количества активных резервов.
Сортируем по складу и названию.
"""
queryset = Showcase.objects.select_related('warehouse').annotate(
active_reservations_count=Count(
'reservations',
filter=Q(reservations__status='reserved')
)
).order_by('warehouse__name', 'name')
# Фильтрация по складу, если указан GET-параметр
warehouse_id = self.request.GET.get('warehouse')
if warehouse_id:
queryset = queryset.filter(warehouse_id=warehouse_id)
# Фильтрация по статусу активности
is_active = self.request.GET.get('is_active')
if is_active == '1':
queryset = queryset.filter(is_active=True)
elif is_active == '0':
queryset = queryset.filter(is_active=False)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Витрины'
# Добавляем информацию для фильтров
from inventory.models import Warehouse
context['warehouses'] = Warehouse.objects.filter(is_active=True).order_by('name')
return context
@method_decorator(login_required, name='dispatch')
class ShowcaseCreateView(CreateView):
"""
Создание новой витрины.
"""
model = Showcase
form_class = ShowcaseForm
template_name = 'inventory/showcase/form.html'
success_url = reverse_lazy('inventory:showcase-list')
def form_valid(self, form):
"""Сохраняем витрину и показываем сообщение об успехе"""
response = super().form_valid(form)
messages.success(
self.request,
f'Витрина "{self.object.name}" успешно создана на складе "{self.object.warehouse.name}"'
)
return response
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Создание витрины'
context['form_title'] = 'Новая витрина'
context['submit_text'] = 'Создать витрину'
return context
@method_decorator(login_required, name='dispatch')
class ShowcaseUpdateView(UpdateView):
"""
Редактирование существующей витрины.
"""
model = Showcase
form_class = ShowcaseForm
template_name = 'inventory/showcase/form.html'
success_url = reverse_lazy('inventory:showcase-list')
def form_valid(self, form):
"""Сохраняем изменения и показываем сообщение об успехе"""
response = super().form_valid(form)
messages.success(
self.request,
f'Витрина "{self.object.name}" успешно обновлена'
)
return response
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'Редактирование витрины: {self.object.name}'
context['form_title'] = f'Редактирование: {self.object.name}'
context['submit_text'] = 'Сохранить изменения'
# Добавляем информацию о резервах на витрине
context['active_reservations_count'] = Reservation.objects.filter(
showcase=self.object,
status='reserved'
).count()
return context
@method_decorator(login_required, name='dispatch')
class ShowcaseDeleteView(DeleteView):
"""
Удаление витрины с подтверждением.
Проверяет наличие активных резервов перед удалением.
"""
model = Showcase
template_name = 'inventory/showcase/delete.html'
success_url = reverse_lazy('inventory:showcase-list')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'Удаление витрины: {self.object.name}'
# Проверяем наличие активных резервов
active_reservations = Reservation.objects.filter(
showcase=self.object,
status='reserved'
).select_related('product')
context['active_reservations'] = active_reservations
context['has_active_reservations'] = active_reservations.exists()
return context
def delete(self, request, *args, **kwargs):
"""Проверяем наличие активных резервов перед удалением"""
showcase = self.get_object()
# Проверка активных резервов
active_reservations_count = Reservation.objects.filter(
showcase=showcase,
status='reserved'
).count()
if active_reservations_count > 0:
messages.error(
request,
f'Невозможно удалить витрину "{showcase.name}": '
f'на ней есть {active_reservations_count} активных резервов. '
'Сначала освободите или продайте зарезервированные товары.'
)
return redirect('inventory:showcase-delete', pk=showcase.pk)
# Удаляем витрину
showcase_name = showcase.name
response = super().delete(request, *args, **kwargs)
messages.success(
request,
f'Витрина "{showcase_name}" успешно удалена'
)
return response
@method_decorator(login_required, name='dispatch')
class SetDefaultShowcaseView(LoginRequiredMixin, View):
"""
Установка витрины по умолчанию для её склада.
Обрабатывает POST запрос от AJAX и возвращает JSON ответ.
"""
def post(self, request, pk):
"""
Установить витрину с заданным pk как витрину по умолчанию для её склада
"""
try:
showcase = get_object_or_404(Showcase, pk=pk, is_active=True)
# Установить эту витрину как по умолчанию
# (метод save() в модели автоматически снимет флаг с других витрин этого склада)
showcase.is_default = True
showcase.save()
return JsonResponse({
'status': 'success',
'message': f'Витрина "{showcase.name}" на складе "{showcase.warehouse.name}" установлена по умолчанию',
'showcase_id': showcase.id,
'showcase_name': showcase.name,
'warehouse_id': showcase.warehouse.id,
'warehouse_name': showcase.warehouse.name
})
except Showcase.DoesNotExist:
return JsonResponse({
'status': 'error',
'message': 'Витрина не найдена'
}, status=404)
except Exception as e:
return JsonResponse({
'status': 'error',
'message': f'Ошибка при установке витрины по умолчанию: {str(e)}'
}, status=500)