mirror of https://github.com/interlegis/sapl.git
Browse Source
Adiciona fluxo intermediário de revisão pelo Setor Legislativo antes do envio ao Protocolo, protegido por feature flag (desabilitado por padrão). - Novos campos: data_envio_setor, data_retorno_setor, usuario_envio_setor, usuario_retorno_setor, observacao_setor em Proposicao - Flag revisao_setor_legislativo em AppConfig (Sistema > Configurações) - Novo grupo "Operador do Setor Legislativo" com permissões específicas - Ações send_setor e return_from_setor no DetailView - Views ProposicaoPendenteSetor (inbox) e RevisarProposicaoSetor - OnlyOffice: setor pode editar documento quando proposição está com eles - Status S (Enviada ao Setor) e V (Revisada pelo Setor) no histórico - Templates: badges, alertas, botões condicionais e subnavpull/3858/head
17 changed files with 602 additions and 25 deletions
@ -0,0 +1,18 @@ |
|||
# Generated by Django 2.2.28 on 2026-02-23 14:02 |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('base', '0063_merge_20260210_1053'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='appconfig', |
|||
name='revisao_setor_legislativo', |
|||
field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Habilitar etapa de revisão pelo Setor Legislativo?'), |
|||
), |
|||
] |
|||
@ -0,0 +1,50 @@ |
|||
# Generated by Django 2.2.28 on 2026-02-23 14:02 |
|||
|
|||
from django.conf import settings |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), |
|||
('materia', '0091_add_codigo_autenticacao'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterModelOptions( |
|||
name='proposicao', |
|||
options={'ordering': ['-data_recebimento'], 'permissions': (('detail_proposicao_enviada', 'Pode acessar detalhes de uma proposição enviada.'), ('detail_proposicao_devolvida', 'Pode acessar detalhes de uma proposição devolvida.'), ('detail_proposicao_incorporada', 'Pode acessar detalhes de uma proposição incorporada.'), ('detail_proposicao_em_revisao_setor', 'Pode acessar detalhes de uma proposição em revisão pelo setor.')), 'verbose_name': 'Proposição', 'verbose_name_plural': 'Proposições'}, |
|||
), |
|||
migrations.AddField( |
|||
model_name='proposicao', |
|||
name='data_envio_setor', |
|||
field=models.DateTimeField(blank=True, null=True, verbose_name='Data de Envio ao Setor Legislativo'), |
|||
), |
|||
migrations.AddField( |
|||
model_name='proposicao', |
|||
name='data_retorno_setor', |
|||
field=models.DateTimeField(blank=True, null=True, verbose_name='Data de Retorno do Setor Legislativo'), |
|||
), |
|||
migrations.AddField( |
|||
model_name='proposicao', |
|||
name='observacao_setor', |
|||
field=models.TextField(blank=True, default='', verbose_name='Observação do Setor Legislativo'), |
|||
), |
|||
migrations.AddField( |
|||
model_name='proposicao', |
|||
name='usuario_envio_setor', |
|||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='proposicoes_enviadas_setor', to=settings.AUTH_USER_MODEL, verbose_name='Usuário Responsável pelo Envio ao Setor'), |
|||
), |
|||
migrations.AddField( |
|||
model_name='proposicao', |
|||
name='usuario_retorno_setor', |
|||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='proposicoes_retornadas_setor', to=settings.AUTH_USER_MODEL, verbose_name='Usuário Responsável pelo Retorno do Setor'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='historicoproposicao', |
|||
name='status', |
|||
field=models.CharField(choices=[('E', 'Enviada'), ('R', 'Recebida'), ('T', 'Retornada'), ('D', 'Devolvida'), ('S', 'Enviada ao Setor Legislativo'), ('V', 'Revisada pelo Setor Legislativo')], db_index=True, max_length=1, verbose_name='Status de Proposição'), |
|||
), |
|||
] |
|||
@ -0,0 +1,17 @@ |
|||
from sapl.compilacao import models as compilacao |
|||
from sapl.materia import models as materia |
|||
from sapl.rules import SGVP_GROUP_SETOR_LEGISLATIVO, RP_LIST, RP_DETAIL, RP_CHANGE, \ |
|||
__listdetailchange__, __perms_publicas__ |
|||
|
|||
rules_group_setor_legislativo = { |
|||
'group': SGVP_GROUP_SETOR_LEGISLATIVO, |
|||
'rules': [ |
|||
(materia.Proposicao, __listdetailchange__ + |
|||
['detail_proposicao_enviada', |
|||
'detail_proposicao_devolvida', |
|||
'detail_proposicao_incorporada', |
|||
'detail_proposicao_em_revisao_setor'], set()), |
|||
(materia.HistoricoProposicao, [RP_LIST, RP_DETAIL], set()), |
|||
(compilacao.TextoArticulado, ['view_restricted_textoarticulado'], __perms_publicas__), |
|||
] |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
{% extends "materia/proposicao_base.html" %} |
|||
{% load i18n %} |
|||
{% load tz %} |
|||
{% load common_tags %} |
|||
{% block base_content %} |
|||
<div class="proposicao-content"> |
|||
<div class="proposicao-header"> |
|||
<h2>{% trans "Revisão do Setor Legislativo" %}</h2> |
|||
<p class="text-muted">{% trans "Proposições enviadas ao setor aguardando revisão" %}</p> |
|||
</div> |
|||
{% if not object_list %} |
|||
<p>{{ NO_ENTRIES_MSG }}</p> |
|||
{% else %} |
|||
<table class="table table-striped table-hover"> |
|||
<thead> |
|||
<tr> |
|||
{% with 'Data Envio ao Setor,Tipo,Descrição,Autor' as list %} |
|||
{% for name in list|split:"," %} |
|||
<th> |
|||
<a title="{% trans 'Clique para alterar a ordem a listagem'%}" href="?o={% if 'o' not in request.GET and forloop.counter == 1 or 'o' in request.GET and forloop.counter|safe == request.GET.o %}-{%endif%}{{forloop.counter}}"> |
|||
{{ name }} |
|||
{% if 'o' in request.GET %} |
|||
{% if 'o' not in request.GET and forloop.counter == 1 or 'o' in request.GET and forloop.counter|safe == request.GET.o %} |
|||
<span class="caret top" title="{% trans 'Listado na Ordem Ascendente'%}"></span> |
|||
{% elif 'o' in request.GET and forloop.counter == request.GET.o|str2intabs %} |
|||
<span class="caret" title="{% trans 'Listado na Ordem Descendente'%}"></span> |
|||
{%endif%} |
|||
{%endif%} |
|||
</a> |
|||
</th> |
|||
{% endfor %} |
|||
<th>{% trans "Ações" %}</th> |
|||
{% endwith %} |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{% if 'o' in request.GET %} |
|||
{% define object_list|sort_by_keys:request.GET.o as list %} |
|||
{% else %} |
|||
{% define object_list as list %} |
|||
{% endif %} |
|||
|
|||
{% if 'page' in request.GET %} |
|||
{% define request.GET.page as pagina %} |
|||
{% else %} |
|||
{% define '1' as pagina %} |
|||
{% endif %} |
|||
|
|||
{% for prop in list %} |
|||
|
|||
{% if forloop.counter > pagina|paginacao_limite_inferior and forloop.counter <= pagina|paginacao_limite_superior %} |
|||
<tr> |
|||
<td> |
|||
<a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}">{{ prop.data_envio_setor|localtime|date:"d/m/Y H:i:s" }}</a> |
|||
</td> |
|||
<td>{{ prop.tipo.descricao }}</td> |
|||
<td>{{ prop.descricao }}</td> |
|||
<td>{{ prop.autor }}</td> |
|||
<td> |
|||
<a href="{% url 'sapl.materia:revisao-proposicao-setor' prop.pk %}" class="btn btn-sm btn-primary" title="{% trans 'Revisar Proposição' %}"> |
|||
<i class="fas fa-pencil-alt"></i> {% trans "Revisar" %} |
|||
</a> |
|||
<a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}" class="btn btn-sm btn-outline-secondary" title="{% trans 'Ver Detalhes' %}"> |
|||
<i class="fas fa-eye"></i> |
|||
</a> |
|||
</td> |
|||
</tr> |
|||
{% endif %} |
|||
{% endfor %} |
|||
</tbody> |
|||
</table> |
|||
{% endif %} |
|||
</div> |
|||
|
|||
{% include 'paginacao.html'%} |
|||
{% endblock %} |
|||
@ -0,0 +1,80 @@ |
|||
{% extends "materia/proposicao_base.html" %} |
|||
{% load i18n %} |
|||
{% load tz %} |
|||
{% load crispy_forms_tags %} |
|||
|
|||
{% block base_content %} |
|||
<div class="proposicao-content"> |
|||
<div class="proposicao-header"> |
|||
<h2>{% trans "Revisar Proposição" %}</h2> |
|||
<p class="text-muted">{{ proposicao.tipo }} — {{ proposicao.descricao|truncatechars:80 }}</p> |
|||
</div> |
|||
|
|||
<!-- Informações da proposição --> |
|||
<div class="card mb-3"> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-md-3"> |
|||
<strong>{% trans "Tipo" %}</strong><br> |
|||
{{ proposicao.tipo }} |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<strong>{% trans "Autor" %}</strong><br> |
|||
{{ proposicao.autor }} |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<strong>{% trans "Envio ao Setor" %}</strong><br> |
|||
{{ proposicao.data_envio_setor|date:"d/m/Y H:i" }} |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<strong>{% trans "Documento" %}</strong><br> |
|||
{% if proposicao.texto_original %} |
|||
<i class="fa fa-check-circle" style="color: #28a745;"></i> {% trans "Criado" %} |
|||
{% else %} |
|||
<i class="fa fa-times-circle" style="color: #dc3545;"></i> {% trans "Não criado" %} |
|||
{% endif %} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- Ementa --> |
|||
<div class="card mb-3"> |
|||
<div class="card-header"> |
|||
<strong>{% trans "Ementa" %}</strong> |
|||
</div> |
|||
<div class="card-body"> |
|||
{{ proposicao.descricao }} |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- Ações do documento --> |
|||
<div class="card mb-3"> |
|||
<div class="card-header"> |
|||
<strong>{% trans "Documento" %}</strong> |
|||
</div> |
|||
<div class="card-body text-center"> |
|||
{% if proposicao.texto_original %} |
|||
<a href="{% url 'sapl.materia:onlyoffice_editor' proposicao.pk %}" class="btn btn-primary mr-2"> |
|||
<i class="fa fa-edit"></i> {% trans "Editar com OnlyOffice" %} |
|||
</a> |
|||
<a href="{% url 'sapl.materia:proposicao_texto' proposicao.pk %}" class="btn btn-outline-secondary" target="_blank"> |
|||
<i class="fa fa-download"></i> {% trans "Baixar Documento" %} |
|||
</a> |
|||
{% else %} |
|||
<p class="text-muted">{% trans "Nenhum documento anexado." %}</p> |
|||
{% endif %} |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- Formulário de revisão --> |
|||
<div class="card mb-3"> |
|||
<div class="card-body"> |
|||
<form method="post" enctype="multipart/form-data"> |
|||
{% csrf_token %} |
|||
{% crispy form %} |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endblock %} |
|||
Loading…
Reference in new issue