from django.db import models from django.conf import settings from django.utils import timezone from core.models.directories import directory_default class EquipmentModel(models.Model): """Каталог моделей: тип + бренд + название заводятся один раз (например «Mikrotik RB952Ui-5ac2nD»). Конкретные экземпляры (Equipment) ссылаются на модель и несут только серийник/инвентарник/локацию — больше не нужно каждый раз переписывать тип и модель.""" equipment_type = models.ForeignKey( 'core.Directory', on_delete=models.PROTECT, limit_choices_to={'directory_type__code': 'equipment_type', 'is_active': True}, related_name='models_by_type', verbose_name='Тип', ) brand = models.ForeignKey( 'core.Directory', on_delete=models.SET_NULL, limit_choices_to={'directory_type__code': 'equipment_brand', 'is_active': True}, related_name='models_by_brand', verbose_name='Бренд', null=True, blank=True, ) name = models.CharField('Модель', max_length=100) # Гарантия по умолчанию (месяцев) — при массовом вводе экземпляров # warranty_until можно посчитать от даты покупки, не вводя вручную warranty_months = models.PositiveIntegerField( 'Гарантия (мес.)', null=True, blank=True, help_text='Стандартный срок гарантии для этой модели', ) notes = models.TextField('Примечания', blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = 'Модель оборудования' verbose_name_plural = 'Модели оборудования (каталог)' ordering = ['equipment_type__name', 'brand__name', 'name'] constraints = [ models.UniqueConstraint( fields=['equipment_type', 'brand', 'name'], name='unique_equipment_model', ), ] def __str__(self): brand = f'{self.brand.name} ' if self.brand_id else '' return f'{brand}{self.name}'.strip() @property def full_label(self): return f'{self.equipment_type.name} · {self}' class Equipment(models.Model): equipment_model = models.ForeignKey( EquipmentModel, on_delete=models.PROTECT, related_name='units', verbose_name='Модель', ) serial_number = models.CharField('Серийный номер', max_length=100, unique=True) inventory_number = models.CharField('Инвентарный номер', max_length=50, blank=True, unique=True, null=True) status = models.ForeignKey( 'core.Directory', on_delete=models.PROTECT, limit_choices_to={'directory_type__code': 'equipment_status', 'is_active': True}, related_name='equipment_by_status', verbose_name='Статус', default=directory_default('equipment_status', 'stock'), ) assigned_to = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_equipment', verbose_name='Выдано сотруднику', ) location = models.ForeignKey( 'locations.Location', on_delete=models.SET_NULL, null=True, blank=True, related_name='location_equipment', verbose_name='Локация', ) purchase_date = models.DateField('Дата покупки', null=True, blank=True) warranty_until = models.DateField('Гарантия до', null=True, blank=True) notes = models.TextField('Примечания', blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = 'Оборудование' verbose_name_plural = 'Оборудование' ordering = ['equipment_model__equipment_type__name', 'equipment_model__name'] indexes = [ models.Index(fields=['assigned_to']), models.Index(fields=['equipment_model']), ] def __str__(self): return f'{self.equipment_model} [{self.serial_number}]' # --- Удобные проксі до моделі каталогу (щоб шаблони/код не лізли # кожен раз через equipment_model) --- @property def equipment_type(self): return self.equipment_model.equipment_type @property def brand(self): return self.equipment_model.brand @property def model(self): return self.equipment_model.name @property def is_under_warranty(self): if not self.warranty_until: return None return self.warranty_until >= timezone.now().date() @property def warranty_status(self): if not self.warranty_until: return 'Нет данных' if self.is_under_warranty: days = (self.warranty_until - timezone.now().date()).days if days <= 30: return f'Истекает через {days} дн.' return 'Действительна' return 'Истекла' class EquipmentAssignment(models.Model): """История выдачи и перемещений оборудования.""" equipment = models.ForeignKey( Equipment, on_delete=models.CASCADE, related_name='assignments', verbose_name='Оборудование', ) assigned_to = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='equipment_assignments', verbose_name='Сотрудник', ) location = models.ForeignKey( 'locations.Location', on_delete=models.SET_NULL, null=True, blank=True, related_name='equipment_assignments', verbose_name='Локация', ) assigned_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='issued_equipment', verbose_name='Выдал', ) assigned_at = models.DateTimeField('Дата выдачи', default=timezone.now) returned_at = models.DateTimeField('Дата возврата', null=True, blank=True) notes = models.TextField('Примечания', blank=True) class Meta: verbose_name = 'Перемещение оборудования' verbose_name_plural = 'История перемещений' ordering = ['-assigned_at'] indexes = [ models.Index(fields=['equipment', 'assigned_at']), ] def __str__(self): target = self.assigned_to or self.location or 'Склад' return f'{self.equipment} → {target} ({self.assigned_at:%d.%m.%Y})' @property def is_active(self): return self.returned_at is None