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 %}
+
+ {# ─────────────────────────────────────────────────────────────────── #}
+