Files
octopus/myproject/products/services/unit_service.py

149 lines
6.1 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 -*-
"""
Сервис для управления единицами измерения.
Отвечает за создание и управление справочником единиц измерения (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},
{'code': 'коробка', 'name': 'Коробка', 'short_name': 'кор.', 'position': 13},
]
@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'))