Files
octopus/myproject/products/admin_displays.py
Andrey Smakotin 6c8af5ab2c 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>
2025-11-02 19:04:03 +03:00

266 lines
8.9 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.
"""
Визуальные компоненты для отображения качества фотографий в Django админке.
Модулю используется для форматирования вывода уровней качества фото
с цветами, иконками и подсказками.
"""
from django.conf import settings
from django.utils.html import format_html
def get_quality_color(quality_level):
"""
Получить цвет Bootstrap для уровня качества.
Args:
quality_level (str): Уровень качества
Returns:
str: CSS цвет (success/info/warning/danger)
"""
labels = getattr(settings, 'IMAGE_QUALITY_LABELS', {})
info = labels.get(quality_level, {})
return info.get('color', 'secondary')
def get_quality_label(quality_level):
"""
Получить человеко-читаемое название уровня качества.
Args:
quality_level (str): Уровень качества
Returns:
str: Название (например, "Отлично")
"""
labels = getattr(settings, 'IMAGE_QUALITY_LABELS', {})
info = labels.get(quality_level, {})
return info.get('label', 'Неизвестно')
def get_quality_icon(quality_level):
"""
Получить иконку для уровня качества.
Args:
quality_level (str): Уровень качества
Returns:
str: Иконка (✓, ◐, ⚠, ✗)
"""
labels = getattr(settings, 'IMAGE_QUALITY_LABELS', {})
info = labels.get(quality_level, {})
return info.get('icon', '?')
def format_quality_badge(quality_level, show_icon=True):
"""
Форматирует уровень качества в виде цветного бэджа Bootstrap.
Пример вывода: [🟢 Отлично] или [🔴 Плохо]
Args:
quality_level (str): Уровень качества (excellent/good/acceptable/poor/very_poor)
show_icon (bool): Показывать ли иконку
Returns:
str: HTML с отформатированным бэджем
"""
labels = getattr(settings, 'IMAGE_QUALITY_LABELS', {})
info = labels.get(quality_level, {})
label_text = info.get('label', 'Неизвестно')
color = info.get('color', 'secondary')
icon = info.get('icon', '?')
description = info.get('description', '')
# Формируем текст бэджа
if show_icon:
badge_text = f"{icon} {label_text}"
else:
badge_text = label_text
# Выбираем CSS класс Bootstrap
badge_class = info.get('badge_class', 'badge-secondary')
# Создаем HTML с tooltip при наведении
html = format_html(
'<span class="badge {} " title="{}" style="font-size: 13px; padding: 6px 10px; cursor: help;">{}</span>',
badge_class,
description,
badge_text
)
return html
def format_quality_badge_with_size(quality_level, width=None, height=None):
"""
Форматирует качество с указанием размеров изображения.
Пример: "🟢 Отлично (2150×2150px)"
Args:
quality_level (str): Уровень качества
width (int): Ширина изображения (опционально)
height (int): Высота изображения (опционально)
Returns:
str: HTML с бэджем и размерами
"""
label_text = get_quality_label(quality_level)
icon = get_quality_icon(quality_level)
color = get_quality_color(quality_level)
size_text = ""
if width and height:
size_text = f" ({width}×{height}px)"
labels = getattr(settings, 'IMAGE_QUALITY_LABELS', {})
info = labels.get(quality_level, {})
badge_class = info.get('badge_class', 'badge-secondary')
html = format_html(
'<span class="badge {}" style="font-size: 13px; padding: 6px 10px;">{} {}{}</span>',
badge_class,
icon,
label_text,
size_text
)
return html
def format_quality_display(quality_level, width=None, height=None, warning=False):
"""
Полное отображение качества с индикатором warning.
Args:
quality_level (str): Уровень качества
width (int): Ширина изображения (опционально)
height (int): Высота изображения (опционально)
warning (bool): Требует ли обновления
Returns:
str: HTML с полной информацией о качестве
"""
badge = format_quality_badge_with_size(quality_level, width, height)
if warning:
# Добавляем индикатор warning
warning_indicator = format_html(
' <span style="color: #ff6b6b; font-weight: bold;" title="Требует обновления перед выгрузкой на сайт">⚠️ Требует обновления</span>'
)
return format_html('{} {}', badge, warning_indicator)
return badge
def format_photo_quality_column(obj, show_size=True):
"""
Для использования в list_display - отображает качество фотографии объекта.
Пример использования:
def photo_quality(self, obj):
return format_photo_quality_column(obj)
photo_quality.short_description = 'Качество фото'
Args:
obj: Product, ProductKit или ProductCategory объект
show_size (bool): Показывать ли размеры
Returns:
str: HTML с качеством первого фото
"""
first_photo = obj.photos.first()
if not first_photo:
return format_html('<span style="color: #999;">Нет фото</span>')
if show_size:
return format_quality_display(
first_photo.quality_level,
width=first_photo.width if hasattr(first_photo, 'width') else None,
height=first_photo.height if hasattr(first_photo, 'height') else None,
warning=first_photo.quality_warning
)
else:
return format_quality_badge(first_photo.quality_level)
def format_photo_inline_quality(photo_obj):
"""
Для использования в inline таблицах - отображает качество фото в строке.
Args:
photo_obj: ProductPhoto, ProductKitPhoto или ProductCategoryPhoto объект
Returns:
str: HTML с качеством фото
"""
if not photo_obj.pk:
# Новый объект еще не сохранён
return format_html('<span style="color: #999;">Сохраните фото</span>')
return format_quality_display(
photo_obj.quality_level,
width=photo_obj.width if hasattr(photo_obj, 'width') else None,
height=photo_obj.height if hasattr(photo_obj, 'height') else None,
warning=photo_obj.quality_warning
)
def format_photo_preview_with_quality(photo_obj, max_width=250, max_height=250):
"""
Превью фотографии с индикатором качества под ней.
Args:
photo_obj: ProductPhoto, ProductKitPhoto или ProductCategoryPhoto объект
max_width (int): Максимальная ширина превью
max_height (int): Максимальная высота превью
Returns:
str: HTML с фото и индикатором качества
"""
if not photo_obj.image:
return format_html('<span style="color: #999;">Нет изображения</span>')
quality_display = format_quality_badge(photo_obj.quality_level)
html = format_html(
'<div style="text-align: center;">'
'<img src="{}" style="max-width: {}px; max-height: {}px; border-radius: 4px; margin-bottom: 8px;" />'
'<div>{}</div>'
'</div>',
photo_obj.get_large_url() if hasattr(photo_obj, 'get_large_url') else photo_obj.image.url,
max_width,
max_height,
quality_display
)
return html
def get_quality_filter_display(value):
"""
Получить описание фильтра для качества фото.
Args:
value (str): Значение фильтра (excellent/good/acceptable/poor/very_poor/warning/no_warning)
Returns:
str: Описание для отображения
"""
filter_descriptions = {
'excellent': '🟢 Отлично',
'good': '🟡 Хорошо',
'acceptable': '🟠 Приемлемо',
'poor': '🔴 Плохо',
'very_poor': '🔴 Очень плохо',
'warning': '⚠️ Требует обновления',
'no_warning': '✓ Готово к выгрузке',
}
return filter_descriptions.get(value, value)