From e9dd39d664d7daffeb4743bc5decaa7c21517e6d Mon Sep 17 00:00:00 2001 From: KemuelAlves Date: Thu, 7 May 2026 14:32:50 -0300 Subject: [PATCH] =?UTF-8?q?Como=20cliente,=20quero=20que=20documentos=20n?= =?UTF-8?q?=C3=A3o=20assinados=20tenham=20filtro=20na=20tela=20de=20Minhas?= =?UTF-8?q?=20Mat=C3=A9rias=20[AB#1322]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/materia/forms.py | 50 ++++++++ sapl/materia/urls.py | 5 + sapl/materia/views.py | 51 +++++++- .../js/materia_pesquisa_download_pdfs.js | 22 ++++ .../materia/materialegislativa_filter.html | 35 +++++- .../materias_pendentes_assinatura_list.html | 119 ++++++++++++++++++ sapl/templates/navbar.yaml | 2 + 7 files changed, 279 insertions(+), 5 deletions(-) create mode 100644 sapl/templates/materia/materias_pendentes_assinatura_list.html diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index ec2afb73e..eb6de00a4 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -979,12 +979,26 @@ class AnexadaForm(ModelForm): fields = ['tipo', 'numero', 'ano', 'data_anexacao', 'data_desanexacao'] +CHOICE_STATUS_ASSINATURA = [ + ('', _('Todas')), + ('pendente', _('⚠ Pendente de Assinatura')), + ('assinada', _('✔ Assinada')), +] + + class MateriaLegislativaFilterSet(django_filters.FilterSet): ano = django_filters.ChoiceFilter(required=False, label='Ano da Matéria', choices=choice_anos_com_materias) + status_assinatura = django_filters.ChoiceFilter( + required=False, + label=_('Status de Assinatura'), + choices=CHOICE_STATUS_ASSINATURA, + method='filter_status_assinatura' + ) + autoria__autor = django_filters.CharFilter(widget=forms.HiddenInput()) autoria__primeiro_autor = django_filters.BooleanFilter( @@ -1074,6 +1088,17 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet): self.filters['o'].label = _('Ordenação') self.form.fields['tipo_listagem'] = self.tipo_listagem + row_assinatura = to_row([ + (HTML(''' +
+ ✍️ + Assinatura Digital: +
+ '''), 3), + ('status_assinatura', 4), + ]) + row1 = to_row( [('tipo', 5), ('ementa', 7)]) row2 = to_row( @@ -1131,6 +1156,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet): HTML(autor_label), HTML(autor_modal), row4, + row_assinatura, ), Button('btn_pesquisa_avancada', 'Pesquisa Avançada >>>', css_id='btn_pesquisa_avancada_id', @@ -1158,6 +1184,30 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet): ) ) + def filter_status_assinatura(self, queryset, name, value): + if value == 'pendente': + # Tem texto original mas não tem PDF assinado + return queryset.filter( + texto_original__isnull=False + ).exclude( + texto_original='' + ).filter( + pdf_assinado__isnull=True + ) | queryset.filter( + texto_original__isnull=False + ).exclude( + texto_original='' + ).filter( + pdf_assinado='' + ) + elif value == 'assinada': + return queryset.exclude( + pdf_assinado__isnull=True + ).exclude( + pdf_assinado='' + ) + return queryset + @property def qs(self): qs = qs_override_django_filter(self) diff --git a/sapl/materia/urls.py b/sapl/materia/urls.py index 4333063eb..c0939bc67 100644 --- a/sapl/materia/urls.py +++ b/sapl/materia/urls.py @@ -16,6 +16,7 @@ from sapl.materia.views import (AcompanhamentoConfirmarView, LegislacaoCitadaCrud, MateriaAssuntoCrud, MateriaLegislativaCrud, MateriaLegislativaPesquisaView, MateriaTaView, + MateriasPendentesAssinaturaView, NumeracaoCrud, OrgaoCrud, OrigemCrud, PrimeiraTramitacaoEmLoteView, ProposicaoCrud, ProposicaoDevolvida, ProposicaoPendente, @@ -122,6 +123,10 @@ urlpatterns_materia = [ url(r'^materia/pesquisar-materia$', MateriaLegislativaPesquisaView.as_view(), name='pesquisar_materia'), + + url(r'^materia/pendentes-assinatura$', + MateriasPendentesAssinaturaView.as_view(), name='materias_pendentes_assinatura'), + url(r'^materia/(?P\d+)/acompanhar-materia/$', AcompanhamentoMateriaView.as_view(), name='acompanhar_materia'), url(r'^materia/(?P\d+)/acompanhar-confirmar$', diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 9ea94385a..1e888c5c9 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -15,7 +15,7 @@ from crispy_forms.layout import Div, HTML, Submit from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import permission_required -from django.contrib.auth.mixins import PermissionRequiredMixin +from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, ValidationError from django.db.models import Max, Q @@ -38,7 +38,7 @@ from django.utils.decorators import method_decorator import sapl from sapl.base.email_utils import do_envia_email_confirmacao -from sapl.base.models import Autor, CasaLegislativa, AppConfig as BaseAppConfig +from sapl.base.models import Autor, CasaLegislativa, AppConfig as BaseAppConfig, OperadorAutor from sapl.comissoes.models import Participacao from sapl.compilacao.models import STATUS_TA_IMMUTABLE_RESTRICT, STATUS_TA_PRIVATE from sapl.compilacao.views import IntegracaoTaView @@ -641,6 +641,53 @@ class ProposicaoPendenteSetor(PermissionRequiredMixin, ListView): return context +class MateriasPendentesAssinaturaView(LoginRequiredMixin, ListView): + template_name = 'materia/materias_pendentes_assinatura_list.html' + model = MateriaLegislativa + paginate_by = 20 + login_url = '/login/' + + def get_autor(self): + try: + return OperadorAutor.objects.get(user=self.request.user).autor + except OperadorAutor.DoesNotExist: + return None + + def get_queryset(self): + autor = self.get_autor() + qs = MateriaLegislativa.objects.filter( + texto_original__isnull=False + ).exclude( + texto_original='' + ).filter( + Q(pdf_assinado__isnull=True) | Q(pdf_assinado='') + ) + if autor: + qs = qs.filter(autoria__autor=autor) + return qs.order_by('-data_apresentacao', '-id').distinct() + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + autor = self.get_autor() + context['autor'] = autor + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination(page_obj.number, paginator.num_pages) + context['total'] = paginator.count + # URL para pesquisar todas filtrando por autor + pendente + if autor: + context['url_pesquisa_completa'] = ( + reverse('sapl.materia:pesquisar_materia') + + f'?autoria__autor={autor.pk}&status_assinatura=pendente' + ) + else: + context['url_pesquisa_completa'] = ( + reverse('sapl.materia:pesquisar_materia') + + '?status_assinatura=pendente' + ) + return context + + class RevisarProposicaoSetor(PermissionRequiredMixin, UpdateView): template_name = "materia/revisar_proposicao_setor.html" model = Proposicao diff --git a/sapl/static/js/materia_pesquisa_download_pdfs.js b/sapl/static/js/materia_pesquisa_download_pdfs.js index 389c7065f..c213e3e23 100644 --- a/sapl/static/js/materia_pesquisa_download_pdfs.js +++ b/sapl/static/js/materia_pesquisa_download_pdfs.js @@ -208,5 +208,27 @@ downloadAllPDFs(); }); } + + // ── Filtro rápido de assinatura ────────────────────────────────── + const selectAssinatura = document.getElementById('select-status-assinatura'); + if (selectAssinatura) { + selectAssinatura.addEventListener('change', function () { + const status = this.value; + const url = new URL(window.location.href); + const params = url.searchParams; + + // Volta sempre para a página 1 + params.delete('page'); + + if (status === '') { + params.delete('status_assinatura'); + } else { + params.set('status_assinatura', status); + } + + window.location.href = url.pathname + '?' + params.toString(); + }); + } + // ───────────────────────────────────────────────────────────────── }); })(); diff --git a/sapl/templates/materia/materialegislativa_filter.html b/sapl/templates/materia/materialegislativa_filter.html index 885ab5d38..11bf3cb12 100644 --- a/sapl/templates/materia/materialegislativa_filter.html +++ b/sapl/templates/materia/materialegislativa_filter.html @@ -19,6 +19,23 @@ {% endif %} + {# ── Filtro rápido de assinatura ─────────────────────────────────── #} +
+ + {% with status_atual=request.GET.status_assinatura %} + + {% endwith %} +
+ {# ─────────────────────────────────────────────────────────────────── #} +
{% switch "SOLR_SWITCH" %} @@ -37,6 +54,7 @@ {% endblock %} {% block detail_content %} + {% if not show_results %} {% crispy filter.form %} {% endif %} @@ -215,6 +233,19 @@ Texto Original +   + {% if m.pdf_assinado %} + + Assinado{% if m.assinado_em %} em {{ m.assinado_em|date:"d/m/Y" }}{% endif %} + + {% elif m.texto_original %} + + Pendente de Assinatura + + {% endif %}
{% endif %} {% if m.texto_articulado.exists %}Texto Articulado
{% endif %} @@ -256,7 +287,7 @@ {% block extra_js %} -