feat: упростить создание заказов и рефакторинг единиц измерения

- Добавить inline-редактирование цен в списке товаров
- Оптимизировать карточки товаров в POS-терминале
- Рефакторинг моделей единиц измерения
- Миграция unit -> base_unit в SalesUnit
- Улучшить UI форм создания/редактирования товаров

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-17 03:34:43 +03:00
parent 928b340486
commit 2f1f0621e6
24 changed files with 1079 additions and 227 deletions

View File

@@ -766,7 +766,8 @@ def get_items_api(request):
)
).prefetch_related(
'categories',
first_product_photo
first_product_photo,
'sales_units' # Загружаем единицы продажи для POS
)
# Фильтруем по категории, если указана
@@ -794,11 +795,34 @@ def get_items_api(request):
available = p.available_qty
reserved = p.reserved_qty
free_qty = available - reserved
# Подсчитываем активные единицы продажи
sales_units_count = p.sales_units.filter(is_active=True).count()
# Получаем активные единицы продажи
active_sales_units = [su for su in p.sales_units.all() if su.is_active]
sales_units_count = len(active_sales_units)
has_sales_units = sales_units_count > 0
# Находим единицу продажи по умолчанию
default_sales_unit = None
available_qty_in_unit = free_qty # Количество в единицах продажи
price_in_unit = str(p.actual_price) # Цена в единицах продажи
if has_sales_units:
# Ищем единицу с is_default=True или берем первую активную
default_unit = next((su for su in active_sales_units if su.is_default), active_sales_units[0])
if default_unit and default_unit.conversion_factor and default_unit.conversion_factor > 0:
# Конвертируем свободное количество в единицы продажи
available_qty_in_unit = free_qty * default_unit.conversion_factor
price_in_unit = str(default_unit.actual_price)
default_sales_unit = {
'id': default_unit.id,
'name': default_unit.name,
'price': str(default_unit.actual_price),
'conversion_factor': str(default_unit.conversion_factor),
'min_quantity': str(default_unit.min_quantity),
'quantity_step': str(default_unit.quantity_step),
'is_default': default_unit.is_default
}
products.append({
'id': p.id,
'name': p.name,
@@ -811,9 +835,12 @@ def get_items_api(request):
'available_qty': str(available),
'reserved_qty': str(reserved),
'free_qty': str(free_qty), # Передаём как строку для сохранения точности
'free_qty_sort': float(free_qty), # Для сортировки отдельное поле
'free_qty_sort': float(available_qty_in_unit if has_sales_units and default_sales_unit else free_qty), # Для сортировки
'sales_units_count': sales_units_count,
'has_sales_units': has_sales_units
'has_sales_units': has_sales_units,
'default_sales_unit': default_sales_unit,
'available_qty_in_unit': str(available_qty_in_unit),
'price_in_unit': price_in_unit
})
# Prefetch для первого фото комплектов