358 lines
13 KiB
Python
358 lines
13 KiB
Python
"""
|
||
Тесты для Inventory - transfer, write_off и инвентарные номера.
|
||
"""
|
||
|
||
import pytest
|
||
from rest_framework import status
|
||
|
||
from procurement.constants import EquipmentStatus
|
||
from procurement.models import (
|
||
Equipment,
|
||
EquipmentCategory,
|
||
EquipmentTransferLog,
|
||
MaintenanceRecord,
|
||
)
|
||
from procurement.services import InventoryNumberGenerator
|
||
|
||
|
||
@pytest.mark.django_db
|
||
class TestInventoryNumberGenerator:
|
||
"""Тесты для генератора инвентарных номеров."""
|
||
|
||
@pytest.fixture(autouse=True)
|
||
def setup(self, department_factory, user_factory, link_factory):
|
||
"""Настройка тестовых данных."""
|
||
self.department = department_factory(name="IT Inventory Test")
|
||
self.user = user_factory(
|
||
staff=True,
|
||
email="inventory_user@test.com",
|
||
first_name="Test",
|
||
last_name="Inventory",
|
||
)
|
||
link_factory(self.user, self.department, is_active=True)
|
||
self.category = EquipmentCategory.objects.create(
|
||
name="Computers",
|
||
description="Компьютеры и ноутбуки",
|
||
)
|
||
|
||
def test_generate_first_number(self):
|
||
"""Тест: генерация первого инвентарного номера за год."""
|
||
from django.utils import timezone
|
||
|
||
year = timezone.now().year
|
||
|
||
number = InventoryNumberGenerator.generate()
|
||
|
||
assert number == f"INV-{year}-0001"
|
||
|
||
def test_generate_sequential_numbers(self):
|
||
"""Тест: последовательная генерация номеров."""
|
||
from django.utils import timezone
|
||
|
||
year = timezone.now().year
|
||
|
||
# Создаём оборудование с первым номером
|
||
Equipment.objects.create(
|
||
name="Laptop 1",
|
||
inventory_number=f"INV-{year}-0005",
|
||
category=self.category,
|
||
department=self.department,
|
||
responsible_person=self.user,
|
||
status=EquipmentStatus.IN_USE,
|
||
purchase_date="2025-01-01",
|
||
purchase_cost="10000.00",
|
||
)
|
||
|
||
number = InventoryNumberGenerator.generate()
|
||
|
||
assert number == f"INV-{year}-0006"
|
||
|
||
def test_validate_correct_format(self):
|
||
"""Тест: валидация правильного формата."""
|
||
assert InventoryNumberGenerator.validate("INV-2025-0001") is True
|
||
assert InventoryNumberGenerator.validate("INV-2024-9999") is True
|
||
|
||
def test_validate_incorrect_format(self):
|
||
"""Тест: валидация неправильного формата."""
|
||
assert InventoryNumberGenerator.validate("INV-2025") is False
|
||
assert InventoryNumberGenerator.validate("ABC-2025-0001") is False
|
||
assert InventoryNumberGenerator.validate("") is False
|
||
|
||
|
||
@pytest.mark.django_db
|
||
class TestEquipmentTransferEndpoint:
|
||
"""Тесты для /equipment/{id}/transfer/ endpoint."""
|
||
|
||
@pytest.fixture(autouse=True)
|
||
def setup(self, api_client, department_factory, user_factory, link_factory):
|
||
"""Настройка тестовых данных."""
|
||
self.client = api_client
|
||
self.dept1 = department_factory(name="IT Dept")
|
||
self.dept2 = department_factory(name="HR Dept")
|
||
|
||
self.user1 = user_factory(
|
||
staff=True,
|
||
email="transfer_user1@test.com",
|
||
first_name="User",
|
||
last_name="One",
|
||
)
|
||
self.user2 = user_factory(
|
||
email="transfer_user2@test.com",
|
||
first_name="User",
|
||
last_name="Two",
|
||
)
|
||
link_factory(self.user1, self.dept1, is_active=True)
|
||
link_factory(self.user2, self.dept2, is_active=True)
|
||
|
||
self.category = EquipmentCategory.objects.create(
|
||
name="Laptops",
|
||
description="Ноутбуки",
|
||
)
|
||
self.equipment = Equipment.objects.create(
|
||
name="MacBook Pro",
|
||
inventory_number="INV-2025-0200",
|
||
category=self.category,
|
||
department=self.dept1,
|
||
responsible_person=self.user1,
|
||
status=EquipmentStatus.IN_USE,
|
||
purchase_date="2025-01-01",
|
||
purchase_cost="10000.00",
|
||
)
|
||
|
||
def test_transfer_to_another_department(self):
|
||
"""Тест: перевод оборудования в другой отдел."""
|
||
self.client.force_authenticate(user=self.user1)
|
||
|
||
response = self.client.post(
|
||
f"/api/v1/procurement/equipment/{self.equipment.id}/transfer/",
|
||
{
|
||
"to_department": self.dept2.id,
|
||
"reason": "Переезд сотрудника",
|
||
},
|
||
)
|
||
|
||
assert response.status_code == status.HTTP_200_OK
|
||
assert response.data["status"] == "transferred"
|
||
|
||
# Проверяем обновление оборудования
|
||
self.equipment.refresh_from_db()
|
||
assert self.equipment.department == self.dept2
|
||
|
||
# Проверяем лог перевода
|
||
log = EquipmentTransferLog.objects.filter(equipment=self.equipment).first()
|
||
assert log is not None
|
||
assert log.from_department == self.dept1
|
||
assert log.to_department == self.dept2
|
||
assert log.reason == "Переезд сотрудника"
|
||
|
||
def test_transfer_to_another_person(self):
|
||
"""Тест: перевод оборудования другому пользователю."""
|
||
self.client.force_authenticate(user=self.user1)
|
||
|
||
response = self.client.post(
|
||
f"/api/v1/procurement/equipment/{self.equipment.id}/transfer/",
|
||
{
|
||
"to_person": self.user2.id,
|
||
"reason": "Смена ответственного",
|
||
},
|
||
)
|
||
|
||
assert response.status_code == status.HTTP_200_OK
|
||
|
||
self.equipment.refresh_from_db()
|
||
assert self.equipment.responsible_person == self.user2
|
||
|
||
def test_transfer_requires_target(self):
|
||
"""Тест: нужно указать отдел или пользователя."""
|
||
self.client.force_authenticate(user=self.user1)
|
||
|
||
response = self.client.post(
|
||
f"/api/v1/procurement/equipment/{self.equipment.id}/transfer/",
|
||
{"reason": "Без цели"},
|
||
)
|
||
|
||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||
|
||
|
||
@pytest.mark.django_db
|
||
class TestEquipmentWriteOffEndpoint:
|
||
"""Тесты для /equipment/{id}/write_off/ endpoint."""
|
||
|
||
@pytest.fixture(autouse=True)
|
||
def setup(self, api_client, department_factory, user_factory, link_factory):
|
||
"""Настройка тестовых данных."""
|
||
self.client = api_client
|
||
self.department = department_factory(name="IT WriteOff")
|
||
self.user = user_factory(
|
||
staff=True,
|
||
email="writeoff_user@test.com",
|
||
first_name="Test",
|
||
last_name="WriteOff",
|
||
)
|
||
link_factory(self.user, self.department, is_active=True)
|
||
|
||
self.category = EquipmentCategory.objects.create(
|
||
name="Printers",
|
||
description="Принтеры",
|
||
)
|
||
self.equipment = Equipment.objects.create(
|
||
name="HP LaserJet",
|
||
inventory_number="INV-2025-0300",
|
||
category=self.category,
|
||
department=self.department,
|
||
responsible_person=self.user,
|
||
status=EquipmentStatus.IN_USE,
|
||
purchase_date="2025-01-01",
|
||
purchase_cost="10000.00",
|
||
)
|
||
|
||
def test_write_off_equipment(self):
|
||
"""Тест: списание оборудования."""
|
||
self.client.force_authenticate(user=self.user)
|
||
|
||
response = self.client.post(
|
||
f"/api/v1/procurement/equipment/{self.equipment.id}/write_off/",
|
||
{"reason": "Износ"},
|
||
)
|
||
|
||
assert response.status_code == status.HTTP_200_OK
|
||
assert response.data["status"] == "written_off"
|
||
|
||
self.equipment.refresh_from_db()
|
||
assert self.equipment.status == EquipmentStatus.RETIRED
|
||
assert "Износ" in self.equipment.notes
|
||
|
||
def test_cannot_write_off_twice(self):
|
||
"""Тест: нельзя списать уже списанное оборудование."""
|
||
self.equipment.status = EquipmentStatus.RETIRED
|
||
self.equipment.save()
|
||
|
||
self.client.force_authenticate(user=self.user)
|
||
|
||
response = self.client.post(
|
||
f"/api/v1/procurement/equipment/{self.equipment.id}/write_off/",
|
||
{"reason": "Повторно"},
|
||
)
|
||
|
||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||
|
||
|
||
@pytest.mark.django_db
|
||
class TestEquipmentMaintenanceEndpoint:
|
||
"""Тесты для /equipment/{id}/add_maintenance/ endpoint."""
|
||
|
||
@pytest.fixture(autouse=True)
|
||
def setup(self, api_client, department_factory, user_factory, link_factory):
|
||
"""Настройка тестовых данных."""
|
||
self.client = api_client
|
||
self.department = department_factory(name="IT Maintenance")
|
||
self.user = user_factory(
|
||
staff=True,
|
||
email="maintenance_user@test.com",
|
||
first_name="Test",
|
||
last_name="Maintenance",
|
||
)
|
||
link_factory(self.user, self.department, is_active=True)
|
||
|
||
self.category = EquipmentCategory.objects.create(
|
||
name="Servers",
|
||
description="Серверы",
|
||
)
|
||
self.equipment = Equipment.objects.create(
|
||
name="Dell PowerEdge",
|
||
inventory_number="INV-2025-0400",
|
||
category=self.category,
|
||
department=self.department,
|
||
responsible_person=self.user,
|
||
status=EquipmentStatus.IN_USE,
|
||
purchase_date="2025-01-01",
|
||
purchase_cost="10000.00",
|
||
)
|
||
|
||
def test_add_maintenance_record(self):
|
||
"""Тест: добавление записи об обслуживании."""
|
||
self.client.force_authenticate(user=self.user)
|
||
|
||
response = self.client.post(
|
||
f"/api/v1/procurement/equipment/{self.equipment.id}/add_maintenance/",
|
||
{
|
||
"type": "repair",
|
||
"description": "Замена блока питания",
|
||
"cost": "5000.00",
|
||
},
|
||
)
|
||
|
||
assert response.status_code == status.HTTP_201_CREATED
|
||
assert "maintenance_id" in response.data
|
||
|
||
# Проверяем создание записи
|
||
record = MaintenanceRecord.objects.get(id=response.data["maintenance_id"])
|
||
assert record.equipment == self.equipment
|
||
assert record.description == "Замена блока питания"
|
||
assert record.performed_by == self.user
|
||
|
||
|
||
@pytest.mark.django_db
|
||
class TestEquipmentTransferHistoryEndpoint:
|
||
"""Тесты для /equipment/{id}/transfer_history/ endpoint."""
|
||
|
||
@pytest.fixture(autouse=True)
|
||
def setup(self, api_client, department_factory, user_factory, link_factory):
|
||
"""Настройка тестовых данных."""
|
||
self.client = api_client
|
||
self.dept1 = department_factory(name="IT History 1")
|
||
self.dept2 = department_factory(name="IT History 2")
|
||
|
||
self.user1 = user_factory(
|
||
staff=True,
|
||
email="history_user1@test.com",
|
||
first_name="User",
|
||
last_name="History1",
|
||
)
|
||
self.user2 = user_factory(
|
||
email="history_user2@test.com",
|
||
first_name="User",
|
||
last_name="History2",
|
||
)
|
||
link_factory(self.user1, self.dept1, is_active=True)
|
||
|
||
self.category = EquipmentCategory.objects.create(
|
||
name="Phones",
|
||
description="Телефоны",
|
||
)
|
||
self.equipment = Equipment.objects.create(
|
||
name="iPhone 15",
|
||
inventory_number="INV-2025-0600",
|
||
category=self.category,
|
||
department=self.dept1,
|
||
responsible_person=self.user1,
|
||
status=EquipmentStatus.IN_USE,
|
||
purchase_date="2025-01-01",
|
||
purchase_cost="10000.00",
|
||
)
|
||
|
||
# Создаём историю переводов
|
||
EquipmentTransferLog.objects.create(
|
||
equipment=self.equipment,
|
||
from_department=self.dept1,
|
||
to_department=self.dept2,
|
||
from_person=self.user1,
|
||
to_person=self.user2,
|
||
reason="Первый перевод",
|
||
created_by=self.user1,
|
||
)
|
||
|
||
def test_get_transfer_history(self):
|
||
"""Тест: получение истории переводов."""
|
||
self.client.force_authenticate(user=self.user1)
|
||
|
||
response = self.client.get(
|
||
f"/api/v1/procurement/equipment/{self.equipment.id}/transfer_history/"
|
||
)
|
||
|
||
assert response.status_code == status.HTTP_200_OK
|
||
assert len(response.data) == 1
|
||
assert response.data[0]["reason"] == "Первый перевод"
|
||
assert "from_department" in response.data[0]
|
||
assert "to_department" in response.data[0]
|