diff --git a/api/customs/serializers.py b/api/customs/serializers.py index 0da90a0..89e8bf0 100644 --- a/api/customs/serializers.py +++ b/api/customs/serializers.py @@ -46,6 +46,60 @@ class PartidaSerializer(serializers.ModelSerializer): class Meta: model = Partida fields = '__all__' + read_only_fields = ('created_at', 'updated_at', 'numero_partida') + + def validate_unique(self, attrs): + """ + Sobrescribe la validación de unicidad para manejar correctamente + las actualizaciones de registros existentes. + """ + # Si estamos actualizando un registro existente, excluirlo de la validación + if self.instance: + # Para actualizaciones, crear una instancia temporal con los nuevos datos + # pero sin guardarla, solo para validar unicidad excluyendo el registro actual + exclude = {'id': self.instance.id} + else: + # Para creaciones nuevas, no excluir nada + exclude = {} + + # Crear una instancia temporal con los datos combinados + if self.instance: + # Combinar datos existentes con los nuevos + combined_attrs = {} + for field in self.Meta.model._meta.fields: + field_name = field.name + if field_name in attrs: + combined_attrs[field_name] = attrs[field_name] + elif hasattr(self.instance, field_name): + combined_attrs[field_name] = getattr(self.instance, field_name) + else: + combined_attrs = attrs + + # Verificar unique_together manualmente para pedimento + numero_partida + if 'pedimento' in combined_attrs and 'numero_partida' in combined_attrs: + queryset = self.Meta.model.objects.filter( + pedimento=combined_attrs['pedimento'], + numero_partida=combined_attrs['numero_partida'] + ) + + # Si estamos actualizando, excluir el registro actual + if self.instance: + queryset = queryset.exclude(id=self.instance.id) + + if queryset.exists(): + raise serializers.ValidationError({ + 'non_field_errors': [ + f'Ya existe una partida con el número {combined_attrs["numero_partida"]} para este pedimento.' + ] + }) + + def validate(self, data): + """ + Validación adicional personalizada. + """ + # Llamar a la validación de unicidad personalizada + self.validate_unique(data) + return data class TipoOperacionSerializer(serializers.ModelSerializer): class Meta: diff --git a/api/customs/tasks/microservice_v2.py b/api/customs/tasks/microservice_v2.py new file mode 100644 index 0000000..2754306 --- /dev/null +++ b/api/customs/tasks/microservice_v2.py @@ -0,0 +1,269 @@ + +from celery import shared_task, group +from api.customs.models import * +from api.customs.serializers import PedimentoSerializer +from api.vucem.models import * +import requests +from config.settings import SERVICE_API_URL_V2 +from datetime import datetime +import json + +def credenciales_to_dict(credenciales): + if not credenciales: + return {} + return { + "id": str(credenciales.id), + "user": credenciales.usuario, + "password": credenciales.password, + "efirma": credenciales.efirma, + "key": credenciales.key.url if credenciales.key else None, + "cer": credenciales.cer.url if credenciales.cer else None, + "is_active": credenciales.is_active, + "organizacion": str(credenciales.organizacion.id) if credenciales.organizacion else None, + } + +def pedimento_to_dict(pedimento): + return { + "id": str(pedimento.id), + "pedimento": str(pedimento.pedimento), + "pedimento_app": str(pedimento.pedimento_app), + "aduana": str(pedimento.aduana), + "patente": str(pedimento.patente), + "organizacion": str(pedimento.organizacion.id), # nunca None + "regimen": str(pedimento.regimen or ""), + "clave_pedimento": str(pedimento.clave_pedimento or ""), + "numero_operacion": str(pedimento.numero_operacion or "") + } + +def cove_to_dict(cove): + return { + "id": cove.id, + "cove": str(cove.numero_cove), + } + +def edoc_to_dict(edoc): + return { + "id": edoc.id, + "numero_edocument": str(edoc.numero_edocument), + } + +def partida_to_dict(partida): + return { + "id": partida.id, + "numero": partida.numero_partida, + } + +@shared_task +def procesar_pedimentos_completos(organizacion_id): + pedimentos = Pedimento.objects.filter(organizacion_id=organizacion_id) + respuestas = [] + for pedimento in pedimentos[:10]: + if not pedimento.documents.filter(document_type=2).exists(): # Tipo 2: Pedimento Completo + # Convertir el pedimento a JSON usando el serializer + pedimento_dict = pedimento_to_dict(pedimento) + credenciales = Vucem.objects.filter(id=CredencialesImportador.objects.filter(rfc=pedimento.contribuyente).first().vucem.id).first() + + credenciales_dict = credenciales_to_dict(credenciales) + + payload = { + "pedimento": pedimento_dict, + "credencial": credenciales_dict + } + + + response = requests.post( + f"{SERVICE_API_URL_V2}/services/pedimento_completo", + data=json.dumps(payload), + headers={"Content-Type": "application/json"} + ) + # Aquí puedes continuar con el resto de tu lógica + + respuestas.append(response.json()) + return respuestas + +@shared_task +def procesar_remesas(organizacion_id): + pedimentos = Pedimento.objects.filter(organizacion_id=organizacion_id) + respuestas = [] + for pedimento in pedimentos: + if not pedimento.documents.filter(document_type=3).exists(): # Tipo 3: Remesa + # Convertir el pedimento a JSON usando el serializer + pedimento_dict = pedimento_to_dict(pedimento) + credenciales = Vucem.objects.filter(id=CredencialesImportador.objects.filter(rfc=pedimento.contribuyente).first().vucem.id).first() + + credenciales_dict = credenciales_to_dict(credenciales) + + payload = { + "pedimento": pedimento_dict, + "credencial": credenciales_dict + } + + + response = requests.post( + f"{SERVICE_API_URL_V2}/services/remesas", + data=json.dumps(payload), + headers={"Content-Type": "application/json"} + ) + # Aquí puedes continuar con el resto de tu lógica + + respuestas.append(response.json()) + return respuestas + +@shared_task +def procesar_coves(organizacion_id): + pedimentos = Pedimento.objects.filter( + organizacion_id=organizacion_id, + coves__isnull=False + ).distinct() + respuestas = [] + for pedimento in pedimentos[:10]: + if pedimento.coves.filter(cove_descargado=False).exists(): # Tipo 3: Remesa + + # Convertir el pedimento a JSON usando el serializer + pedimento_dict = pedimento_to_dict(pedimento) + credenciales = Vucem.objects.filter(id=CredencialesImportador.objects.filter(rfc=pedimento.contribuyente).first().vucem.id).first() + + credenciales_dict = credenciales_to_dict(credenciales) + + payload = { + "coves": [cove_to_dict(cove) for cove in pedimento.coves.filter(cove_descargado=False)], + "pedimento": pedimento_dict, + "credencial": credenciales_dict + } + + response = requests.post( + f"{SERVICE_API_URL_V2}/services/all/coves", + data=json.dumps(payload), + headers={"Content-Type": "application/json"} + ) + # Aquí puedes continuar con el resto de tu lógica + + respuestas.append(response.json()) + return respuestas + +@shared_task +def procesar_acuse_coves(organizacion_id): + pedimentos = Pedimento.objects.filter( + organizacion_id=organizacion_id, + coves__isnull=False + ).distinct() + respuestas = [] + for pedimento in pedimentos[10:20]: + if pedimento.coves.filter(acuse_cove_descargado=False).exists(): # Tipo 3: Remesa + + # Convertir el pedimento a JSON usando el serializer + pedimento_dict = pedimento_to_dict(pedimento) + credenciales = Vucem.objects.filter(id=CredencialesImportador.objects.filter(rfc=pedimento.contribuyente).first().vucem.id).first() + + credenciales_dict = credenciales_to_dict(credenciales) + + payload = { + "coves": [cove_to_dict(cove) for cove in pedimento.coves.filter(acuse_cove_descargado=False)], + "pedimento": pedimento_dict, + "credencial": credenciales_dict + } + + response = requests.post( + f"{SERVICE_API_URL_V2}/services/all/acuse/cove/", + data=json.dumps(payload), + headers={"Content-Type": "application/json"} + ) + # Aquí puedes continuar con el resto de tu lógica + + respuestas.append(response.json()) + return respuestas + +@shared_task +def procesar_acuses(organizacion_id): + pedimentos = Pedimento.objects.filter( + organizacion_id=organizacion_id, + documentos__isnull=False + ).distinct() + respuestas = [] + for pedimento in pedimentos[:10]: + if pedimento.documentos.filter(acuse_descargado=False).exists(): # Tipo 3: Remesa + + # Convertir el pedimento a JSON usando el serializer + pedimento_dict = pedimento_to_dict(pedimento) + credenciales = Vucem.objects.filter(id=CredencialesImportador.objects.filter(rfc=pedimento.contribuyente).first().vucem.id).first() + + credenciales_dict = credenciales_to_dict(credenciales) + + payload = { + "edocs": [edoc_to_dict(edoc) for edoc in pedimento.documentos.filter(acuse_descargado=False)], + "pedimento": pedimento_dict, + "credencial": credenciales_dict + } + + response = requests.post( + f"{SERVICE_API_URL_V2}/services/all/acuse/pedimento/", + data=json.dumps(payload), + headers={"Content-Type": "application/json"} + ) + # Aquí puedes continuar con el resto de tu lógica + + respuestas.append(response.json()) + return respuestas + +@shared_task +def procesar_edocs(organizacion_id): + pedimentos = Pedimento.objects.filter( + organizacion_id=organizacion_id, + documentos__isnull=False + ).distinct() + respuestas = [] + for pedimento in pedimentos[:10]: + if pedimento.documentos.filter(edocument_descargado=False).exists(): # Tipo 3: Remesa + + # Convertir el pedimento a JSON usando el serializer + pedimento_dict = pedimento_to_dict(pedimento) + credenciales = Vucem.objects.filter(id=CredencialesImportador.objects.filter(rfc=pedimento.contribuyente).first().vucem.id).first() + + credenciales_dict = credenciales_to_dict(credenciales) + + payload = { + "edocs": [edoc_to_dict(edoc) for edoc in pedimento.documentos.filter(edocument_descargado=False)], + "pedimento": pedimento_dict, + "credencial": credenciales_dict + } + + response = requests.post( + f"{SERVICE_API_URL_V2}/services/all/acuse/pedimento/", + data=json.dumps(payload), + headers={"Content-Type": "application/json"} + ) + # Aquí puedes continuar con el resto de tu lógica + + respuestas.append(response.json()) + return respuestas + +@shared_task +def procesar_partidas(organizacion_id): + pedimentos = Pedimento.objects.filter( + organizacion_id=organizacion_id, + partidas__isnull=False + ).distinct() + respuestas = [] + for pedimento in pedimentos[:10]: + if pedimento.partidas.filter(descargado=False).exists(): # Tipo 4: Partidas + # Convertir el pedimento a JSON usando el serializer + pedimento_dict = pedimento_to_dict(pedimento) + credenciales = Vucem.objects.filter(id=CredencialesImportador.objects.filter(rfc=pedimento.contribuyente).first().vucem.id).first() + + credenciales_dict = credenciales_to_dict(credenciales) + + payload = { + "partidas": [partida_to_dict(partida) for partida in pedimento.partidas.filter(descargado=False)], + "pedimento": pedimento_dict, + "credencial": credenciales_dict + } + + response = requests.post( + f"{SERVICE_API_URL_V2}/services/all/partidas/", + data=json.dumps(payload), + headers={"Content-Type": "application/json"} + ) + # Aquí puedes continuar con el resto de tu lógica + + respuestas.append(response.json()) + return respuestas \ No newline at end of file diff --git a/config/settings.py b/config/settings.py index 8012be0..44d4414 100644 --- a/config/settings.py +++ b/config/settings.py @@ -100,7 +100,7 @@ ALLOWED_HOSTS = [ SITE_URL = os.getenv('SITE_URL') SERVICE_API_URL = os.getenv('SERVICE_API_URL') - +SERVICE_API_URL_V2 = os.getenv('SERVICE_API_URL_V2') # Application definition BASE_APPS = [ 'django.contrib.admin', diff --git a/test.json b/test.json new file mode 100644 index 0000000..6ed38dc --- /dev/null +++ b/test.json @@ -0,0 +1,29 @@ +{ + "partidas": [ + { + "id": 88, + "numero": 1 + } + ], + "pedimento": { + "id": "c192e4b3-68b7-49fb-b600-6e5f5ae463ee", + "pedimento": "0000451", + "pedimento_app": "20-24-3910-0000451", + "aduana": "240", + "patente": "3910", + "organizacion": "9d705e97-d3f2-4b6c-8d92-9f1af2b2d4b4", + "regimen": "IMD", + "clave_pedimento": "A1", + "numero_operacion": "21612122481" + }, + "credencial": { + "id": "40d1d433-dba5-47a8-b00c-dce077c3c5cd", + "user": "TEC1406248Q2", + "password": "4Z+6Aulov0ohSWLda/INLyEm8p2xJSYK/iZnA+vjHZPDD4otHSBnr1p7GPugdwCh", + "efirma": "TXM241007", + "key": "/media/vucem_keys/TEC1406248Q2_clave_vJA0zau.key", + "cer": "/media/vucem_certs/TEC1406248Q2_certificado_Du8N2Q9.cer", + "is_active": true, + "organizacion": "9d705e97-d3f2-4b6c-8d92-9f1af2b2d4b4" + } +} \ No newline at end of file