- Обновлены начальные миграции для всех приложений - Удалены устаревшие миграции для единиц измерения и SKU - Добавлен новый сервис unit_service.py для управления единицами - Обновлены команды инициализации данных тенанта
148 lines
6.0 KiB
Python
148 lines
6.0 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Сервис для управления единицами измерения.
|
||
|
||
Отвечает за создание и управление справочником единиц измерения (UnitOfMeasure).
|
||
"""
|
||
import logging
|
||
from typing import List, Dict, Any
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class UnitOfMeasureService:
|
||
"""
|
||
Сервис для управления единицами измерения.
|
||
|
||
Предоставляет методы для создания и управления базовыми единицами измерения,
|
||
которые используются в системе для товаров и продаж.
|
||
"""
|
||
|
||
# Базовый набор единиц измерения для новых тенантов
|
||
DEFAULT_UNITS = [
|
||
# Базовые единицы (из старых UNIT_CHOICES)
|
||
{'code': 'шт', 'name': 'Штука', 'short_name': 'шт.', 'position': 1},
|
||
{'code': 'м', 'name': 'Метр', 'short_name': 'м.', 'position': 2},
|
||
{'code': 'г', 'name': 'Грамм', 'short_name': 'г.', 'position': 3},
|
||
{'code': 'л', 'name': 'Литр', 'short_name': 'л.', 'position': 4},
|
||
{'code': 'кг', 'name': 'Килограмм', 'short_name': 'кг.', 'position': 5},
|
||
|
||
# Флористические единицы
|
||
{'code': 'банч', 'name': 'Банч', 'short_name': 'банч', 'position': 10},
|
||
{'code': 'ветка', 'name': 'Ветка', 'short_name': 'вет.', 'position': 11},
|
||
{'code': 'пучок', 'name': 'Пучок', 'short_name': 'пуч.', 'position': 12},
|
||
]
|
||
|
||
@classmethod
|
||
def create_default_units(cls) -> List:
|
||
"""
|
||
Создает базовый набор единиц измерения для тенанта.
|
||
|
||
Использует get_or_create, поэтому безопасно вызывать повторно.
|
||
|
||
Returns:
|
||
List[UnitOfMeasure]: Список созданных/существующих единиц измерения
|
||
"""
|
||
from products.models import UnitOfMeasure
|
||
|
||
created_units = []
|
||
|
||
for unit_data in cls.DEFAULT_UNITS:
|
||
unit, created = UnitOfMeasure.objects.get_or_create(
|
||
code=unit_data['code'],
|
||
defaults={
|
||
'name': unit_data['name'],
|
||
'short_name': unit_data['short_name'],
|
||
'position': unit_data['position'],
|
||
'is_active': True,
|
||
}
|
||
)
|
||
created_units.append(unit)
|
||
|
||
if created:
|
||
logger.debug(f"Создана единица измерения: {unit.code} - {unit.name}")
|
||
|
||
logger.info(f"Инициализация единиц измерения завершена: {len(created_units)} единиц")
|
||
return created_units
|
||
|
||
@classmethod
|
||
def reset_default_units(cls) -> List:
|
||
"""
|
||
Удаляет все единицы измерения и создаёт их заново.
|
||
|
||
ВНИМАНИЕ: Используйте только при инициализации тенанта или в тестах!
|
||
Удаление единиц может нарушить связи с существующими товарами.
|
||
|
||
Returns:
|
||
List[UnitOfMeasure]: Список созданных единиц измерения
|
||
"""
|
||
from products.models import UnitOfMeasure
|
||
|
||
logger.warning("Удаление всех единиц измерения...")
|
||
UnitOfMeasure.objects.all().delete()
|
||
|
||
return cls.create_default_units()
|
||
|
||
@classmethod
|
||
def get_or_create_unit(cls, code: str, name: str, short_name: str,
|
||
position: int = 0) -> tuple:
|
||
"""
|
||
Получает или создаёт единицу измерения.
|
||
|
||
Args:
|
||
code: Уникальный код единицы
|
||
name: Полное название
|
||
short_name: Короткое название для UI
|
||
position: Позиция для сортировки
|
||
|
||
Returns:
|
||
tuple: (UnitOfMeasure, created) - единица и флаг создания
|
||
"""
|
||
from products.models import UnitOfMeasure
|
||
|
||
unit, created = UnitOfMeasure.objects.get_or_create(
|
||
code=code,
|
||
defaults={
|
||
'name': name,
|
||
'short_name': short_name,
|
||
'position': position,
|
||
'is_active': True,
|
||
}
|
||
)
|
||
|
||
if created:
|
||
logger.info(f"Создана единица измерения: {code} - {name}")
|
||
|
||
return unit, created
|
||
|
||
@classmethod
|
||
def get_unit_by_code(cls, code: str):
|
||
"""
|
||
Получает единицу измерения по коду.
|
||
|
||
Args:
|
||
code: Код единицы измерения
|
||
|
||
Returns:
|
||
UnitOfMeasure или None, если не найдена
|
||
"""
|
||
from products.models import UnitOfMeasure
|
||
|
||
try:
|
||
return UnitOfMeasure.objects.get(code=code, is_active=True)
|
||
except UnitOfMeasure.DoesNotExist:
|
||
logger.warning(f"Единица измерения с кодом '{code}' не найдена")
|
||
return None
|
||
|
||
@classmethod
|
||
def get_active_units(cls) -> List:
|
||
"""
|
||
Возвращает все активные единицы измерения.
|
||
|
||
Returns:
|
||
List[UnitOfMeasure]: Список активных единиц, отсортированных по position
|
||
"""
|
||
from products.models import UnitOfMeasure
|
||
|
||
return list(UnitOfMeasure.objects.filter(is_active=True).order_by('position', 'code'))
|