Auto commit: 2025-11-27T08:45:23.463Z

This commit is contained in:
Flatlogic Bot 2025-11-27 08:45:23 +00:00
parent 1ff7bbc53c
commit 91ab4c4999
17 changed files with 151 additions and 8 deletions

View File

@ -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/

Binary file not shown.

View File

@ -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):

View File

@ -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

View File

@ -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',
},
),
]

View File

@ -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')}"
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

9
core/signals.py Normal file
View File

@ -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)

View File

@ -0,0 +1,39 @@
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card mt-5">
<div class="card-body">
<h3 class="card-title text-center">Acceso para Técnicos</h3>
<form method="post">
{% csrf_token %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-danger">{{ message }}</div>
{% endfor %}
{% endif %}
<div class="mb-3">
<label for="technician" class="form-label">Seleccione su nombre</label>
<select class="form-select" id="technician" name="technician">
{% for technician in technicians %}
<option value="{{ technician.id }}">{{ technician.get_full_name|default:technician.username }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="pin" class="form-label">PIN</label>
<input type="password" class="form-control" id="pin" name="pin" maxlength="4" inputmode="numeric" pattern="[0-9]*">
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Ingresar</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -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"),
]

View File

@ -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")
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})