Browse Source

Como cliente, quero que documentos não assinados tenham filtro na tela de Minhas Matérias [AB#1322]

pull/3858/head
KemuelAlves 4 months ago
parent
commit
e9dd39d664
  1. 50
      sapl/materia/forms.py
  2. 5
      sapl/materia/urls.py
  3. 51
      sapl/materia/views.py
  4. 22
      sapl/static/js/materia_pesquisa_download_pdfs.js
  5. 35
      sapl/templates/materia/materialegislativa_filter.html
  6. 119
      sapl/templates/materia/materias_pendentes_assinatura_list.html
  7. 2
      sapl/templates/navbar.yaml

50
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('''
<div class="alert alert-warning d-flex align-items-center py-2 mb-0"
style="border-left: 4px solid #f0ad4e; background:#fffbf0;">
<span class="mr-2" style="font-size:1.2em;"></span>
<strong class="mr-2">Assinatura Digital:</strong>
</div>
'''), 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)

5
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<pk>\d+)/acompanhar-materia/$',
AcompanhamentoMateriaView.as_view(), name='acompanhar_materia'),
url(r'^materia/(?P<pk>\d+)/acompanhar-confirmar$',

51
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

22
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();
});
}
// ─────────────────────────────────────────────────────────────────
});
})();

35
sapl/templates/materia/materialegislativa_filter.html

@ -19,6 +19,23 @@
</div>
{% endif %}
{# ── Filtro rápido de assinatura ─────────────────────────────────── #}
<div class="d-inline-flex align-items-center ml-2" style="vertical-align:middle;">
<label class="mb-0 mr-1 font-weight-bold text-secondary" style="white-space:nowrap;font-size:.85rem;">
<i class="fas fa-signature mr-1"></i> Assinatura:
</label>
{% with status_atual=request.GET.status_assinatura %}
<select id="select-status-assinatura"
class="custom-select custom-select-sm"
style="min-width:210px;">
<option value="" {% if not status_atual %}selected{% endif %}>Todas as matérias</option>
<option value="pendente" {% if status_atual == 'pendente' %}selected{% endif %}>⚠ Pendente de Assinatura</option>
<option value="assinada" {% if status_atual == 'assinada' %}selected{% endif %}>✔ Assinada</option>
</select>
{% endwith %}
</div>
{# ─────────────────────────────────────────────────────────────────── #}
<div class="actions btn-group float-right pb-4" role="group">
{% switch "SOLR_SWITCH" %}
<a href="{% url 'sapl.base:haystack_search' %}" class="btn btn-outline-primary">
@ -37,6 +54,7 @@
{% endblock %}
{% block detail_content %}
{% if not show_results %}
{% crispy filter.form %}
{% endif %}
@ -215,6 +233,19 @@
Texto Original
</a>
</strong>
&nbsp;
{% if m.pdf_assinado %}
<a href="{% url 'sapl.materia:materia_pdf_assinado' m.pk %}" target="_blank"
class="badge badge-success"
title="PDF assinado disponível — clique para baixar">
<i class="fas fa-signature"></i> Assinado{% if m.assinado_em %} em {{ m.assinado_em|date:"d/m/Y" }}{% endif %}
</a>
{% elif m.texto_original %}
<span class="badge badge-warning text-dark"
title="Documento ainda não possui assinatura digital">
<i class="fas fa-clock"></i> Pendente de Assinatura
</span>
{% endif %}
</br>
{% endif %}
{% if m.texto_articulado.exists %}<strong><a href="{% url 'sapl.materia:materia_ta' m.id%}">Texto Articulado</a></strong></br>{% endif %}
@ -256,7 +287,7 @@
{% block extra_js %}
<script src="{% static 'js/materia_pesquisa_download_pdfs.js' %}"></script>
<script type="text/javascript" >
<script type="text/javascript">
$( document ).ready(function() {
$('.link_votacao_nominal').on('click', function(event) {
event.preventDefault();
@ -265,7 +296,6 @@
function pesquisaAvancada(){
$('.pesquisa_avancada').toggle();
var id_btn = "#btn_pesquisa_avancada_id";
if ($(id_btn).val().endsWith('>>>')){
$(id_btn).val($(id_btn).val().replace('>>>', '<<<'))
@ -276,7 +306,6 @@
function votacaoNominal(id){
$('#div_' + id).toggle();
var id_link = "#link_votacao_nominal_" + id;
if ($(id_link).text().indexOf('>>>') > -1){
$(id_link).text($(id_link).text().replace('>>>', '<<<'))

119
sapl/templates/materia/materias_pendentes_assinatura_list.html

@ -0,0 +1,119 @@
{% extends "crud/detail.html" %}
{% load i18n %}
{% load static %}
{% block actions %}
<div class="actions btn-group float-right pb-4" role="group">
<a href="{{ url_pesquisa_completa }}" class="btn btn-outline-secondary">
<i class="fas fa-search"></i> {% trans "Ver na Pesquisa Completa" %}
</a>
<a href="{% url 'sapl.materia:pesquisar_materia' %}" class="btn btn-outline-primary">
<i class="fas fa-list"></i> {% trans "Pesquisar Matérias" %}
</a>
</div>
{% endblock %}
{% block detail_content %}
<div class="d-flex align-items-center justify-content-between mb-3 flex-wrap" style="gap:.75rem;">
<div>
<h4 class="mb-0">
<span class="badge badge-warning text-dark px-3 py-2" style="font-size:1rem;">
<i class="fas fa-clock mr-1"></i> Matérias Pendentes de Assinatura
</span>
</h4>
{% if autor %}
<small class="text-muted mt-1 d-block">
<i class="fas fa-user mr-1"></i> Autor: <strong>{{ autor }}</strong>
</small>
{% endif %}
</div>
<div>
{% if total == 0 %}
<span class="badge badge-success px-3 py-2" style="font-size:.95rem;">
<i class="fas fa-check-circle mr-1"></i> Nenhuma pendência
</span>
{% elif total == 1 %}
<span class="badge badge-warning text-dark px-3 py-2" style="font-size:.95rem;">
1 matéria pendente
</span>
{% else %}
<span class="badge badge-warning text-dark px-3 py-2" style="font-size:.95rem;">
{{ total }} matérias pendentes
</span>
{% endif %}
</div>
</div>
{% if not object_list %}
<div class="alert alert-success d-flex align-items-center" role="alert">
<i class="fas fa-check-circle fa-2x mr-3"></i>
<div>
<strong>Tudo em dia!</strong><br>
Não há matérias com documento pendente de assinatura digital.
</div>
</div>
{% else %}
<div class="alert alert-warning d-flex align-items-center mb-3" role="alert"
style="border-left: 5px solid #f0ad4e;">
<i class="fas fa-exclamation-triangle fa-lg mr-2"></i>
As matérias abaixo possuem <strong>&nbsp;Texto Original&nbsp;</strong> mas ainda
<strong>&nbsp;não foram assinadas digitalmente</strong>.
</div>
<table class="table table-hover table-bordered">
<thead class="thead-light">
<tr>
<th style="width:160px;">Matéria</th>
<th>Ementa</th>
<th style="width:130px;">Apresentação</th>
<th style="width:130px;">Situação</th>
<th style="width:110px;" class="text-center">Ações</th>
</tr>
</thead>
<tbody>
{% for m in object_list %}
<tr>
<td>
<a href="{% url 'sapl.materia:materialegislativa_detail' m.pk %}"
class="font-weight-bold">
{{ m.tipo.sigla }} {{ m.numero }}/{{ m.ano }}
</a>
<br>
<small class="text-muted">{{ m.tipo }}</small>
</td>
<td>
<span title="{{ m.ementa }}">
{{ m.ementa|truncatechars:120 }}
</span>
</td>
<td class="text-center">
{% if m.data_apresentacao %}
{{ m.data_apresentacao|date:"d/m/Y" }}
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td class="text-center">
{% if m.em_tramitacao %}
<span class="badge badge-info">Em Tramitação</span>
{% else %}
<span class="badge badge-secondary">Arquivada</span>
{% endif %}
</td>
<td class="text-center">
<a href="{% url 'sapl.materia:materialegislativa_detail' m.pk %}"
class="btn btn-sm btn-outline-primary"
title="Abrir matéria para assinar">
<i class="fas fa-signature"></i> Assinar
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% include "paginacao.html" %}
{% endif %}
{% endblock detail_content %}

2
sapl/templates/navbar.yaml

@ -49,6 +49,8 @@
check_permission: materia.can_access_impressos {% comment %} FIXME transformar para checagens de menu_[funcionalidade]{% endcomment%}
- title: {% trans 'Matérias Legislativas' %}
url: sapl.materia:pesquisar_materia
- title: {% trans 'Matérias Pendentes de Assinatura' %}
url: sapl.materia:materias_pendentes_assinatura
- title: {% trans 'Pautas das Sessões' %}
url: sapl.sessao:pauta_sessao
- title: {% trans 'Proposições' %}

Loading…
Cancel
Save