From 81153f00b8bbea6f913fa61488bc5726eee50b62 Mon Sep 17 00:00:00 2001 From: kemuel Date: Thu, 13 Aug 2026 17:31:36 -0300 Subject: [PATCH] Fix(IntegracaoHub): materia pela generic FK e autor_nome de volta AB#1480 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dois achados do teste manual de integracao (13/08, ambiente real): 1. select_related('materia_gerada') estourava FieldError — o campo NAO existe no modelo: o bloco `materia_gerada = models.ForeignKey(...)` do materia/models.py esta DENTRO de uma docstring (codigo morto). O vinculo real e a generic FK conteudo_gerado_related (content_type + object_id), que aponta para MateriaLegislativa OU DocumentoAcessorio — so a materia interessa ao contrato. Sem isso, ProtocoloGerado nunca sairia. 2. O commit do autor_nome (ebf0fac) ficou fora do merge do PR #4 e o campo sumiu do poll. Sem ele o acervo perde o nome do autor nao mapeado, que e a regra do refinamento do historico §3.2. Reaplicado. Verificado no ambiente real: poll devolve as proposicoes do acervo com autor_nome e rascunho corretos. Co-Authored-By: Claude Opus 5 --- sapl/integracao_hub/serializacao.py | 19 +++++++++++++------ sapl/integracao_hub/views.py | 4 ++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/sapl/integracao_hub/serializacao.py b/sapl/integracao_hub/serializacao.py index ed8f1d581..acfefcc9a 100644 --- a/sapl/integracao_hub/serializacao.py +++ b/sapl/integracao_hub/serializacao.py @@ -14,13 +14,17 @@ def _iso(valor): def _materia_da_proposicao(proposicao): - materia = proposicao.materia_gerada - if materia is None: - conteudo = getattr(proposicao, 'conteudo_gerado_related', None) - if isinstance(conteudo, MateriaLegislativa): - materia = conteudo - if materia is None: + """A matéria gerada vem da generic FK, não de um campo direto. + + O `materia_gerada` que aparece no models.py está DENTRO de uma docstring + (código morto) — o vínculo real é `conteudo_gerado_related` + (content_type + object_id), que aponta para MateriaLegislativa OU para + DocumentoAcessorio. Só a matéria interessa ao contrato canônico. + """ + conteudo = proposicao.conteudo_gerado_related + if not isinstance(conteudo, MateriaLegislativa): return None + materia = conteudo return { 'id': materia.pk, 'tipo': { @@ -45,6 +49,9 @@ def serializar_proposicao(proposicao): 'descricao': proposicao.tipo.descricao if proposicao.tipo else None, }, 'autor': proposicao.autor_id, + # Nome para exibição no acervo do consumidor (refinamento do histórico §3.2): + # nunca usado para resolver identidade — contrato §3.2. + 'autor_nome': proposicao.autor.nome if proposicao.autor else None, 'ementa': proposicao.descricao, 'rascunho': proposicao.data_envio is None, 'cancelado': proposicao.cancelado, diff --git a/sapl/integracao_hub/views.py b/sapl/integracao_hub/views.py index 64d2b664d..17e890965 100644 --- a/sapl/integracao_hub/views.py +++ b/sapl/integracao_hub/views.py @@ -180,7 +180,7 @@ class ProposicoesCadastradasPollView(PollView): status=status.HTTP_400_BAD_REQUEST) itens = (Proposicao.objects .filter(id__gt=id_gt, cancelado=False) - .select_related('tipo', 'materia_gerada__tipo', 'content_type') + .select_related('tipo', 'autor', 'content_type') .order_by('id')[:self._limite(request)]) return self._resposta(itens, serializar_proposicao) @@ -197,7 +197,7 @@ class PollPorDataView(PollView): filtro = {'%s__gte' % self.campo_cursor: desde, 'cancelado': False} itens = (Proposicao.objects .filter(**filtro) - .select_related('tipo', 'materia_gerada__tipo', 'content_type') + .select_related('tipo', 'autor', 'content_type') .order_by(self.campo_cursor, 'id')[:self._limite(request)]) return self._resposta(itens, serializar_proposicao)