Files
eumi_v4/equipment/views.py
T

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)