Corp/backend/tests/procurement/test_models.py
2026-09-07 19:43:48 +03:00

419 lines
14 KiB
Python
Raw Permalink 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.

"""
Тесты для моделей модуля закупок.
"""
from datetime import date, timedelta
from decimal import Decimal
import pytest
from django.contrib.auth import get_user_model
from employees.models import Department
from procurement.constants import (
EquipmentStatus,
ProcurementStatus,
UrgencyLevel,
)
from procurement.models import (
ApprovalRoute,
Budget,
Equipment,
EquipmentCategory,
ProcurementItem,
ProcurementRequest,
Supplier,
)
User = get_user_model()
HEAD_PRIORITY = 1
FINANCE_PRIORITY = 2
DIRECTOR_PRIORITY = 3
@pytest.fixture
def department(db):
"""Создать тестовый отдел."""
return Department.objects.create(name="Тестовый отдел", description="Для тестов")
@pytest.fixture
def user(db):
"""Создать тестового пользователя."""
return User.objects.create_user(
email="test@example.com",
password="testpass123",
phone_number="+79991234567",
send_activation_email=False,
first_name="Тест",
last_name="Тестов",
)
@pytest.fixture
def budget(db, department):
"""Создать тестовый бюджет."""
# Создаем бюджет для текущего квартала
from django.utils import timezone
now = timezone.now()
quarter = (now.month - 1) // 3 + 1
return Budget.objects.create(
department=department,
year=now.year,
quarter=quarter,
allocated_amount=Decimal("100000.00"),
spent_amount=Decimal("30000.00"),
)
@pytest.fixture
def approval_routes(db, user):
ApprovalRoute.objects.create(
priority=HEAD_PRIORITY,
resolver_type=ApprovalRoute.ResolverType.DEPARTMENT_HEAD,
)
ApprovalRoute.objects.create(
priority=FINANCE_PRIORITY,
min_amount=Decimal("10000.00"),
resolver_type=ApprovalRoute.ResolverType.FIXED_EMPLOYEE,
employee=user,
)
ApprovalRoute.objects.create(
priority=DIRECTOR_PRIORITY,
min_amount=Decimal("50000.00"),
resolver_type=ApprovalRoute.ResolverType.FIXED_EMPLOYEE,
employee=user,
)
@pytest.mark.django_db
class TestProcurementRequest:
"""Тесты модели ProcurementRequest."""
def test_create_procurement_request(self, department, user):
"""Тест создания заявки на закупку."""
request = ProcurementRequest.objects.create(
title="Покупка ноутбуков",
description="Нужны 5 ноутбуков для отдела",
department=department,
requestor=user,
status=ProcurementStatus.DRAFT,
urgency=UrgencyLevel.MEDIUM,
)
assert request.id is not None
assert request.title == "Покупка ноутбуков"
assert request.is_editable is True
assert request.items_count == 0
def test_get_required_approval_priorities_low(
self, department, user, approval_routes
):
"""Тест получения уровней согласования для малой суммы."""
request = ProcurementRequest.objects.create(
title="Канцтовары",
department=department,
requestor=user,
)
ProcurementItem.objects.create(
request=request,
name="Бумага",
quantity=1,
unit="шт",
estimated_unit_price=Decimal("5000.00"),
)
approvals = request.get_required_approval_priorities()
assert len(approvals) == 1
assert HEAD_PRIORITY in approvals
def test_get_required_approval_priorities_medium(
self, department, user, approval_routes
):
"""Тест получения уровней согласования для средней суммы (10k-50k)."""
request = ProcurementRequest.objects.create(
title="Оргтехника",
department=department,
requestor=user,
)
# Добавляем позицию для средней суммы (30000)
ProcurementItem.objects.create(
request=request,
name="Принтер",
quantity=1,
unit="шт",
estimated_unit_price=Decimal("30000.00"),
)
approvals = request.get_required_approval_priorities()
assert len(approvals) == 2
assert HEAD_PRIORITY in approvals
assert FINANCE_PRIORITY in approvals
def test_get_required_approval_priorities_high(
self, department, user, approval_routes
):
"""Тест получения уровней согласования для большой суммы (>50k)."""
request = ProcurementRequest.objects.create(
title="Серверное оборудование",
department=department,
requestor=user,
)
# Добавляем позицию для большой суммы (500000)
ProcurementItem.objects.create(
request=request,
name="Сервер",
quantity=1,
unit="шт",
estimated_unit_price=Decimal("500000.00"),
)
approvals = request.get_required_approval_priorities()
assert len(approvals) == 3
assert HEAD_PRIORITY in approvals
assert FINANCE_PRIORITY in approvals
assert DIRECTOR_PRIORITY in approvals
def test_total_cost_uses_actual_price_before_estimated(self, department, user):
"""Фактическая цена при расчете заявки приоритетнее ориентировочной."""
request = ProcurementRequest.objects.create(
title="Уточненная закупка",
department=department,
requestor=user,
)
ProcurementItem.objects.create(
request=request,
name="Позиция с фактической ценой",
quantity=2,
unit="шт",
estimated_unit_price=Decimal("100.00"),
actual_unit_price=Decimal("80.00"),
)
ProcurementItem.objects.create(
request=request,
name="Позиция без фактической цены",
quantity=3,
unit="шт",
estimated_unit_price=Decimal("50.00"),
)
ProcurementItem.objects.create(
request=request,
name="Позиция без цены",
quantity=4,
unit="шт",
)
assert request.total_cost == Decimal("310.00")
def test_is_editable_draft(self, department, user):
"""Тест: черновик можно редактировать."""
request = ProcurementRequest.objects.create(
title="Черновик",
department=department,
requestor=user,
status=ProcurementStatus.DRAFT,
)
assert request.is_editable is True
def test_is_not_editable_pending(self, department, user):
"""Тест: заявку на согласовании редактировать нельзя."""
request = ProcurementRequest.objects.create(
title="На согласовании",
department=department,
requestor=user,
status=ProcurementStatus.PENDING,
)
assert request.is_editable is False
@pytest.mark.django_db
class TestProcurementItem:
"""Тесты модели ProcurementItem."""
def test_create_procurement_item(self, department, user):
"""Тест создания позиции заявки."""
request = ProcurementRequest.objects.create(
title="Заявка", department=department, requestor=user
)
item = ProcurementItem.objects.create(
request=request,
name="Ноутбук Dell XPS 15",
quantity=3,
unit="шт",
estimated_unit_price=Decimal("80000.00"),
)
assert item.id is not None
assert item.total_price == Decimal("240000.00")
assert request.items_count == 1
def test_total_price_calculation(self, department, user):
"""Тест расчета общей стоимости позиции."""
request = ProcurementRequest.objects.create(
title="Заявка", department=department, requestor=user
)
item = ProcurementItem.objects.create(
request=request,
name="Кабель HDMI",
quantity=10,
unit="шт",
estimated_unit_price=Decimal("500.50"),
)
assert item.total_price == Decimal("5005.00")
def test_total_price_prefers_actual_unit_price(self, department, user):
"""Фактическая цена приоритетнее ориентировочной в сумме позиции."""
request = ProcurementRequest.objects.create(
title="Заявка", department=department, requestor=user
)
item = ProcurementItem.objects.create(
request=request,
name="Кабель HDMI",
quantity=10,
unit="шт",
estimated_unit_price=Decimal("500.50"),
actual_unit_price=Decimal("450.00"),
)
assert item.total_price == Decimal("4500.00")
@pytest.mark.django_db
class TestBudget:
"""Тесты модели Budget."""
def test_create_budget(self, department):
"""Тест создания бюджета."""
budget = Budget.objects.create(
department=department,
year=2025,
quarter=2,
allocated_amount=Decimal("200000.00"),
)
assert budget.id is not None
assert budget.remaining_amount == Decimal("200000.00")
assert budget.utilization_percentage == Decimal("0.00")
def test_remaining_amount(self, budget):
"""Тест расчета остатка бюджета."""
assert budget.remaining_amount == Decimal("70000.00")
def test_utilization_percentage(self, budget):
"""Тест расчета процента использования."""
assert budget.utilization_percentage == Decimal("30.00")
def test_can_spend_true(self, budget):
"""Тест: можно потратить сумму в пределах бюджета."""
assert budget.can_spend(Decimal("50000.00")) is True
def test_can_spend_false(self, budget):
"""Тест: нельзя потратить сумму сверх бюджета."""
assert budget.can_spend(Decimal("80000.00")) is False
@pytest.mark.django_db
class TestEquipment:
"""Тесты модели Equipment."""
def test_create_equipment(self, department, user):
"""Тест создания оборудования."""
category = EquipmentCategory.objects.create(name="Компьютеры")
equipment = Equipment.objects.create(
name="Ноутбук Dell",
inventory_number="INV-2025-0001",
serial_number="SN123456",
category=category,
status=EquipmentStatus.AVAILABLE,
department=department,
responsible_person=user,
purchase_date=date.today(),
warranty_until=date.today() + timedelta(days=365),
purchase_cost=Decimal("80000.00"),
)
assert equipment.id is not None
assert equipment.is_under_warranty is True
assert str(equipment) == "INV-2025-0001 - Ноутбук Dell"
def test_is_under_warranty_true(self, department, user):
"""Тест: оборудование на гарантии."""
category = EquipmentCategory.objects.create(name="Техника")
equipment = Equipment.objects.create(
name="Принтер",
inventory_number="INV-2025-0002",
category=category,
department=department,
purchase_date=date.today(),
warranty_until=date.today() + timedelta(days=30),
purchase_cost=Decimal("20000.00"),
)
assert equipment.is_under_warranty is True
def test_is_under_warranty_false(self, department, user):
"""Тест: гарантия истекла."""
category = EquipmentCategory.objects.create(name="Техника")
equipment = Equipment.objects.create(
name="Старый принтер",
inventory_number="INV-2024-0001",
category=category,
department=department,
purchase_date=date.today() - timedelta(days=400),
warranty_until=date.today() - timedelta(days=35),
purchase_cost=Decimal("20000.00"),
)
assert equipment.is_under_warranty is False
@pytest.mark.django_db
class TestEquipmentCategory:
"""Тесты модели EquipmentCategory."""
def test_create_category(self):
"""Тест создания категории."""
category = EquipmentCategory.objects.create(
name="Оргтехника", description="Принтеры, сканеры"
)
assert category.id is not None
assert category.full_path == "Оргтехника"
def test_hierarchical_categories(self):
"""Тест иерархических категорий."""
parent = EquipmentCategory.objects.create(name="Компьютеры")
child = EquipmentCategory.objects.create(name="Ноутбуки", parent=parent)
assert child.full_path == "Компьютеры → Ноутбуки"
assert str(child) == "Компьютеры → Ноутбуки"
@pytest.mark.django_db
class TestSupplier:
"""Тесты модели Supplier."""
def test_create_supplier(self):
"""Тест создания поставщика."""
supplier = Supplier.objects.create(
name="ООО Рога и Копыта",
contact_person="Иван Иванов",
phone="+7 (999) 123-45-67",
email="info@rogakopyta.ru",
inn="1234567890",
rating=Decimal("4.50"),
)
assert supplier.id is not None
assert supplier.is_active is True
assert str(supplier) == "ООО Рога и Копыта"