970 lines
31 KiB
Python
970 lines
31 KiB
Python
from django.contrib.staticfiles import finders
|
|
from django.template.loader import get_template
|
|
from xhtml2pdf import pisa
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.urls import reverse
|
|
from django.http import HttpResponse
|
|
from django.db.models import Q
|
|
from django.shortcuts import render, get_object_or_404
|
|
from users.models import Employee
|
|
from .models import Computer, Monitor, Peripheral, MaintenanceRecord, HandoverProtocol
|
|
from .forms import (
|
|
ComputerForm,
|
|
MonitorForm,
|
|
PeripheralForm,
|
|
MaintenanceRecordForm,
|
|
HandoverProtocolForm,
|
|
)
|
|
from django.core.exceptions import PermissionDenied
|
|
from itertools import chain
|
|
from operator import attrgetter
|
|
from django.template.loader import get_template
|
|
from django.http import HttpResponse
|
|
from .forms import DecommissionRecordForm
|
|
from .models import DecommissionRecord
|
|
from datetime import date, timedelta
|
|
from .models import DigitalCertificate
|
|
from .forms import DigitalCertificateForm
|
|
import csv
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.shortcuts import render
|
|
|
|
|
|
@login_required
|
|
def computer_maintenance_create(request, pk):
|
|
computer = get_object_or_404(Computer, pk=pk)
|
|
|
|
if request.method == "POST":
|
|
# 1. Creăm instanța și îi legăm computerul DE LA ÎNCEPUT
|
|
maintenance_instance = MaintenanceRecord(computer=computer)
|
|
|
|
# 2. Pasăm instanța către formular pentru a trece de validarea din clean()
|
|
form = MaintenanceRecordForm(request.POST, instance=maintenance_instance)
|
|
|
|
if form.is_valid():
|
|
form.save() # Acum se va salva perfect!
|
|
|
|
# Returnăm status 204 și instruim HTMX să reîncarce complet pagina
|
|
response = HttpResponse(status=204)
|
|
response["HX-Refresh"] = "true"
|
|
return response
|
|
else:
|
|
initial_name = (
|
|
request.user.get_full_name() or request.user.username
|
|
if request.user.is_authenticated
|
|
else ""
|
|
)
|
|
form = MaintenanceRecordForm(initial={"performed_by": initial_name})
|
|
|
|
return render(
|
|
request,
|
|
"equipment/partials/maintenance_form.html",
|
|
{
|
|
"form": form,
|
|
"equipment_title": computer.equipment_code,
|
|
"post_url": reverse(
|
|
"equipment:computer_maintenance_create", args=[computer.pk]
|
|
),
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def print_inventory_sheet(request, employee_id):
|
|
employee = get_object_or_404(Employee, id=employee_id)
|
|
inventory = employee.get_active_inventory
|
|
|
|
context = {
|
|
"employee": employee,
|
|
"inventory": inventory,
|
|
}
|
|
return render(request, "equipment/print_inventory.html", context)
|
|
|
|
|
|
@login_required
|
|
def print_handover_protocol(request, protocol_id):
|
|
protocol = get_object_or_404(HandoverProtocol, id=protocol_id)
|
|
|
|
context = {
|
|
"protocol": protocol,
|
|
}
|
|
return render(request, "equipment/print_handover.html", context)
|
|
|
|
|
|
@login_required
|
|
def equipment_list(request):
|
|
query = request.GET.get("search", "")
|
|
|
|
# Fetch only active equipment
|
|
computers = Computer.objects.filter(status="activ").order_by("-entry_date")
|
|
monitors = Monitor.objects.filter(status="activ").order_by("-entry_date")
|
|
peripherals = Peripheral.objects.filter(status="activ").order_by("-entry_date")
|
|
|
|
if query:
|
|
computers = computers.filter(
|
|
Q(equipment_code__icontains=query)
|
|
| Q(oem_name__icontains=query)
|
|
| Q(motherboard__icontains=query)
|
|
| Q(cpu__icontains=query)
|
|
| Q(ram__icontains=query)
|
|
| Q(storage__icontains=query)
|
|
| Q(gpu__icontains=query)
|
|
| Q(mac_address__icontains=query)
|
|
| Q(os_license__icontains=query)
|
|
| Q(observations__icontains=query)
|
|
| Q(assigned_to__name__icontains=query)
|
|
)
|
|
monitors = monitors.filter(
|
|
Q(equipment_code__icontains=query)
|
|
| Q(name__icontains=query)
|
|
| Q(serial_number__icontains=query)
|
|
| Q(screen_diagonal__icontains=query)
|
|
| Q(observations__icontains=query)
|
|
| Q(assigned_to__name__icontains=query)
|
|
)
|
|
peripherals = peripherals.filter(
|
|
Q(equipment_code__icontains=query)
|
|
| Q(name__icontains=query)
|
|
| Q(peripheral_type__icontains=query)
|
|
| Q(serial_number__icontains=query)
|
|
| Q(observations__icontains=query)
|
|
| Q(assigned_to__name__icontains=query)
|
|
)
|
|
|
|
context = {
|
|
"computers": computers,
|
|
"monitors": monitors,
|
|
"peripherals": peripherals,
|
|
}
|
|
|
|
if request.headers.get("HX-Request"):
|
|
return render(request, "equipment/partials/equipment_tables.html", context)
|
|
|
|
return render(request, "equipment/equipment_list.html", context)
|
|
|
|
|
|
@login_required
|
|
def computer_form(request, pk=None):
|
|
# Doar personalul IT (staff) are voie să adauge/modifice
|
|
if not request.user.is_staff:
|
|
raise PermissionDenied("Nu aveți permisiunea de a edita echipamente.")
|
|
|
|
if pk:
|
|
computer = get_object_or_404(Computer, pk=pk)
|
|
else:
|
|
computer = None
|
|
|
|
if request.method == "POST":
|
|
form = ComputerForm(request.POST, instance=computer)
|
|
if form.is_valid():
|
|
form.save()
|
|
# Return 204 and trigger the table refresh and modal close
|
|
response = HttpResponse(status=204)
|
|
response["HX-Trigger"] = "equipmentListChanged, closeModal"
|
|
return response
|
|
else:
|
|
form = ComputerForm(instance=computer)
|
|
|
|
return render(
|
|
request,
|
|
"equipment/partials/computer_form.html",
|
|
{"form": form, "computer": computer},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def monitor_form(request, pk=None):
|
|
if pk:
|
|
monitor = get_object_or_404(Monitor, pk=pk)
|
|
else:
|
|
monitor = None
|
|
|
|
if request.method == "POST":
|
|
form = MonitorForm(request.POST, instance=monitor)
|
|
if form.is_valid():
|
|
form.save()
|
|
response = HttpResponse(status=204)
|
|
response["HX-Trigger"] = "equipmentListChanged, closeModal"
|
|
return response
|
|
else:
|
|
form = MonitorForm(instance=monitor)
|
|
|
|
return render(
|
|
request,
|
|
"equipment/partials/monitor_form.html",
|
|
{"form": form, "monitor": monitor},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def peripheral_form(request, pk=None):
|
|
if pk:
|
|
peripheral = get_object_or_404(Peripheral, pk=pk)
|
|
else:
|
|
peripheral = None
|
|
|
|
if request.method == "POST":
|
|
form = PeripheralForm(request.POST, instance=peripheral)
|
|
if form.is_valid():
|
|
form.save()
|
|
response = HttpResponse(status=204)
|
|
response["HX-Trigger"] = "equipmentListChanged, closeModal"
|
|
return response
|
|
else:
|
|
form = PeripheralForm(instance=peripheral)
|
|
|
|
return render(
|
|
request,
|
|
"equipment/partials/peripheral_form.html",
|
|
{"form": form, "peripheral": peripheral},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def computer_technical_sheet(request, pk):
|
|
computer = get_object_or_404(Computer, pk=pk)
|
|
|
|
# Fetch maintenance logs (using the related_name from your models)
|
|
maintenance_logs = computer.maintenance_logs.all().order_by("-date")
|
|
|
|
# Calculate hardware changes from simple_history
|
|
history = list(computer.history.all())
|
|
hardware_changes = []
|
|
|
|
tracked_fields = {
|
|
"cpu": "Procesor",
|
|
"ram": "Memorie RAM",
|
|
"storage": "Stocare",
|
|
"gpu": "Placă Video",
|
|
"motherboard": "Placă de bază",
|
|
"status": "Status",
|
|
}
|
|
|
|
# Compare each historical record with the one that came before it
|
|
for i in range(len(history) - 1):
|
|
new_rec = history[i]
|
|
old_rec = history[i + 1]
|
|
|
|
for field, label in tracked_fields.items():
|
|
old_val = getattr(old_rec, field)
|
|
new_val = getattr(new_rec, field)
|
|
|
|
if old_val != new_val:
|
|
hardware_changes.append(
|
|
{
|
|
"date": new_rec.history_date,
|
|
"field": label,
|
|
"old": old_val,
|
|
"new": new_val,
|
|
}
|
|
)
|
|
|
|
# --- Extragere istoric posesie ---
|
|
ownership_history = []
|
|
# Parcurgem cronologic (de la cea mai veche la cea mai nouă înregistrare)
|
|
history_asc = list(computer.history.all().order_by("history_date"))
|
|
|
|
current_owner = None
|
|
start_date = None
|
|
|
|
for record in history_asc:
|
|
owner = record.assigned_to
|
|
|
|
# Dacă s-a schimbat posesorul
|
|
if owner != current_owner:
|
|
# Salvăm perioada precedentă
|
|
if start_date is not None:
|
|
ownership_history.append(
|
|
{
|
|
"owner": (
|
|
current_owner.name
|
|
if current_owner
|
|
else "Magazie (Neasignat)"
|
|
),
|
|
"start_date": start_date,
|
|
"end_date": record.history_date,
|
|
}
|
|
)
|
|
|
|
# Începem perioada nouă
|
|
current_owner = owner
|
|
start_date = record.history_date
|
|
|
|
# Adăugăm perioada curentă, care e încă în desfășurare
|
|
if start_date is not None:
|
|
ownership_history.append(
|
|
{
|
|
"owner": current_owner.name if current_owner else "Magazie (Neasignat)",
|
|
"start_date": start_date,
|
|
"end_date": None, # Semnifică 'Prezent'
|
|
}
|
|
)
|
|
|
|
# Inversăm lista pentru a avea cel mai recent posesor sus
|
|
ownership_history.reverse()
|
|
# ------------------------------------------
|
|
|
|
return render(
|
|
request,
|
|
"equipment/technical_sheet.html",
|
|
{
|
|
"computer": computer,
|
|
"maintenance_logs": maintenance_logs,
|
|
"hardware_changes": hardware_changes,
|
|
"ownership_history": ownership_history,
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def monitor_technical_sheet(request, pk):
|
|
monitor = get_object_or_404(Monitor, pk=pk)
|
|
maintenance_logs = monitor.maintenance_logs.all().order_by("-date")
|
|
|
|
# --- Extragere istoric posesie ---
|
|
ownership_history = []
|
|
history_asc = list(monitor.history.all().order_by("history_date"))
|
|
current_owner = None
|
|
start_date = None
|
|
|
|
for record in history_asc:
|
|
owner = record.assigned_to
|
|
if owner != current_owner:
|
|
if start_date is not None:
|
|
ownership_history.append(
|
|
{
|
|
"owner": (
|
|
current_owner.name
|
|
if current_owner
|
|
else "Magazie (Neasignat)"
|
|
),
|
|
"start_date": start_date,
|
|
"end_date": record.history_date,
|
|
}
|
|
)
|
|
current_owner = owner
|
|
start_date = record.history_date
|
|
|
|
if start_date is not None:
|
|
ownership_history.append(
|
|
{
|
|
"owner": current_owner.name if current_owner else "Magazie (Neasignat)",
|
|
"start_date": start_date,
|
|
"end_date": None,
|
|
}
|
|
)
|
|
ownership_history.reverse()
|
|
# ---------------------------------
|
|
|
|
return render(
|
|
request,
|
|
"equipment/monitor_technical_sheet.html",
|
|
{
|
|
"monitor": monitor,
|
|
"maintenance_logs": maintenance_logs,
|
|
"ownership_history": ownership_history, # Trimitem variabila către HTML
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def monitor_maintenance_create(request, pk):
|
|
monitor = get_object_or_404(Monitor, pk=pk)
|
|
if request.method == "POST":
|
|
maintenance_instance = MaintenanceRecord(monitor=monitor)
|
|
form = MaintenanceRecordForm(request.POST, instance=maintenance_instance)
|
|
if form.is_valid():
|
|
form.save()
|
|
response = HttpResponse(status=204)
|
|
response["HX-Refresh"] = "true"
|
|
return response
|
|
else:
|
|
initial_name = (
|
|
request.user.get_full_name() or request.user.username
|
|
if request.user.is_authenticated
|
|
else ""
|
|
)
|
|
form = MaintenanceRecordForm(initial={"performed_by": initial_name})
|
|
|
|
return render(
|
|
request,
|
|
"equipment/partials/maintenance_form.html",
|
|
{
|
|
"form": form,
|
|
"equipment_title": monitor.equipment_code,
|
|
"post_url": reverse(
|
|
"equipment:monitor_maintenance_create", args=[monitor.pk]
|
|
),
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
@login_required
|
|
def peripheral_technical_sheet(request, pk):
|
|
peripheral = get_object_or_404(Peripheral, pk=pk)
|
|
maintenance_logs = peripheral.maintenance_logs.all().order_by("-date")
|
|
|
|
# --- Extragere istoric posesie ---
|
|
ownership_history = []
|
|
history_asc = list(peripheral.history.all().order_by("history_date"))
|
|
current_owner = None
|
|
start_date = None
|
|
|
|
for record in history_asc:
|
|
owner = record.assigned_to
|
|
if owner != current_owner:
|
|
if start_date is not None:
|
|
ownership_history.append(
|
|
{
|
|
"owner": (
|
|
current_owner.name
|
|
if current_owner
|
|
else "Magazie (Neasignat)"
|
|
),
|
|
"start_date": start_date,
|
|
"end_date": record.history_date,
|
|
}
|
|
)
|
|
current_owner = owner
|
|
start_date = record.history_date
|
|
|
|
if start_date is not None:
|
|
ownership_history.append(
|
|
{
|
|
"owner": current_owner.name if current_owner else "Magazie (Neasignat)",
|
|
"start_date": start_date,
|
|
"end_date": None,
|
|
}
|
|
)
|
|
ownership_history.reverse()
|
|
# ---------------------------------
|
|
|
|
return render(
|
|
request,
|
|
"equipment/peripheral_technical_sheet.html",
|
|
{
|
|
"peripheral": peripheral,
|
|
"maintenance_logs": maintenance_logs,
|
|
"ownership_history": ownership_history, # Trimitem variabila către HTML
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def peripheral_maintenance_create(request, pk):
|
|
peripheral = get_object_or_404(Peripheral, pk=pk)
|
|
if request.method == "POST":
|
|
maintenance_instance = MaintenanceRecord(peripheral=peripheral)
|
|
form = MaintenanceRecordForm(request.POST, instance=maintenance_instance)
|
|
if form.is_valid():
|
|
form.save()
|
|
response = HttpResponse(status=204)
|
|
response["HX-Refresh"] = "true"
|
|
return response
|
|
else:
|
|
initial_name = (
|
|
request.user.get_full_name() or request.user.username
|
|
if request.user.is_authenticated
|
|
else ""
|
|
)
|
|
form = MaintenanceRecordForm(initial={"performed_by": initial_name})
|
|
|
|
return render(
|
|
request,
|
|
"equipment/partials/maintenance_form.html",
|
|
{
|
|
"form": form,
|
|
"equipment_title": peripheral.equipment_code,
|
|
"post_url": reverse(
|
|
"equipment:peripheral_maintenance_create", args=[peripheral.pk]
|
|
),
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def handover_protocol_list(request):
|
|
# Preluăm toate procesele verbale, ordonate de la cel mai nou la cel mai vechi
|
|
protocols = HandoverProtocol.objects.all().order_by("-date", "-document_number")
|
|
return render(request, "equipment/handover_list.html", {"protocols": protocols})
|
|
|
|
|
|
@login_required
|
|
def handover_protocol_form(request, pk=None):
|
|
# --- Restricție de securitate ---
|
|
if not request.user.is_staff:
|
|
raise PermissionDenied(
|
|
"Nu aveți permisiunea de a genera procese verbale de predare-primire."
|
|
)
|
|
|
|
if pk:
|
|
protocol = get_object_or_404(HandoverProtocol, pk=pk)
|
|
else:
|
|
protocol = None
|
|
|
|
if request.method == "POST":
|
|
# ATENȚIE: request.FILES este obligatoriu pentru a prelua documentul PDF/Scanat
|
|
form = HandoverProtocolForm(request.POST, request.FILES, instance=protocol)
|
|
|
|
if form.is_valid():
|
|
protocol_instance = form.save(commit=False)
|
|
|
|
# Completăm automat "Întocmit de (IT)" cu numele utilizatorului logat
|
|
if not protocol_instance.it_validator and request.user.is_authenticated:
|
|
protocol_instance.it_validator = (
|
|
request.user.get_full_name() or request.user.username
|
|
)
|
|
|
|
protocol_instance.save()
|
|
|
|
# ATENȚIE: Pentru câmpurile de tip SelectMultiple (Many-to-Many),
|
|
# trebuie apelată manual această funcție după salvarea instanței
|
|
form.save_m2m()
|
|
|
|
# Preluăm noul posesor din formular
|
|
new_owner = protocol_instance.new_holder
|
|
|
|
# Trecem toate echipamentele selectate în proprietatea noului posesor
|
|
if new_owner:
|
|
# Folosim .save() în loc de .update() pentru ca simple_history să înregistreze modificarea
|
|
for pc in protocol_instance.computers.all():
|
|
pc.assigned_to = new_owner
|
|
pc.save()
|
|
|
|
for mon in protocol_instance.monitors.all():
|
|
mon.assigned_to = new_owner
|
|
mon.save()
|
|
|
|
for per in protocol_instance.peripherals.all():
|
|
per.assigned_to = new_owner
|
|
per.save()
|
|
|
|
response = HttpResponse(status=204)
|
|
response["HX-Refresh"] = "true"
|
|
return response
|
|
else:
|
|
form = HandoverProtocolForm(instance=protocol)
|
|
|
|
return render(
|
|
request,
|
|
"equipment/partials/handover_form.html",
|
|
{"form": form, "protocol": protocol},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def dashboard_view(request):
|
|
# --- Coloana 1: Sumar Inventar (Înlocuitor temporar pt. Tichete) ---
|
|
total_computers = Computer.objects.count()
|
|
total_monitors = Monitor.objects.count()
|
|
total_peripherals = Peripheral.objects.count()
|
|
|
|
unassigned_computers = Computer.objects.filter(assigned_to__isnull=True)
|
|
unassigned_monitors = Monitor.objects.filter(assigned_to__isnull=True)
|
|
|
|
# --- Coloana 2: Acțiuni Executate (Istoric simple_history) ---
|
|
# Preluăm ultimele 10 acțiuni din fiecare categorie
|
|
pc_hist = Computer.history.all()[:10]
|
|
mon_hist = Monitor.history.all()[:10]
|
|
per_hist = Peripheral.history.all()[:10]
|
|
|
|
# Combinăm listele și le sortăm descrescător după data acțiunii (history_date)
|
|
activity_log = sorted(
|
|
chain(pc_hist, mon_hist, per_hist), key=attrgetter("history_date"), reverse=True
|
|
)[
|
|
:10
|
|
] # Păstrăm doar ultimele 10 cele mai recente acțiuni în total
|
|
|
|
# --- Coloana 3: Mentenanțe Efectuate ---
|
|
recent_maintenance = MaintenanceRecord.objects.all().order_by("-date", "-id")[:10]
|
|
|
|
context = {
|
|
"total_computers": total_computers,
|
|
"total_monitors": total_monitors,
|
|
"total_peripherals": total_peripherals,
|
|
"unassigned_computers": unassigned_computers,
|
|
"unassigned_monitors": unassigned_monitors,
|
|
"activity_log": activity_log,
|
|
"recent_maintenance": recent_maintenance,
|
|
}
|
|
|
|
return render(request, "equipment/dashboard.html", context)
|
|
|
|
|
|
@login_required
|
|
def generate_handover_pdf(request, pk):
|
|
protocol = get_object_or_404(HandoverProtocol, pk=pk)
|
|
|
|
# Lăsăm Django să găsească calea absolută a fonturilor pe disc
|
|
font_regular = finders.find("fonts/LiberationSerif-Regular.ttf")
|
|
font_bold = finders.find("fonts/LiberationSerif-Bold.ttf")
|
|
|
|
# Găsim calea către siglă
|
|
logo_path = finders.find("img/stema.png")
|
|
|
|
# Definim contextul care va fi trimis către șablonul HTML
|
|
context = {
|
|
"protocol": protocol,
|
|
"font_regular": font_regular,
|
|
"font_bold": font_bold,
|
|
"logo_path": logo_path, # Trimitem calea siglei în context
|
|
# Preluăm numele complet (dacă e setat în admin), altfel luăm username-ul
|
|
"generated_by": request.user.get_full_name() or request.user.username,
|
|
}
|
|
|
|
template = get_template("equipment/pdf_handover.html")
|
|
html = template.render(context)
|
|
|
|
# --- REZOLVARE DIACRITICE ---
|
|
# Înlocuim "ș" și "ț" cu virgulă (standard nou) cu variantele cu sedilă (standard vechi)
|
|
# pentru a fi recunoscute de fontul din 2010 și librăria PDF.
|
|
html = html.replace("ș", "ş").replace("ț", "ţ")
|
|
html = html.replace("Ș", "Ş").replace("Ț", "Ţ")
|
|
# ----------------------------
|
|
|
|
# Creăm răspunsul HTTP de tip PDF
|
|
response = HttpResponse(content_type="application/pdf")
|
|
# Setăm numele fișierului descărcat
|
|
response["Content-Disposition"] = (
|
|
f'attachment; filename="Proces_Verbal_Nr_{protocol.document_number}.pdf"'
|
|
)
|
|
|
|
# Generăm PDF-ul
|
|
pisa_status = pisa.CreatePDF(html, dest=response)
|
|
|
|
if pisa_status.err:
|
|
return HttpResponse("A apărut o eroare la generarea PDF-ului", status=500)
|
|
return response
|
|
|
|
|
|
@login_required
|
|
def load_user_equipment(request):
|
|
user_id = request.GET.get("previous_holder")
|
|
form = HandoverProtocolForm()
|
|
|
|
if user_id:
|
|
# Filtrăm doar echipamentele active ale utilizatorului selectat
|
|
form.fields["computers"].queryset = Computer.objects.filter(
|
|
assigned_to_id=user_id, status="activ"
|
|
)
|
|
form.fields["monitors"].queryset = Monitor.objects.filter(
|
|
assigned_to_id=user_id, status="activ"
|
|
)
|
|
form.fields["peripherals"].queryset = Peripheral.objects.filter(
|
|
assigned_to_id=user_id, status="activ"
|
|
)
|
|
else:
|
|
form.fields["computers"].queryset = Computer.objects.none()
|
|
form.fields["monitors"].queryset = Monitor.objects.none()
|
|
form.fields["peripherals"].queryset = Peripheral.objects.none()
|
|
|
|
# Trimitem doar bucata HTML cu cele 3 liste
|
|
return render(
|
|
request, "equipment/partials/equipment_dropdowns.html", {"form": form}
|
|
)
|
|
|
|
|
|
@login_required
|
|
def decommission_list(request):
|
|
# Listăm toate procesele verbale de casare
|
|
records = DecommissionRecord.objects.all().order_by("-date", "-document_number")
|
|
return render(request, "equipment/decommission_list.html", {"records": records})
|
|
|
|
|
|
@login_required
|
|
def decommission_form(request, pk=None):
|
|
# --- Restricție de securitate ---
|
|
if not request.user.is_staff:
|
|
raise PermissionDenied("Nu aveți permisiunea de a gestiona casările.")
|
|
|
|
if pk:
|
|
record = get_object_or_404(DecommissionRecord, pk=pk)
|
|
else:
|
|
record = None
|
|
|
|
if request.method == "POST":
|
|
form = DecommissionRecordForm(request.POST, instance=record)
|
|
|
|
if form.is_valid():
|
|
decommission_instance = form.save()
|
|
|
|
# --- ACTUALIZARE AUTOMATĂ A INVENTARULUI ---
|
|
# Trecem toate echipamentele selectate pe status "casat" și le scoatem de la utilizatori
|
|
for pc in decommission_instance.computers.all():
|
|
pc.status = "casat"
|
|
pc.assigned_to = None
|
|
pc.save()
|
|
|
|
for mon in decommission_instance.monitors.all():
|
|
mon.status = "casat"
|
|
mon.assigned_to = None
|
|
mon.save()
|
|
|
|
for per in decommission_instance.peripherals.all():
|
|
per.status = "casat"
|
|
per.assigned_to = None
|
|
per.save()
|
|
# -------------------------------------------
|
|
|
|
response = HttpResponse(status=204)
|
|
response["HX-Refresh"] = "true"
|
|
return response
|
|
else:
|
|
form = DecommissionRecordForm(instance=record)
|
|
|
|
return render(
|
|
request,
|
|
"equipment/partials/decommission_form.html",
|
|
{"form": form, "record": record},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def generate_decommission_pdf(request, pk):
|
|
record = get_object_or_404(DecommissionRecord, pk=pk)
|
|
|
|
font_regular = finders.find("fonts/LiberationSerif-Regular.ttf")
|
|
font_bold = finders.find("fonts/LiberationSerif-Bold.ttf")
|
|
logo_path = finders.find("img/stema.png")
|
|
|
|
context = {
|
|
"record": record,
|
|
"font_regular": font_regular,
|
|
"font_bold": font_bold,
|
|
"logo_path": logo_path,
|
|
"generated_by": request.user.get_full_name() or request.user.username,
|
|
}
|
|
|
|
template = get_template("equipment/pdf_decommission.html")
|
|
html = template.render(context)
|
|
|
|
# Rezolvare diacritice pentru fontul vechi
|
|
html = html.replace("ș", "ş").replace("ț", "ţ")
|
|
html = html.replace("Ș", "Ş").replace("Ț", "Ţ")
|
|
|
|
response = HttpResponse(content_type="application/pdf")
|
|
response["Content-Disposition"] = (
|
|
f'attachment; filename="PV_Casare_{record.document_number}.pdf"'
|
|
)
|
|
|
|
pisa_status = pisa.CreatePDF(html, dest=response)
|
|
|
|
if pisa_status.err:
|
|
return HttpResponse("A apărut o eroare la generarea PDF-ului", status=500)
|
|
return response
|
|
|
|
|
|
@login_required
|
|
def certificate_list(request):
|
|
certificates = DigitalCertificate.objects.all().order_by("expiration_date")
|
|
|
|
today = date.today()
|
|
warning_date = today + timedelta(days=30)
|
|
|
|
# Calculăm statusul în timp real pentru fiecare certificat
|
|
for cert in certificates:
|
|
if cert.is_canceled:
|
|
cert.status_color = "secondary"
|
|
cert.status_text = "Anulat"
|
|
elif cert.expiration_date < today:
|
|
cert.status_color = "danger"
|
|
cert.status_text = "Expirat"
|
|
elif cert.expiration_date <= warning_date:
|
|
cert.status_color = "warning text-dark"
|
|
cert.status_text = "Expiră curând"
|
|
else:
|
|
cert.status_color = "success"
|
|
cert.status_text = "Activ"
|
|
|
|
return render(
|
|
request, "equipment/certificate_list.html", {"certificates": certificates}
|
|
)
|
|
|
|
|
|
@login_required
|
|
def certificate_form(request, pk=None):
|
|
# --- Restricție de securitate ---
|
|
if not request.user.is_staff:
|
|
raise PermissionDenied(
|
|
"Nu aveți permisiunea de a adăuga sau edita certificate digitale."
|
|
)
|
|
|
|
if pk:
|
|
certificate = get_object_or_404(DigitalCertificate, pk=pk)
|
|
else:
|
|
certificate = None
|
|
|
|
if request.method == "POST":
|
|
form = DigitalCertificateForm(request.POST, instance=certificate)
|
|
if form.is_valid():
|
|
form.save()
|
|
response = HttpResponse(status=204)
|
|
response["HX-Refresh"] = "true"
|
|
return response
|
|
else:
|
|
form = DigitalCertificateForm(instance=certificate)
|
|
|
|
return render(
|
|
request,
|
|
"equipment/partials/certificate_form.html",
|
|
{"form": form, "certificate": certificate},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def export_inventory_csv(request):
|
|
response = HttpResponse(content_type="text/csv; charset=utf-8-sig")
|
|
response["Content-Disposition"] = 'attachment; filename="Inventar_Echipamente.csv"'
|
|
|
|
writer = csv.writer(response, delimiter=";")
|
|
|
|
# ==========================================
|
|
# 1. SECȚIUNEA COMPUTERE
|
|
# ==========================================
|
|
writer.writerow(["--- COMPUTERE ---"])
|
|
writer.writerow(
|
|
[
|
|
"Cod Inventar",
|
|
"OEM / Model",
|
|
"Placă de bază",
|
|
"Procesor (CPU)",
|
|
"Memorie RAM",
|
|
"Stocare",
|
|
"Adresă MAC",
|
|
"Utilizator Curent",
|
|
"Status",
|
|
]
|
|
)
|
|
|
|
computers = Computer.objects.filter(status="activ").order_by("assigned_to__name")
|
|
for pc in computers:
|
|
owner = pc.assigned_to.name if pc.assigned_to else "Magazie (Neasignat)"
|
|
writer.writerow(
|
|
[
|
|
pc.equipment_code,
|
|
pc.oem_name or "Asamblat",
|
|
pc.motherboard,
|
|
pc.cpu,
|
|
pc.ram,
|
|
pc.storage,
|
|
pc.mac_address,
|
|
owner,
|
|
pc.get_status_display(),
|
|
]
|
|
)
|
|
|
|
writer.writerow([]) # Rând liber pentru spațiere
|
|
writer.writerow([])
|
|
|
|
# ==========================================
|
|
# 2. SECȚIUNEA MONITOARE
|
|
# ==========================================
|
|
writer.writerow(["--- MONITOARE ---"])
|
|
writer.writerow(
|
|
[
|
|
"Cod Inventar",
|
|
"Model / Nume",
|
|
"Diagonală",
|
|
"Număr Serial",
|
|
"Utilizator Curent",
|
|
"Status",
|
|
]
|
|
)
|
|
|
|
monitors = Monitor.objects.filter(status="activ").order_by("assigned_to__name")
|
|
for mon in monitors:
|
|
owner = mon.assigned_to.name if mon.assigned_to else "Magazie (Neasignat)"
|
|
writer.writerow(
|
|
[
|
|
mon.equipment_code,
|
|
mon.name,
|
|
mon.screen_diagonal,
|
|
mon.serial_number,
|
|
owner,
|
|
mon.get_status_display(),
|
|
]
|
|
)
|
|
|
|
writer.writerow([]) # Rând liber pentru spațiere
|
|
writer.writerow([])
|
|
|
|
# ==========================================
|
|
# 3. SECȚIUNEA PERIFERICE
|
|
# ==========================================
|
|
writer.writerow(["--- PERIFERICE ---"])
|
|
writer.writerow(
|
|
[
|
|
"Cod Inventar",
|
|
"Tip Echipament",
|
|
"Model / Nume",
|
|
"Număr Serial",
|
|
"Utilizator Curent",
|
|
"Status",
|
|
]
|
|
)
|
|
|
|
peripherals = Peripheral.objects.filter(status="activ").order_by(
|
|
"assigned_to__name"
|
|
)
|
|
for per in peripherals:
|
|
owner = per.assigned_to.name if per.assigned_to else "Magazie (Neasignat)"
|
|
writer.writerow(
|
|
[
|
|
per.equipment_code,
|
|
per.get_peripheral_type_display(),
|
|
per.name,
|
|
per.serial_number,
|
|
owner,
|
|
per.get_status_display(),
|
|
]
|
|
)
|
|
|
|
return response
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
@login_required
|
|
def maintenance_report(request):
|
|
# Preluăm datele exact așa cum le introduce utilizatorul (ex: 20.05.2026 sau 20-05-2026)
|
|
start_date_raw = request.GET.get("start_date")
|
|
end_date_raw = request.GET.get("end_date")
|
|
|
|
records = None
|
|
|
|
# Funcție ajutătoare pentru a traduce formatul românesc în formatul bazei de date
|
|
def parse_romanian_date(date_str):
|
|
if not date_str:
|
|
return None
|
|
|
|
clean_str = str(date_str).strip()
|
|
|
|
# Unificăm separatorii (transformăm cratima în punct pentru a verifica mai ușor)
|
|
clean_str = clean_str.replace("-", ".")
|
|
|
|
try:
|
|
# Traducem din ZZ.LL.AAAA în AAAA-LL-ZZ
|
|
parsed = datetime.strptime(clean_str, "%d.%m.%Y")
|
|
return parsed.strftime("%Y-%m-%d")
|
|
except ValueError:
|
|
# Dacă deja este în formatul corect (ex. a fost introdus manual AAAA-LL-ZZ), îl lăsăm așa
|
|
return clean_str
|
|
|
|
# Convertim datele pentru baza de date
|
|
start_date_db = parse_romanian_date(start_date_raw)
|
|
end_date_db = parse_romanian_date(end_date_raw)
|
|
|
|
if start_date_db and end_date_db:
|
|
records = MaintenanceRecord.objects.filter(
|
|
date__range=[start_date_db, end_date_db]
|
|
).order_by("-date")
|
|
|
|
context = {
|
|
"records": records,
|
|
"start_date": start_date_raw, # Trimitem înapoi formatul original pentru a rămâne afișat în formular
|
|
"end_date": end_date_raw,
|
|
}
|
|
return render(request, "equipment/maintenance_report.html", context)
|