diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 210aa89..03da944 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/settings.py b/config/settings.py index 6ec8df1..5432d61 100644 --- a/config/settings.py +++ b/config/settings.py @@ -41,6 +41,11 @@ CSRF_TRUSTED_ORIGINS = [ for host in CSRF_TRUSTED_ORIGINS ] +SESSION_COOKIE_SECURE = True +CSRF_COOKIE_SECURE = True +SESSION_COOKIE_SAMESITE = "None" +CSRF_COOKIE_SAMESITE = "None" + # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 34a7528..05c4b0f 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/apps.cpython-311.pyc b/core/__pycache__/apps.cpython-311.pyc index 7ad0138..9d653a0 100644 Binary files a/core/__pycache__/apps.cpython-311.pyc and b/core/__pycache__/apps.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index e5de757..c6dd706 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/signals.cpython-311.pyc b/core/__pycache__/signals.cpython-311.pyc new file mode 100644 index 0000000..790288c Binary files /dev/null and b/core/__pycache__/signals.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index ec30d5e..694d76b 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index d550545..1cce771 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index 8bea5ae..b12d16b 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,5 +1,29 @@ from django.contrib import admin -from .models import MachineModel, SparePart, ServiceIntervention +from django.contrib.auth.admin import UserAdmin as BaseUserAdmin +from django.contrib.auth.models import User +from .models import MachineModel, SparePart, ServiceIntervention, TechnicianProfile + +# Define an inline admin descriptor for TechnicianProfile model +# which acts a bit like a singleton +class TechnicianProfileInline(admin.StackedInline): + model = TechnicianProfile + can_delete = False + verbose_name_plural = 'Perfil de Técnico' + +# Define a new User admin +class UserAdmin(BaseUserAdmin): + inlines = (TechnicianProfileInline,) + +# Re-register UserAdmin +admin.site.unregister(User) +admin.site.register(User, UserAdmin) + +@admin.register(TechnicianProfile) +class TechnicianProfileAdmin(admin.ModelAdmin): + list_display = ('user', 'is_technician') + list_filter = ('is_technician',) + search_fields = ('user__username', 'user__first_name', 'user__last_name') + @admin.register(MachineModel) class MachineModelAdmin(admin.ModelAdmin): diff --git a/core/apps.py b/core/apps.py index 8115ae6..da7b127 100644 --- a/core/apps.py +++ b/core/apps.py @@ -4,3 +4,6 @@ from django.apps import AppConfig class CoreConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'core' + + def ready(self): + import core.signals diff --git a/core/migrations/0004_technicianprofile.py b/core/migrations/0004_technicianprofile.py new file mode 100644 index 0000000..d798f6e --- /dev/null +++ b/core/migrations/0004_technicianprofile.py @@ -0,0 +1,29 @@ +# Generated by Django 5.2.7 on 2025-11-27 08:04 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_alter_machinemodel_options_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='TechnicianProfile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('pin', models.CharField(max_length=4, verbose_name='PIN')), + ('is_technician', models.BooleanField(default=True, verbose_name='Es técnico')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='technician_profile', to=settings.AUTH_USER_MODEL, verbose_name='Usuario')), + ], + options={ + 'verbose_name': 'Perfil de Técnico', + 'verbose_name_plural': 'Perfiles de Técnico', + }, + ), + ] diff --git a/core/migrations/__pycache__/0004_technicianprofile.cpython-311.pyc b/core/migrations/__pycache__/0004_technicianprofile.cpython-311.pyc new file mode 100644 index 0000000..1df6142 Binary files /dev/null and b/core/migrations/__pycache__/0004_technicianprofile.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 9b991e6..b764e51 100644 --- a/core/models.py +++ b/core/models.py @@ -47,4 +47,16 @@ class ServiceIntervention(models.Model): verbose_name_plural = "Intervenciones de Servicio" def __str__(self): - return f"Intervención para {self.machine} el {self.created_at.strftime('%d-%m-%Y')}" \ No newline at end of file + return f"Intervención para {self.machine} el {self.created_at.strftime('%d-%m-%Y')}" + +class TechnicianProfile(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='technician_profile', verbose_name="Usuario") + pin = models.CharField("PIN", max_length=4) + is_technician = models.BooleanField("Es técnico", default=True) + + class Meta: + verbose_name = "Perfil de Técnico" + verbose_name_plural = "Perfiles de Técnico" + + def __str__(self): + return self.user.get_full_name() or self.user.username \ No newline at end of file diff --git a/core/signals.py b/core/signals.py new file mode 100644 index 0000000..2419fa2 --- /dev/null +++ b/core/signals.py @@ -0,0 +1,9 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.contrib.auth.models import User +from .models import TechnicianProfile + +@receiver(post_save, sender=User) +def create_user_profile(sender, instance, created, **kwargs): + if created: + TechnicianProfile.objects.create(user=instance) diff --git a/core/templates/core/technician_login.html b/core/templates/core/technician_login.html new file mode 100644 index 0000000..1edd6e6 --- /dev/null +++ b/core/templates/core/technician_login.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
+
+
+
+
+

Acceso para Técnicos

+
+ {% csrf_token %} + {% if messages %} + {% for message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} +
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 332eff0..8088515 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,9 +1,8 @@ from django.urls import path -from django.urls import path - -from .views import index +from .views import index, technician_login urlpatterns = [ path("", index, name="index"), + path("login/", technician_login, name="technician_login"), ] diff --git a/core/views.py b/core/views.py index f306c47..68046ee 100644 --- a/core/views.py +++ b/core/views.py @@ -2,10 +2,13 @@ import os import platform from django import get_version as django_version -from django.shortcuts import render +from django.contrib.auth import login +from django.contrib.auth.models import User +from django.contrib import messages +from django.shortcuts import render, redirect from django.utils import timezone -from .models import MachineModel, SparePart, ServiceIntervention +from .models import MachineModel, SparePart, ServiceIntervention, TechnicianProfile def index(request): @@ -32,4 +35,24 @@ def index(request): def article_detail(request): - return render(request, "core/article_detail.html") \ No newline at end of file + return render(request, "core/article_detail.html") + +def technician_login(request): + if request.method == 'POST': + technician_id = request.POST.get('technician') + pin = request.POST.get('pin') + + try: + user = User.objects.get(id=technician_id, technician_profile__is_technician=True, is_active=True) + if user.technician_profile.pin == pin: + login(request, user) + return redirect('index') + else: + messages.error(request, 'PIN incorrecto.') + except User.DoesNotExist: + messages.error(request, 'Técnico no encontrado.') + + return redirect('technician_login') + + technicians = User.objects.filter(technician_profile__is_technician=True, is_active=True) + return render(request, 'core/technician_login.html', {'technicians': technicians})