Files
2025-12-05 11:27:16 -03:00

355 lines
12 KiB
Python

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"