from django.db import models from django.core.validators import MinValueValidator from django.utils import timezone class Cliente(models.Model): """Cliente con información del vehículo""" nombre = models.CharField(max_length=200) matricula_vehiculo = models.CharField(max_length=20, unique=True) telefono = models.CharField(max_length=20, blank=True, null=True) email = models.EmailField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'clientes' indexes = [ models.Index(fields=['matricula_vehiculo']), models.Index(fields=['nombre']), ] def __str__(self): return f"{self.nombre} - {self.matricula_vehiculo}" class PedidoCliente(models.Model): """Pedido de recambios de un cliente""" ESTADOS = [ ('pendiente_revision', 'Pendiente Revisión'), ('en_revision', 'En Revisión'), ('pendiente_materiales', 'Pendiente Materiales'), ('completado', 'Completado'), ] numero_pedido = models.CharField(max_length=50, unique=True, db_index=True) cliente = models.ForeignKey(Cliente, on_delete=models.CASCADE, related_name='pedidos') fecha_pedido = models.DateTimeField(default=timezone.now) fecha_cita = models.DateTimeField(blank=True, null=True) estado = models.CharField(max_length=30, choices=ESTADOS, default='pendiente_revision') presupuesto_id = models.CharField(max_length=50, blank=True, null=True) archivo_pdf_path = models.CharField(max_length=500, blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'pedidos_cliente' indexes = [ models.Index(fields=['numero_pedido']), models.Index(fields=['estado']), models.Index(fields=['fecha_cita']), models.Index(fields=['fecha_pedido']), ] ordering = ['-fecha_pedido'] def __str__(self): return f"Pedido {self.numero_pedido} - {self.cliente.matricula_vehiculo}" @property def es_urgente(self): """Verifica si el pedido es urgente (menos de 12 horas para la cita)""" if not self.fecha_cita: return False tiempo_restante = self.fecha_cita - timezone.now() return tiempo_restante.total_seconds() < 12 * 3600 and tiempo_restante.total_seconds() > 0 class ReferenciaPedidoCliente(models.Model): """Referencias (piezas) de un pedido de cliente""" ESTADOS = [ ('pendiente', 'Pendiente'), ('parcial', 'Parcial'), ('completo', 'Completo'), ] pedido_cliente = models.ForeignKey( PedidoCliente, on_delete=models.CASCADE, related_name='referencias' ) referencia = models.CharField(max_length=100, db_index=True) denominacion = models.CharField(max_length=500) unidades_solicitadas = models.IntegerField(validators=[MinValueValidator(1)]) unidades_en_stock = models.IntegerField(default=0, validators=[MinValueValidator(0)]) unidades_pendientes = models.IntegerField(default=0, validators=[MinValueValidator(0)]) estado = models.CharField(max_length=20, choices=ESTADOS, default='pendiente') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'referencias_pedido_cliente' indexes = [ models.Index(fields=['referencia']), models.Index(fields=['estado']), models.Index(fields=['pedido_cliente', 'estado']), ] def __str__(self): return f"{self.referencia} - {self.denominacion} ({self.unidades_solicitadas} unidades)" def calcular_estado(self): """Calcula el estado basado en unidades""" if self.unidades_pendientes <= 0: return 'completo' elif self.unidades_pendientes < self.unidades_solicitadas: return 'parcial' return 'pendiente' def save(self, *args, **kwargs): self.unidades_pendientes = max(0, self.unidades_solicitadas - self.unidades_en_stock) self.estado = self.calcular_estado() super().save(*args, **kwargs) class Proveedor(models.Model): """Proveedor de recambios""" nombre = models.CharField(max_length=200, db_index=True) email = models.EmailField(blank=True, null=True) tiene_web = models.BooleanField(default=True) activo = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'proveedores' indexes = [ models.Index(fields=['nombre']), models.Index(fields=['activo']), ] def __str__(self): return self.nombre class PedidoProveedor(models.Model): """Pedido realizado a un proveedor""" ESTADOS = [ ('pendiente_recepcion', 'Pendiente Recepción'), ('parcial', 'Parcial'), ('completado', 'Completado'), ('cancelado', 'Cancelado'), ] TIPOS = [ ('web', 'Web'), ('manual', 'Manual'), ] proveedor = models.ForeignKey( Proveedor, on_delete=models.CASCADE, related_name='pedidos' ) numero_pedido = models.CharField(max_length=100, blank=True, null=True) fecha_pedido = models.DateTimeField(default=timezone.now) email_confirmacion_path = models.CharField(max_length=500, blank=True, null=True) estado = models.CharField(max_length=30, choices=ESTADOS, default='pendiente_recepcion') tipo = models.CharField(max_length=20, choices=TIPOS, default='web') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'pedidos_proveedor' indexes = [ models.Index(fields=['proveedor', 'estado']), models.Index(fields=['fecha_pedido']), models.Index(fields=['estado']), ] ordering = ['-fecha_pedido'] def __str__(self): return f"Pedido {self.numero_pedido or self.id} - {self.proveedor.nombre}" class ReferenciaPedidoProveedor(models.Model): """Referencias pedidas a un proveedor""" ESTADOS = [ ('pendiente', 'Pendiente'), ('parcial', 'Parcial'), ('recibido', 'Recibido'), ] pedido_proveedor = models.ForeignKey( PedidoProveedor, on_delete=models.CASCADE, related_name='referencias' ) referencia_pedido_cliente = models.ForeignKey( ReferenciaPedidoCliente, on_delete=models.CASCADE, related_name='pedidos_proveedor', blank=True, null=True ) referencia = models.CharField(max_length=100, db_index=True) denominacion = models.CharField(max_length=500) unidades_pedidas = models.IntegerField(validators=[MinValueValidator(1)]) unidades_recibidas = models.IntegerField(default=0, validators=[MinValueValidator(0)]) estado = models.CharField(max_length=20, choices=ESTADOS, default='pendiente') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'referencias_pedido_proveedor' indexes = [ models.Index(fields=['referencia']), models.Index(fields=['estado']), models.Index(fields=['pedido_proveedor', 'estado']), ] def __str__(self): return f"{self.referencia} - {self.denominacion} ({self.unidades_pedidas} unidades)" class Albaran(models.Model): """Albarán recibido de un proveedor""" ESTADOS_PROCESADO = [ ('pendiente', 'Pendiente'), ('procesado', 'Procesado'), ('clasificacion', 'Pendiente Clasificación'), ('error', 'Error'), ] proveedor = models.ForeignKey( Proveedor, on_delete=models.CASCADE, related_name='albaranes', blank=True, null=True ) numero_albaran = models.CharField(max_length=100, blank=True, null=True) fecha_albaran = models.DateField(blank=True, null=True) archivo_path = models.CharField(max_length=500) estado_procesado = models.CharField( max_length=30, choices=ESTADOS_PROCESADO, default='pendiente' ) fecha_procesado = models.DateTimeField(blank=True, null=True) datos_ocr = models.JSONField(default=dict, blank=True) # Datos extraídos por OCR created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'albaranes' indexes = [ models.Index(fields=['proveedor', 'estado_procesado']), models.Index(fields=['numero_albaran']), models.Index(fields=['fecha_albaran']), models.Index(fields=['estado_procesado']), ] ordering = ['-created_at'] def __str__(self): return f"Albarán {self.numero_albaran or self.id} - {self.proveedor.nombre if self.proveedor else 'Sin proveedor'}" class ReferenciaAlbaran(models.Model): """Referencias contenidas en un albarán""" TIPOS_IMPUESTO = [ ('21', '21%'), ('10', '10%'), ('7', '7%'), ('4', '4%'), ('3', '3%'), ('0', '0%'), ] albaran = models.ForeignKey( Albaran, on_delete=models.CASCADE, related_name='referencias' ) referencia = models.CharField(max_length=100, db_index=True) denominacion = models.CharField(max_length=500) unidades = models.IntegerField(validators=[MinValueValidator(1)]) precio_unitario = models.DecimalField(max_digits=10, decimal_places=2, default=0) impuesto_tipo = models.CharField(max_length=5, choices=TIPOS_IMPUESTO, default='21') impuesto_valor = models.DecimalField(max_digits=10, decimal_places=2, default=0) referencia_pedido_proveedor = models.ForeignKey( ReferenciaPedidoProveedor, on_delete=models.SET_NULL, related_name='referencias_albaran', blank=True, null=True ) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'referencias_albaran' indexes = [ models.Index(fields=['referencia']), models.Index(fields=['albaran']), ] def __str__(self): return f"{self.referencia} - {self.denominacion} ({self.unidades} unidades)" class Devolucion(models.Model): """Devolución de material a proveedor""" ESTADOS_ABONO = [ ('pendiente', 'Pendiente Abono'), ('abonado', 'Abonado'), ] proveedor = models.ForeignKey( Proveedor, on_delete=models.CASCADE, related_name='devoluciones' ) referencia = models.CharField(max_length=100, db_index=True) denominacion = models.CharField(max_length=500, blank=True, null=True) unidades = models.IntegerField(validators=[MinValueValidator(1)]) fecha_devolucion = models.DateTimeField(default=timezone.now) estado_abono = models.CharField(max_length=20, choices=ESTADOS_ABONO, default='pendiente') albaran_abono = models.ForeignKey( Albaran, on_delete=models.SET_NULL, related_name='devoluciones', blank=True, null=True ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'devoluciones' indexes = [ models.Index(fields=['proveedor', 'estado_abono']), models.Index(fields=['referencia']), models.Index(fields=['estado_abono']), ] ordering = ['-fecha_devolucion'] def __str__(self): return f"Devolución {self.referencia} - {self.proveedor.nombre} ({self.unidades} unidades)" class StockReferencia(models.Model): """Stock disponible de una referencia""" referencia = models.CharField(max_length=100, unique=True, db_index=True) unidades_disponibles = models.IntegerField(default=0, validators=[MinValueValidator(0)]) ultima_actualizacion = models.DateTimeField(auto_now=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'stock_referencias' indexes = [ models.Index(fields=['referencia']), ] def __str__(self): return f"{self.referencia} - {self.unidades_disponibles} unidades"