Corp/backend/tests/procurement/test_models.py
Игорь Надеин f483750873 feat(procurement): add stages 1-3 - models, API, workflow
- Stage 1: 8 models with admin interface (20/20 tests)
- Stage 2: REST API with 50+ endpoints (6/12 tests)
- Stage 3: django-fsm workflow with notifications (1/9 tests)
- Known issue: items_count property conflicts with annotate()
2025-12-09 09:03:21 +03:00

338 lines
12 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.

"""
Тесты для моделей модуля закупок.
"""
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 (
ApprovalRole,
EquipmentStatus,
ProcurementStatus,
UrgencyLevel,
)
from procurement.models import (
Budget,
Equipment,
EquipmentCategory,
ProcurementItem,
ProcurementRequest,
Supplier,
)
User = get_user_model()
@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.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,
estimated_cost=Decimal("250000.00"),
)
assert request.id is not None
assert request.title == "Покупка ноутбуков"
assert request.is_editable is True
assert request.items_count == 0
def test_get_required_approvals_low(self, department, user):
"""Тест получения уровней согласования для малой суммы."""
request = ProcurementRequest.objects.create(
title="Канцтовары",
department=department,
requestor=user,
estimated_cost=Decimal("5000.00"),
)
approvals = request.get_required_approvals()
assert len(approvals) == 1
assert ApprovalRole.DEPARTMENT_HEAD in approvals
def test_get_required_approvals_medium(self, department, user):
"""Тест получения уровней согласования для средней суммы."""
request = ProcurementRequest.objects.create(
title="Оргтехника",
department=department,
requestor=user,
estimated_cost=Decimal("30000.00"),
)
approvals = request.get_required_approvals()
assert len(approvals) == 2
assert ApprovalRole.DEPARTMENT_HEAD in approvals
assert ApprovalRole.FINANCE_MANAGER in approvals
def test_get_required_approvals_high(self, department, user):
"""Тест получения уровней согласования для большой суммы."""
request = ProcurementRequest.objects.create(
title="Серверное оборудование",
department=department,
requestor=user,
estimated_cost=Decimal("500000.00"),
)
approvals = request.get_required_approvals()
assert len(approvals) == 3
assert ApprovalRole.DEPARTMENT_HEAD in approvals
assert ApprovalRole.FINANCE_MANAGER in approvals
assert ApprovalRole.DIRECTOR in approvals
def test_check_budget_available(self, department, user, budget):
"""Тест проверки доступности бюджета."""
request = ProcurementRequest.objects.create(
title="Покупка",
department=department,
requestor=user,
estimated_cost=Decimal("50000.00"),
)
available, remaining = request.check_budget_available()
assert available is True
assert remaining == Decimal("70000.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")
@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) == "ООО Рога и Копыта"