Files
backend/api/customs/models.py

261 lines
15 KiB
Python

import uuid
from django.db import models
# Create your models here.
class TipoOperacion(models.Model):
tipo = models.CharField(max_length=100)
descripcion = models.CharField(max_length=200)
def __str__(self):
return f"{self.tipo}"
class Meta:
verbose_name = "Tipo de Operacion"
verbose_name_plural = "Tipos de Operacion"
db_table = 'tipo_operacion'
ordering = ['tipo']
class Pedimento(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
pedimento = models.CharField(max_length=20, unique=False, help_text="Número de pedimento aduanal")
pedimento_app = models.CharField(max_length=25, unique=False, help_text="Número de pedimento en la aplicación")
organizacion = models.ForeignKey('organization.Organizacion', on_delete=models.CASCADE, related_name='pedimentos', help_text="Organización a la que pertenece el pedimento")
patente = models.CharField(max_length=20, blank=True, null=True, help_text="Número de patente aduanal")
aduana = models.CharField(max_length=10, blank=True, null=True, help_text="Clave de la aduana según la clasificación aduanera")
regimen = models.CharField(max_length=10, blank=True, null=True, help_text="Clave del régimen aduanero según la clasificación aduanera")
tipo_operacion = models.ForeignKey('TipoOperacion', on_delete=models.SET_NULL, blank=True, null=True, help_text="Tipo de operación del pedimento", related_name='pedimentos')
clave_pedimento = models.CharField(max_length=10, blank=True, null=True, help_text="Clave del pedimento según la clasificación aduanera")
fecha_inicio = models.DateField(help_text="Fecha de inicio del pedimento", blank=True, null=True)
fecha_fin = models.DateField(help_text="Fecha de fin del pedimento", blank=True, null=True)
fecha_pago = models.DateField(help_text="Fecha de pago del pedimento", blank=True, null=True)
alerta = models.BooleanField(default=False, help_text="Indica si el pedimento tiene una alerta asociada")
consultar_vucem = models.BooleanField(default=False, help_text="Solo pedimentos originados desde datastage deben consultar VUCEM automáticamente")
contribuyente = models.ForeignKey('Importador', on_delete=models.CASCADE, related_name='pedimentos', help_text="Contribuyente asociado al pedimento", blank=True, null=True)
agente_aduanal = models.CharField(max_length=100, blank=True, null=True, help_text="RFC del agente aduanal")
curp_apoderado = models.CharField(max_length=18, blank=True, null=True, help_text="CURP del apoderado aduanal")
importe_total = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, help_text="Importe total del pedimento")
saldo_disponible = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, help_text="Saldo disponible del pedimento")
importe_pedimento = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, help_text="Importe del pedimento")
existe_expediente = models.BooleanField(default=False)
remesas = models.BooleanField(default=False, help_text="Indica si el pedimento tiene remesas asociadas")
numero_partidas = models.PositiveIntegerField(default=0, help_text="Número de partidas asociadas al pedimento", blank=True, null=True)
numero_operacion = models.CharField(max_length=20, blank=True, null=True, help_text="Número de operación del pedimento")
created_at = models.DateTimeField(auto_now_add=True, help_text="Fecha de creación del registro")
updated_at = models.DateTimeField(auto_now=True, help_text="Fecha de última actualización del registro")
def __str__(self):
return f"{self.pedimento}"
class Meta:
verbose_name = "Pedimento"
verbose_name_plural = "Pedimentos"
db_table = 'pedimento'
ordering = ['pedimento']
unique_together = [
# ['organizacion', 'pedimento'],
['organizacion', 'pedimento_app']
]
class EstadoDescarga(models.TextChoices):
"""Estado de descarga de documentos VUCEM (requerimiento T2026-05-027):
'error' indica que la descarga no pudo completarse y requiere atención."""
PENDIENTE = 'pendiente', 'Pendiente'
DESCARGADO = 'descargado', 'Descargado'
ERROR = 'error', 'Error'
class Partida(models.Model):
pedimento = models.ForeignKey(Pedimento, on_delete=models.CASCADE, related_name='partidas', help_text="Pedimento asociado a la partida")
organizacion = models.ForeignKey('organization.Organizacion', on_delete=models.CASCADE, related_name='partidas', help_text="Organización a la que pertenece la partida")
numero_partida = models.PositiveIntegerField(help_text="Número de la partida dentro del pedimento")
descargado = models.BooleanField(default=False, help_text="Indica si la partida ha sido descargada")
created_at = models.DateTimeField(auto_now_add=True, help_text="Fecha de creación del registro")
updated_at = models.DateTimeField(auto_now=True, help_text="Fecha de última actualización del registro")
def __str__(self):
return f"Partida {self.numero_partida} del Pedimento {self.pedimento.pedimento}"
class Meta:
verbose_name = "Partida"
verbose_name_plural = "Partidas"
db_table = 'partida'
ordering = ['pedimento', 'numero_partida']
unique_together = ['pedimento', 'numero_partida'] # No puede existir el mismo número de partida para un pedimento
class EDocument(models.Model):
pedimento = models.ForeignKey(Pedimento, on_delete=models.CASCADE, related_name='documentos', help_text="Pedimento asociado al documento")
organizacion = models.ForeignKey('organization.Organizacion', on_delete=models.CASCADE, related_name='edocuments', help_text="Organización a la que pertenece el EDocument")
numero_edocument = models.CharField(max_length=20, unique=True, help_text="Número único del e-documento")
clave = models.CharField(max_length=10, blank=True, null=True, help_text="Clave del e-documento según la clasificación aduanera")
cadena_original = models.TextField(blank=True, null=True, help_text="Cadena original del e-documento")
sello_digital = models.TextField(blank=True, null=True, help_text="Firma digital del e-documento")
descripcion = models.CharField(max_length=200, blank=True, null=True, help_text="Descripción del documento")
created_at = models.DateTimeField(auto_now_add=True, help_text="Fecha de creación del documento")
updated_at = models.DateTimeField(auto_now=True, help_text="Fecha de última actualización del documento")
edocument_descargado = models.BooleanField(default=False, help_text="Indica si el e-documento ha sido descargado (legado, derivado de edocument_estado)")
acuse_descargado = models.BooleanField(default=False, help_text="Indica si el acuse del e-documento ha sido descargado (legado, derivado de acuse_estado)")
edocument_estado = models.CharField(max_length=12, choices=EstadoDescarga.choices, default=EstadoDescarga.PENDIENTE, help_text="Estado de descarga del e-documento: pendiente, descargado o error")
acuse_estado = models.CharField(max_length=12, choices=EstadoDescarga.choices, default=EstadoDescarga.PENDIENTE, help_text="Estado de descarga del acuse: pendiente, descargado o error")
edocument_intentos = models.PositiveSmallIntegerField(default=0, help_text="Intentos automáticos de descarga del e-documento (un ciclo de orquestación = un intento)")
acuse_intentos = models.PositiveSmallIntegerField(default=0, help_text="Intentos automáticos de descarga del acuse (un ciclo de orquestación = un intento)")
ultimo_intento_at = models.DateTimeField(null=True, blank=True, help_text="Fecha del último intento automático de descarga")
ultimo_error = models.TextField(null=True, blank=True, help_text="Detalle del último error de descarga")
def save(self, *args, **kwargs):
# El estado de 3 valores es la fuente de verdad; los booleanos legados se derivan
self.edocument_descargado = self.edocument_estado == EstadoDescarga.DESCARGADO
self.acuse_descargado = self.acuse_estado == EstadoDescarga.DESCARGADO
update_fields = kwargs.get('update_fields')
if update_fields is not None:
update_fields = set(update_fields)
if 'edocument_estado' in update_fields:
update_fields.add('edocument_descargado')
if 'acuse_estado' in update_fields:
update_fields.add('acuse_descargado')
kwargs['update_fields'] = list(update_fields)
super().save(*args, **kwargs)
def __str__(self):
return f"{self.descripcion} - {self.pedimento.pedimento}"
class Meta:
verbose_name = "EDocument"
verbose_name_plural = "EDocuments"
db_table = 'edocs'
ordering = ['created_at']
class Cove(models.Model):
pedimento = models.ForeignKey(Pedimento, on_delete=models.CASCADE, related_name='coves', help_text="Pedimento asociado a la cove")
organizacion = models.ForeignKey('organization.Organizacion', on_delete=models.CASCADE, related_name='coves', help_text="Organización a la que pertenece la cove")
numero_cove = models.CharField(max_length=20, unique=True, help_text="Número único de la cove")
created_at = models.DateTimeField(auto_now_add=True, help_text="Fecha de creación de la cove")
updated_at = models.DateTimeField(auto_now=True, help_text="Fecha de última actualización de la cove")
cove_descargado = models.BooleanField(default=False, help_text="Indica si la cove ha sido descargada (legado, derivado de cove_estado)")
acuse_cove_descargado = models.BooleanField(default=False, help_text="Indica si el acuse de la cove ha sido descargado (legado, derivado de acuse_cove_estado)")
cove_estado = models.CharField(max_length=12, choices=EstadoDescarga.choices, default=EstadoDescarga.PENDIENTE, help_text="Estado de descarga de la cove: pendiente, descargado o error")
acuse_cove_estado = models.CharField(max_length=12, choices=EstadoDescarga.choices, default=EstadoDescarga.PENDIENTE, help_text="Estado de descarga del acuse de la cove: pendiente, descargado o error")
cove_intentos = models.PositiveSmallIntegerField(default=0, help_text="Intentos automáticos de descarga de la cove (un ciclo de orquestación = un intento)")
acuse_cove_intentos = models.PositiveSmallIntegerField(default=0, help_text="Intentos automáticos de descarga del acuse de la cove (un ciclo de orquestación = un intento)")
ultimo_intento_at = models.DateTimeField(null=True, blank=True, help_text="Fecha del último intento automático de descarga")
ultimo_error = models.TextField(null=True, blank=True, help_text="Detalle del último error de descarga")
def save(self, *args, **kwargs):
# El estado de 3 valores es la fuente de verdad; los booleanos legados se derivan
self.cove_descargado = self.cove_estado == EstadoDescarga.DESCARGADO
self.acuse_cove_descargado = self.acuse_cove_estado == EstadoDescarga.DESCARGADO
update_fields = kwargs.get('update_fields')
if update_fields is not None:
update_fields = set(update_fields)
if 'cove_estado' in update_fields:
update_fields.add('cove_descargado')
if 'acuse_cove_estado' in update_fields:
update_fields.add('acuse_cove_descargado')
kwargs['update_fields'] = list(update_fields)
super().save(*args, **kwargs)
def __str__(self):
return f"{self.numero_cove} - {self.pedimento.pedimento}"
class Meta:
verbose_name = "Cove"
verbose_name_plural = "Coves"
db_table = 'coves'
ordering = ['created_at']
class EstadoDeProcesamiento(models.Model):
estado = models.CharField(max_length=50)
def __str__(self):
return self.estado
class Meta:
verbose_name = "Estado de Procesamiento"
verbose_name_plural = "Estados de Procesamiento"
db_table = 'estado_de_procesamiento'
ordering = ['estado']
class TipoDeProcesamiento(models.Model):
tipo = models.CharField(max_length=50)
def __str__(self):
return self.tipo
class Meta:
verbose_name = "Tipo de Procesamiento"
verbose_name_plural = "Tipos de Procesamiento"
db_table = 'tipo_de_procesamiento'
ordering = ['tipo']
class Servicio(models.Model):
endpoint = models.CharField(max_length=100)
descripcion = models.TextField(blank=True, null=True)
hora_inicio = models.TimeField(max_length=50, blank=True, null=True)
hora_fin = models.TimeField(max_length=50, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.endpoint
class Meta:
verbose_name = "Servicio"
verbose_name_plural = "Servicios"
db_table = 'servicio'
ordering = ['endpoint']
class ProcesamientoPedimento(models.Model):
organizacion = models.ForeignKey('organization.Organizacion', on_delete=models.CASCADE, related_name='procesamientos')
estado = models.ForeignKey(EstadoDeProcesamiento, on_delete=models.CASCADE, related_name='procesamientos')
tipo_procesamiento = models.ForeignKey(TipoDeProcesamiento, on_delete=models.CASCADE, related_name='procesamientos', blank=True, null=True)
pedimento = models.ForeignKey(Pedimento, on_delete=models.CASCADE, related_name='procesamientos')
servicio = models.ForeignKey(Servicio, on_delete=models.CASCADE, related_name='procesamientos', blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.pedimento.pedimento} - {self.estado.estado}"
class Meta:
verbose_name = "Procesamiento de Pedimento"
verbose_name_plural = "Procesamientos de Pedimento"
db_table = 'procesamiento_pedimento'
ordering = ['created_at']
class Regimen(models.Model):
id = models.AutoField(primary_key=True)
claveped = models.CharField(max_length=4)
regimenped = models.CharField(max_length=4)
tipo = models.IntegerField()
class Meta:
db_table = 'regimen'
verbose_name = 'Regimen'
verbose_name_plural = 'Regimenes'
def __str__(self):
return f"{self.claveped} - {self.regimenped} - {self.tipo}"
class Importador(models.Model):
rfc = models.CharField(primary_key=True, max_length=13, unique=True, help_text="RFC del importador")
nombre = models.CharField(max_length=200, help_text="Nombre del importador")
organizacion = models.ForeignKey('organization.Organizacion', on_delete=models.CASCADE, related_name='importadores', help_text="Organización a la que pertenece el importador")
created_at = models.DateTimeField(auto_now_add=True, help_text="Fecha de creación del registro")
updated_at = models.DateTimeField(auto_now=True, help_text="Fecha de última actualización del registro")
class Meta:
verbose_name = 'Importador'
verbose_name_plural = 'Importadores'
db_table = 'importador'
ordering = ['rfc']
def __str__(self):
return f"{self.rfc} - {self.nombre}"