Initial commit
This commit is contained in:
354
gestion_pedidos/models.py
Normal file
354
gestion_pedidos/models.py
Normal file
@@ -0,0 +1,354 @@
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user