352 lines
11 KiB
Python
352 lines
11 KiB
Python
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"
|