fix: Улучшения системы ценообразования комплектов

Исправлены 4 проблемы:
1. Расчёт цены первого товара - улучшена валидация в getProductPrice и calculateFinalPrice
2. Отображение actual_price в Select2 вместо обычной цены
3. Количество по умолчанию = 1 для новых форм компонентов
4. Auto-select текста при клике на поле количества для удобства редактирования

Изменённые файлы:
- products/forms.py: добавлен __init__ в KitItemForm для quantity.initial = 1
- products/templates/includes/select2-product-init.html: обновлена formatSelectResult
- products/templates/productkit_create.html: добавлен focus handler для auto-select
- products/templates/productkit_edit.html: добавлен focus handler для auto-select

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-02 19:04:03 +03:00
parent c84a372f98
commit 6c8af5ab2c
120 changed files with 9035 additions and 3036 deletions

View File

@@ -18,7 +18,7 @@ Inventory Views Package
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from .warehouse import WarehouseListView, WarehouseCreateView, WarehouseUpdateView, WarehouseDeleteView
from .warehouse import WarehouseListView, WarehouseCreateView, WarehouseUpdateView, WarehouseDeleteView, SetDefaultWarehouseView
from .incoming import IncomingListView, IncomingCreateView, IncomingUpdateView, IncomingDeleteView
from .batch import IncomingBatchListView, IncomingBatchDetailView, StockBatchListView, StockBatchDetailView
from .sale import SaleListView, SaleCreateView, SaleUpdateView, SaleDeleteView, SaleDetailView
@@ -46,7 +46,7 @@ __all__ = [
# Home
'inventory_home',
# Warehouse
'WarehouseListView', 'WarehouseCreateView', 'WarehouseUpdateView', 'WarehouseDeleteView',
'WarehouseListView', 'WarehouseCreateView', 'WarehouseUpdateView', 'WarehouseDeleteView', 'SetDefaultWarehouseView',
# Incoming
'IncomingListView', 'IncomingCreateView', 'IncomingUpdateView', 'IncomingDeleteView',
# IncomingBatch

View File

@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
from django.shortcuts import render
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, CreateView, UpdateView, DeleteView, View
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib import messages
from django.http import JsonResponse, HttpResponseRedirect
from django.views.decorators.http import require_http_methods
from django.utils.decorators import method_decorator
from ..models import Warehouse
from ..forms import WarehouseForm
@@ -11,6 +14,7 @@ from ..forms import WarehouseForm
class WarehouseListView(LoginRequiredMixin, ListView):
"""
Список всех складов тенанта
Сортирует по is_default (по умолчанию первым), потом по названию
"""
model = Warehouse
template_name = 'inventory/warehouse/warehouse_list.html'
@@ -18,7 +22,8 @@ class WarehouseListView(LoginRequiredMixin, ListView):
paginate_by = 20
def get_queryset(self):
return Warehouse.objects.filter(is_active=True).order_by('name')
# Сортируем: сначала is_default DESC (по умолчанию первый), потом по названию
return Warehouse.objects.filter(is_active=True).order_by('-is_default', 'name')
class WarehouseCreateView(LoginRequiredMixin, CreateView):
@@ -51,16 +56,61 @@ class WarehouseUpdateView(LoginRequiredMixin, UpdateView):
class WarehouseDeleteView(LoginRequiredMixin, DeleteView):
"""
Удаление склада (мягкое удаление - деактивация)
Удаление склада (мягкое удаление - деактивация).
Вместо физического удаления из БД, устанавливаем is_active=False
"""
model = Warehouse
template_name = 'inventory/warehouse/warehouse_confirm_delete.html'
success_url = reverse_lazy('inventory:warehouse-list')
def form_valid(self, form):
# Мягкое удаление - просто деактивируем
warehouse = self.get_object()
warehouse.is_active = False
warehouse.save()
messages.success(self.request, f'Склад "{warehouse.name}" деактивирован.')
return super().form_valid(form)
def post(self, request, *args, **kwargs):
"""
Переопределяем POST метод чтобы использовать мягкое удаление
вместо стандартного физического удаления Django
"""
self.object = self.get_object()
warehouse_name = self.object.name
# Мягкое удаление - просто деактивируем склад
self.object.is_active = False
self.object.save()
messages.success(request, f'Склад "{warehouse_name}" архивирован и скрыт из списка.')
return HttpResponseRedirect(self.get_success_url())
@method_decorator(require_http_methods(["POST"]), name="dispatch")
class SetDefaultWarehouseView(LoginRequiredMixin, View):
"""
Установка склада по умолчанию
Обрабатывает POST запрос от AJAX и возвращает JSON ответ
"""
def post(self, request, pk):
"""
Установить склад с заданным pk как склад по умолчанию
"""
try:
warehouse = get_object_or_404(Warehouse, pk=pk, is_active=True)
# Установить этот склад как по умолчанию
# (метод save() в модели автоматически снимет флаг с других)
warehouse.is_default = True
warehouse.save()
return JsonResponse({
'status': 'success',
'message': f'Склад "{warehouse.name}" установлен по умолчанию',
'warehouse_id': warehouse.id,
'warehouse_name': warehouse.name
})
except Warehouse.DoesNotExist:
return JsonResponse({
'status': 'error',
'message': 'Склад не найден'
}, status=404)
except Exception as e:
return JsonResponse({
'status': 'error',
'message': str(e)
}, status=500)