from django.db import models from datetime import date from simple_history.models import HistoricalRecords from users.models import Employee from django.core.exceptions import ValidationError from django.db.models import Max EQUIPMENT_STATUS = [ ("activ", "Activ"), ("casat", "Casat (Decommissioned)"), ] from django.core.exceptions import ValidationError def validate_file_size(value): limit_mb = 3 if value.size > limit_mb * 1024 * 1024: raise ValidationError(f"Dimensiunea maximă permisă este de {limit_mb} MB.") class Computer(models.Model): # Unique ID and Entry equipment_code = models.CharField( max_length=50, unique=True, verbose_name="Cod Echipament" ) entry_date = models.DateField(default=date.today, verbose_name="Data intrării") # Hardware Specs oem_name = models.CharField( max_length=255, null=True, blank=True, verbose_name="Producător (OEM)" ) motherboard = models.CharField(max_length=255, verbose_name="Placă de bază") cpu = models.CharField(max_length=255, verbose_name="Procesor (CPU)") ram = models.CharField(max_length=100, verbose_name="Memorie RAM") storage = models.CharField(max_length=100, verbose_name="Stocare (HDD/SSD)") optical_drive = models.CharField( max_length=100, blank=True, null=True, verbose_name="Unitate optică" ) gpu = models.CharField( max_length=255, null=True, blank=True, verbose_name="Placă video (GPU)" ) mac_address = models.CharField(max_length=50, verbose_name="Adresă MAC") # Software & Misc os_license = models.CharField(max_length=255, verbose_name="Licență OS") additional_licenses = models.TextField( blank=True, null=True, verbose_name="Licențe adiționale" ) observations = models.TextField(blank=True, null=True, verbose_name="Observații") # Custody Assignment assigned_to = models.ForeignKey( Employee, on_delete=models.SET_NULL, null=True, blank=True, related_name="computers", verbose_name="Atribuit către", ) status = models.CharField( max_length=20, choices=EQUIPMENT_STATUS, default="activ", verbose_name="Stare" ) history = HistoricalRecords() def __str__(self): # Format: Cod Echipament | CPU | RAM | Placă de bază return f"{self.equipment_code} | {self.cpu} | {self.ram} | {self.motherboard}" class Meta: verbose_name = "Computer / Laptop" verbose_name_plural = "Computere / Laptopuri" class Monitor(models.Model): equipment_code = models.CharField( max_length=50, unique=True, verbose_name="Cod Echipament" ) entry_date = models.DateField(default=date.today, verbose_name="Data intrării") name = models.CharField(max_length=255, verbose_name="Nume Monitor") serial_number = models.CharField(max_length=100, verbose_name="Număr Serial") screen_diagonal = models.CharField(max_length=50, verbose_name="Diagonală (inch)") observations = models.TextField(blank=True, null=True, verbose_name="Observații") assigned_to = models.ForeignKey( Employee, on_delete=models.SET_NULL, null=True, blank=True, related_name="monitors", verbose_name="Atribuit către", ) status = models.CharField( max_length=20, choices=EQUIPMENT_STATUS, default="activ", verbose_name="Stare" ) history = HistoricalRecords() def __str__(self): # Format: Cod Echipament | Model return f"{self.equipment_code} | {self.name}" class Meta: verbose_name = "Monitor" verbose_name_plural = "Monitoare" class Peripheral(models.Model): PERIPHERAL_TYPES = [ ("printer", "Imprimantă"), ("ups", "UPS"), ("scanner", "Scanner"), ("copier", "Copiator"), ("ethernet", "Adaptor Ethernet"), ("audio", "Boxe Audio"), ("hdd_ext", "HDD Extern"), ("webcam", "Cameră Web"), ("other", "Altul"), ] equipment_code = models.CharField( max_length=50, unique=True, verbose_name="Cod Echipament" ) entry_date = models.DateField(default=date.today, verbose_name="Data intrării") peripheral_type = models.CharField( max_length=50, choices=PERIPHERAL_TYPES, verbose_name="Tip Periferic" ) name = models.CharField(max_length=255, verbose_name="Nume Echipament") serial_number = models.CharField(max_length=100, verbose_name="Număr Serial") observations = models.TextField(blank=True, null=True, verbose_name="Observații") assigned_to = models.ForeignKey( Employee, on_delete=models.SET_NULL, null=True, blank=True, related_name="peripherals", verbose_name="Atribuit către", ) status = models.CharField( max_length=20, choices=EQUIPMENT_STATUS, default="activ", verbose_name="Stare" ) history = HistoricalRecords() def __str__(self): # Format: Cod Echipament | Tip | Model return f"{self.equipment_code} | {self.get_peripheral_type_display()} | {self.name}" class Meta: verbose_name = "Periferic" verbose_name_plural = "Periferice" class DigitalCertificate(models.Model): CERT_TYPES = [ ("cloud", "Cloud"), ("usb", "USB Token"), ] certificate_type = models.CharField( max_length=50, choices=CERT_TYPES, verbose_name="Tip Certificat" ) issue_date = models.DateField(verbose_name="Data emiterii") expiration_date = models.DateField(verbose_name="Data expirării") observations = models.TextField(blank=True, null=True, verbose_name="Observații") is_canceled = models.BooleanField(default=False, verbose_name="Anulat?") # Links directly to the employee assigned_to = models.ForeignKey( Employee, on_delete=models.CASCADE, related_name="digital_certificates", verbose_name="Titular Certificat", ) history = HistoricalRecords() def __str__(self): return f"Certificat {self.get_certificate_type_display()} - {self.assigned_to.name}" class Meta: verbose_name = "Certificat Digital" verbose_name_plural = "Certificate Digitale" class MaintenanceRecord(models.Model): date = models.DateField(default=date.today, verbose_name="Data intervenției") operation_type = models.CharField( max_length=150, verbose_name="Tip operațiune (Curățare, Upgrade, etc.)" ) description = models.TextField(verbose_name="Descriere detaliată") performed_by = models.CharField(max_length=100, verbose_name="Efectuat de") next_maintenance_date = models.DateField( null=True, blank=True, verbose_name="Următoarea mentenanță planificată" ) # Links to equipment (only one should be populated) computer = models.ForeignKey( Computer, on_delete=models.CASCADE, null=True, blank=True, related_name="maintenance_logs", ) monitor = models.ForeignKey( Monitor, on_delete=models.CASCADE, null=True, blank=True, related_name="maintenance_logs", ) peripheral = models.ForeignKey( Peripheral, on_delete=models.CASCADE, null=True, blank=True, related_name="maintenance_logs", ) history = HistoricalRecords() def clean(self): equipment_count = sum( [bool(self.computer), bool(self.monitor), bool(self.peripheral)] ) if equipment_count != 1: raise ValidationError( "O înregistrare de mentenanță trebuie să fie legată de exact un singur echipament." ) def save(self, *args, **kwargs): self.clean() super().save(*args, **kwargs) def __str__(self): return f"{self.date} - {self.operation_type}" class Meta: verbose_name = "Foaie de Mentenanță" verbose_name_plural = "Foi de Mentenanță" class DecommissionRecord(models.Model): date = models.DateField(default=date.today, verbose_name="Data casării") document_number = models.CharField( max_length=100, verbose_name="Număr Proces Verbal" ) reason = models.TextField(verbose_name="Motivul casării (Defect, Uzat moral, etc.)") # Am transformat relațiile în ManyToMany pentru a permite liste de echipamente computers = models.ManyToManyField( Computer, blank=True, verbose_name="Computere casate" ) monitors = models.ManyToManyField( Monitor, blank=True, verbose_name="Monitoare casate" ) peripherals = models.ManyToManyField( Peripheral, blank=True, verbose_name="Periferice casate" ) # Comisia de casare (opțional, dar util pentru PDF) committee_members = models.TextField( blank=True, null=True, verbose_name="Membrii comisiei de casare (nume și funcție)", ) history = HistoricalRecords() # Eliminăm vechea funcție clean() care forța un singur echipament # Eliminăm vechea funcție save() - vom face actualizarea statusului direct din View (ca la predare) def __str__(self): return f"PV Casare nr. {self.document_number} din {self.date}" class Meta: verbose_name = "Proces Verbal de Casare" verbose_name_plural = "Procese Verbale de Casare" class HandoverProtocol(models.Model): date = models.DateField(default=date.today, verbose_name="Data documentului") document_number = models.IntegerField( unique=True, blank=True, verbose_name="Număr Proces Verbal" ) previous_holder = models.ForeignKey( Employee, on_delete=models.SET_NULL, null=True, blank=True, related_name="equipment_handed_over", verbose_name="Predat de (Predător)", ) new_holder = models.ForeignKey( Employee, on_delete=models.SET_NULL, null=True, blank=True, related_name="equipment_received", verbose_name="Primit de (Primitor)", ) # We will populate this automatically in the admin it_validator = models.CharField( max_length=255, blank=True, verbose_name="Întocmit de (IT)" ) computers = models.ManyToManyField( Computer, blank=True, verbose_name="Computere transferate" ) monitors = models.ManyToManyField( Monitor, blank=True, verbose_name="Monitoare transferate" ) peripherals = models.ManyToManyField( Peripheral, blank=True, verbose_name="Periferice transferate" ) notes = models.TextField(blank=True, null=True, verbose_name="Observații") # Field to attach the signed document scanned_document = models.FileField( upload_to="handover_protocols/", blank=True, null=True, validators=[validate_file_size], verbose_name="Document scanat (semnat)", ) history = HistoricalRecords() def save(self, *args, **kwargs): if not self.document_number: max_num = HandoverProtocol.objects.aggregate(Max("document_number"))[ "document_number__max" ] self.document_number = ( (max_num + 1) if (max_num and max_num >= 1000) else 1000 ) super().save(*args, **kwargs) def __str__(self): return f"PV Predare-Primire nr. {self.document_number} din {self.date}" class Meta: verbose_name = "Proces Verbal Predare-Primire" verbose_name_plural = "Procese Verbale Predare-Primire"