fix: filtrado de partidas por nomenclatura de documento (core/partida_docs)

Frontera (_|.|$) tras vu_PT_{app}_{numero} para cubrir los 3 formatos sin
confundir partida 1 con 11/100. Fuente unica en core/partida_docs.py, reusada
por get_documentos, handlers de borrado/descarga y fix_partidas_error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-24 07:31:07 -06:00
parent d732602775
commit 244bbcb21c
6 changed files with 316 additions and 43 deletions

View File

@@ -60,7 +60,9 @@ from django.core.files.base import ContentFile
from django.db import transaction
from rest_framework.parsers import MultiPartParser, FormParser
from api.record.models import Document, DocumentType, Fuente
from unicodedata import normalize
from core.partida_docs import es_doc_de_partida
from collections import defaultdict
from unicodedata import normalize
from datetime import datetime
from django.utils import timezone
# Importar rarfile de manera opcional
@@ -2354,6 +2356,41 @@ class PartidaViewSet(viewsets.ModelViewSet):
return qs.filter(pedimento__contribuyente__in=user.rfc.all())
return Partida.objects.none()
def list(self, request, *args, **kwargs):
# Precarga los documentos de la página en una sola consulta para evitar
# el N+1 de get_documentos (una consulta regex por cada partida).
queryset = self.filter_queryset(self.get_queryset()).select_related('pedimento')
page = self.paginate_queryset(queryset)
objetos = page if page is not None else list(queryset)
ctx = self.get_serializer_context()
ctx['docs_por_partida'] = self._mapa_docs_partida(objetos)
serializer = self.get_serializer(objetos, many=True, context=ctx)
if page is not None:
return self.get_paginated_response(serializer.data)
return Response(serializer.data)
def _mapa_docs_partida(self, partidas):
"""Asigna los documentos de respuesta (tipo 1) de los pedimentos de la
página a cada partida por nombre de archivo, en memoria.
Devuelve {(pedimento_id, numero_partida): [Document, ...]}."""
ped_ids = {p.pedimento_id for p in partidas}
if not ped_ids:
return {}
docs_por_ped = defaultdict(list)
qs = Document.objects.filter(
pedimento_id__in=ped_ids, document_type_id=1,
).select_related('pedimento')
for d in qs:
docs_por_ped[d.pedimento_id].append(d)
mapa = {}
for p in partidas:
app = p.pedimento.pedimento_app
mapa[(p.pedimento_id, p.numero_partida)] = [
d for d in docs_por_ped.get(p.pedimento_id, [])
if es_doc_de_partida(d.archivo.name, app, p.numero_partida)
]
return mapa
def perform_create(self, serializer):
if is_internal_service_request(self.request):
serializer.save()