Browse Source

Impl parametrização da identificação de docs adms (#3588)

* ajusta front do form de configurações da aplicação

* cria parametrização para identificação de doc adms
pull/3587/head
LeandroJataí 2 years ago
committed by GitHub
parent
commit
4d25239d86
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      sapl/base/forms.py
  2. 23
      sapl/base/migrations/0051_auto_20220814_2138.py
  3. 185
      sapl/base/models.py
  4. 45
      sapl/base/views.py
  5. 4
      sapl/norma/models.py
  6. 3
      sapl/protocoloadm/forms.py
  7. 76
      sapl/protocoloadm/models.py
  8. 2
      sapl/protocoloadm/views.py
  9. 6
      sapl/templates/base/AppConfig.html
  10. 38
      sapl/templates/base/appconfig_form.html
  11. 34
      sapl/templates/base/layouts.yaml
  12. 1
      sapl/templates/materia/layouts.yaml
  13. 20
      sapl/templates/materia/materialegislativa_detail.html
  14. 2
      sapl/templates/protocoloadm/documentoadministrativo_filter.html
  15. 2
      sapl/templates/protocoloadm/layouts.yaml
  16. 3
      sapl/utils.py

3
sapl/base/forms.py

@ -1583,7 +1583,8 @@ class ConfiguracoesAppForm(ModelForm):
'tramitacao_documento',
'google_recaptcha_site_key',
'google_recaptcha_secret_key',
'sapl_as_sapn']
'sapl_as_sapn',
'identificacao_de_documentos']
def __init__(self, *args, **kwargs):
super(ConfiguracoesAppForm, self).__init__(*args, **kwargs)

23
sapl/base/migrations/0051_auto_20220814_2138.py

@ -0,0 +1,23 @@
# Generated by Django 2.2.28 on 2022-08-15 00:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0050_metadata'),
]
operations = [
migrations.AddField(
model_name='appconfig',
name='identificacao_de_documentos',
field=models.CharField(default='{sigla}{numero}/{ano}{-}{complemento} - {nome}', help_text='\n Como mostrar a identificação dos documentos administrativos?\n Você pode usar um conjunto de combinações que pretender.\n Ao fazer sua edição, será mostrado logo abaixo o último documento cadastrado, como exemplo de resultado de sua edição.\n Em caso de erro, nenhum documento será mostrado e aparecerá apenas o formato padrão mínimo, que é este: "{sigla}{numero}/{ano}{-}{complemento} - {nome}".\n Muito importante, use as chaves "{}", sem elas, você estará inserindo um texto qualquer e não o valor de um campo.\n Você pode combinar as seguintes campos: {sigla} {nome} {numero} {ano} {complemento} {assunto}\n Ainda pode ser usado {/}, {-}, {.} se você quiser que uma barra, traço, ou ponto\n seja adicionado apenas se o próximo campo que será usado tenha algum conteúdo\n (não use dois destes destes condicionais em sequência, somente o último será considerado).\n ', max_length=254, verbose_name='Formato da identificação dos documentos'),
),
migrations.AlterField(
model_name='appconfig',
name='protocolo_manual',
field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Permitir informe manual de data e hora de protocolo?'),
),
]

185
sapl/base/models.py

@ -1,6 +1,7 @@
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields.jsonb import JSONField
from django.core.cache import cache
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.db.models.deletion import CASCADE
@ -93,63 +94,124 @@ class AppConfig(models.Model):
('N', _('Nunca Protocolar ao incorporar uma proposição')),
)
documentos_administrativos = models.CharField(
max_length=1,
verbose_name=_('Visibilidade dos Documentos Administrativos'),
choices=TIPO_DOCUMENTO_ADMINISTRATIVO, default='O')
# MANTENHA A SEQUÊNCIA EQUIVALENTE COM /sapl/templates/base/layout.yaml
# AppConfig:
estatisticas_acesso_normas = models.CharField(
# CONFIGURAÇÕES GERAIS
# Linha 1 ------------
esfera_federacao = models.CharField(
max_length=1,
verbose_name=_('Estatísticas de acesso a normas'),
choices=RELATORIO_ATOS_ACESSADOS, default='N')
blank=True,
default="",
verbose_name=_('Esfera Federação'),
choices=ESFERA_FEDERACAO_CHOICES)
sapl_as_sapn = models.BooleanField(
verbose_name=_(
'Utilizar SAPL como SAPN?'),
choices=YES_NO_CHOICES, default=False)
sequencia_numeracao_proposicao = models.CharField(
max_length=1,
verbose_name=_('Sequência de numeração de proposições'),
choices=SEQUENCIA_NUMERACAO_PROPOSICAO, default='A')
# MÓDULO PARLAMENTARES
# MÓDULO MESA DIRETORA
# MÓDULO COMISSÕES
# MÓDULO BANCADAS PARLAMENTARES
# MÓDULO DOCUMENTOS ADMINISTRATIVOS
# Linha 1 -------------------------
documentos_administrativos = models.CharField(
max_length=1,
verbose_name=_('Visibilidade dos Documentos Administrativos'),
choices=TIPO_DOCUMENTO_ADMINISTRATIVO, default='O')
tramitacao_documento = models.BooleanField(
verbose_name=_(
'Tramitar documentos anexados junto com os documentos principais?'),
choices=YES_NO_CHOICES, default=True)
# Linha 2 -------------------------
protocolo_manual = models.BooleanField(
verbose_name=_('Permitir informe manual de data e hora de protocolo?'),
choices=YES_NO_CHOICES, default=False)
sequencia_numeracao_protocolo = models.CharField(
max_length=1,
verbose_name=_('Sequência de numeração de protocolos'),
choices=SEQUENCIA_NUMERACAO_PROTOCOLO, default='A')
inicio_numeracao_protocolo = models.PositiveIntegerField(
verbose_name=_('Início da numeração de protocolo'),
default=1
)
# Linha 3 -------------------------
identificacao_de_documentos = models.CharField(
max_length=254,
verbose_name=_('Formato da identificação dos documentos'),
default='{sigla}{numero}/{ano}{-}{complemento} - {nome}',
help_text="""
Como mostrar a identificação dos documentos administrativos?
Você pode usar um conjunto de combinações que pretender.
Ao fazer sua edição, será mostrado logo abaixo o último documento cadastrado, como exemplo de resultado de sua edição.
Em caso de erro, nenhum documento será mostrado e aparecerá apenas o formato padrão mínimo, que é este: "{sigla}{numero}/{ano}{-}{complemento} - {nome}".
Muito importante, use as chaves "{}", sem elas, você estará inserindo um texto qualquer e não o valor de um campo.
Você pode combinar as seguintes campos: {sigla} {nome} {numero} {ano} {complemento} {assunto}
Ainda pode ser usado {/}, {-}, {.} se você quiser que uma barra, traço, ou ponto
seja adicionado apenas se o próximo campo que será usado tenha algum conteúdo
(não use dois destes destes condicionais em sequência, somente o último será considerado).
"""
)
esfera_federacao = models.CharField(
# MÓDULO PROPOSIÇÕES
# Linha 1 ----------
sequencia_numeracao_proposicao = models.CharField(
max_length=1,
blank=True,
default="",
verbose_name=_('Esfera Federação'),
choices=ESFERA_FEDERACAO_CHOICES)
verbose_name=_('Sequência de numeração de proposições'),
choices=SEQUENCIA_NUMERACAO_PROPOSICAO, default='A')
receber_recibo_proposicao = models.BooleanField(
verbose_name=_('Protocolar proposição somente com recibo?'),
choices=YES_NO_CHOICES, default=True)
proposicao_incorporacao_obrigatoria = models.CharField(
verbose_name=_('Regra de incorporação de proposições e protocolo'),
max_length=1, choices=POLITICA_PROTOCOLO_CHOICES, default='O')
escolher_numero_materia_proposicao = models.BooleanField(
verbose_name=_(
'Indicar número da matéria a ser gerada na proposição?'),
choices=YES_NO_CHOICES, default=False)
# TODO: a ser implementado na versão 3.2
# painel_aberto = models.BooleanField(
# verbose_name=_('Painel aberto para usuário anônimo'),
# choices=YES_NO_CHOICES, default=False)
# MÓDULO MATÉRIA LEGISLATIVA
# Linha 1 ------------------
tramitacao_origem_fixa = models.BooleanField(
verbose_name=_(
'Fixar origem de novas tramitações como sendo a tramitação de destino da última tramitação?'),
choices=YES_NO_CHOICES,
default=True,
help_text=_('Ao utilizar a opção NÂO, você compreende que os controles '
'de origem e destino das tramitações são anulados, '
'podendo seu operador registrar quaisquer origem e '
'destino para as tramitações. Se você colocar Não, '
'fizer tramitações aleatórias e voltar para SIM, '
'o destino da tramitação mais recente será utilizado '
'para a origem de uma nova inserção!'))
tramitacao_materia = models.BooleanField(
verbose_name=_(
'Tramitar matérias anexadas junto com as matérias principais?'),
choices=YES_NO_CHOICES, default=True)
# MÓDULO NORMAS JURÍDICAS
# MÓDULO TEXTOS ARTICULADOS
# Linha 1 -----------------
texto_articulado_proposicao = models.BooleanField(
verbose_name=_('Usar Textos Articulados para Proposições'),
choices=YES_NO_CHOICES, default=False)
texto_articulado_materia = models.BooleanField(
verbose_name=_('Usar Textos Articulados para Matérias'),
choices=YES_NO_CHOICES, default=False)
texto_articulado_norma = models.BooleanField(
verbose_name=_('Usar Textos Articulados para Normas'),
choices=YES_NO_CHOICES, default=True)
proposicao_incorporacao_obrigatoria = models.CharField(
verbose_name=_('Regra de incorporação de proposições e protocolo'),
max_length=1, choices=POLITICA_PROTOCOLO_CHOICES, default='O')
# MÓDULO SESSÃO PLENÁRIA
assinatura_ata = models.CharField(
verbose_name=_('Quem deve assinar a ata'),
max_length=1, choices=ASSINATURA_ATA_CHOICES, default='T')
# MÓDULO PAINEL
cronometro_discurso = models.DurationField(
verbose_name=_('Cronômetro do Discurso'),
blank=True,
@ -174,41 +236,20 @@ class AppConfig(models.Model):
default=False,
verbose_name=_('Mostrar brasão da Casa no painel?'))
receber_recibo_proposicao = models.BooleanField(
verbose_name=_('Protocolar proposição somente com recibo?'),
choices=YES_NO_CHOICES, default=True)
protocolo_manual = models.BooleanField(
verbose_name=_('Informar data e hora de protocolo?'),
choices=YES_NO_CHOICES, default=False)
escolher_numero_materia_proposicao = models.BooleanField(
verbose_name=_(
'Indicar número da matéria a ser gerada na proposição?'),
choices=YES_NO_CHOICES, default=False)
# MÓDULO ESTATÍSTICAS DE ACESSO
estatisticas_acesso_normas = models.CharField(
max_length=1,
verbose_name=_('Estatísticas de acesso a normas'),
choices=RELATORIO_ATOS_ACESSADOS, default='N')
tramitacao_origem_fixa = models.BooleanField(
verbose_name=_(
'Fixar origem de novas tramitações como sendo a tramitação de destino da última tramitação?'),
choices=YES_NO_CHOICES,
default=True,
help_text=_('Ao utilizar a opção NÂO, você compreende que os controles '
'de origem e destino das tramitações são anulados, '
'podendo seu operador registrar quaisquer origem e '
'destino para as tramitações. Se você colocar Não, '
'fizer tramitações aleatórias e voltar para SIM, '
'o destino da tramitação mais recente será utilizado '
'para a origem de uma nova inserção!'))
# MÓDULO SEGURANÇA
tramitacao_materia = models.BooleanField(
verbose_name=_(
'Tramitar matérias anexadas junto com as matérias principais?'),
choices=YES_NO_CHOICES, default=True)
# MÓDULO LEXML
tramitacao_documento = models.BooleanField(
verbose_name=_(
'Tramitar documentos anexados junto com os documentos principais?'),
choices=YES_NO_CHOICES, default=True)
# TODO: a ser implementado na versão 3.2
# painel_aberto = models.BooleanField(
# verbose_name=_('Painel aberto para usuário anônimo'),
# choices=YES_NO_CHOICES, default=False)
google_recaptcha_site_key = models.CharField(
verbose_name=_('Chave pública gerada pelo Google Recaptcha'),
@ -217,11 +258,6 @@ class AppConfig(models.Model):
verbose_name=_('Chave privada gerada pelo Google Recaptcha'),
max_length=256, default='')
sapl_as_sapn = models.BooleanField(
verbose_name=_(
'Utilizar SAPL como SAPN?'),
choices=YES_NO_CHOICES, default=False)
class Meta:
verbose_name = _('Configurações da Aplicação')
verbose_name_plural = _('Configurações da Aplicação')
@ -231,15 +267,32 @@ class AppConfig(models.Model):
)
ordering = ('-id',)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
fields = self._meta.get_fields()
for f in fields:
if f.name != 'id' and not cache.get(f'sapl_{f.name}') is None:
cache.set(f'sapl_{f.name}', getattr(self, f.name), 600)
return models.Model.save(self, force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
@classmethod
def attr(cls, attr):
value = cache.get(f'sapl_{attr}')
if not value is None:
return value
print(f'entrou aqui para {attr}')
config = AppConfig.objects.first()
if not config:
config = AppConfig()
config.save()
return getattr(config, attr)
value = getattr(config, attr)
cache.set(f'sapl_{attr}', value, 600)
return value
def __str__(self):
return _('Configurações da Aplicação - %(id)s') % {

45
sapl/base/views.py

@ -4,7 +4,9 @@ import datetime
import itertools
import logging
import os
import re
from django.apps.registry import apps
from django.contrib import messages
from django.contrib.auth import get_user_model, views
from django.contrib.auth.mixins import PermissionRequiredMixin
@ -32,8 +34,8 @@ from django.views.generic.base import RedirectView, TemplateView
from django_filters.views import FilterView
from haystack.query import SearchQuerySet
from haystack.views import SearchView
from ratelimit.decorators import ratelimit
from sapl import settings
from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica
from sapl.base.forms import (AutorForm, TipoAutorForm, AutorFilterSet, RecuperarSenhaForm,
@ -1893,6 +1895,7 @@ class UserCrud(Crud):
list_field_names = [
'usuario', 'groups', 'is_active'
]
def openapi_url(self):
return ''
@ -1927,7 +1930,6 @@ class UserCrud(Crud):
class DetailView(Crud.DetailView):
layout_key = 'UserDetail'
def hook_usuario(self, obj):
return 'Usuário', '{}<br><small>{}</small>'.format(
obj.get_full_name() or '...',
@ -2100,10 +2102,45 @@ class AppConfigCrud(CrudAux):
kwargs={'pk': app_config.pk}))
class UpdateView(CrudAux.UpdateView):
template_name = 'base/AppConfig.html'
form_class = ConfiguracoesAppForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = self.model._meta.verbose_name
return context
def get(self, request, *args, **kwargs):
if 'jsidd' in request.GET:
return self.json_simular_identificacao_de_documentos(request, *args, **kwargs)
return super().get(request, *args, **kwargs)
def json_simular_identificacao_de_documentos(self, request, *args, **kwargs):
DocumentoAdministrativo = apps.get_model(
'protocoloadm',
'DocumentoAdministrativo'
)
d = DocumentoAdministrativo.objects.order_by('-id').first()
jsidd = request.GET.get('jsidd', '')
values = {
'{sigla}': d.tipo.sigla if d else 'OF',
'{nome}': d.tipo.descricao if d else 'Ofício',
'{numero}': f'{d.numero:0>3}' if d else '001',
'{ano}': f'{d.ano}' if d else str(timezone.now().year),
'{complemento}': d.complemento if d else 'GAB',
'{assunto}': d.assunto if d else 'Simulação de Identificação de Documentos'
}
result = DocumentoAdministrativo.mask_to_str(values, jsidd)
return JsonResponse({
'jsidd': result[0],
'error': list(result[1])
})
def form_valid(self, form):
numeracao = AppConfig.objects.last().sequencia_numeracao_protocolo
numeracao_antiga = AppConfig.objects.last().inicio_numeracao_protocolo

4
sapl/norma/models.py

@ -151,7 +151,9 @@ class NormaJuridica(models.Model):
verbose_name=_('Tipo da Norma Jurídica'))
materia = models.ForeignKey(
MateriaLegislativa, blank=True, null=True,
on_delete=models.PROTECT, verbose_name=_('Matéria'))
on_delete=models.PROTECT,
verbose_name=_('Matéria'),
related_name='normajuridica_set')
orgao = models.ForeignKey(
Orgao, blank=True, null=True,
on_delete=models.PROTECT, verbose_name=_('Órgão'))

3
sapl/protocoloadm/forms.py

@ -1139,7 +1139,8 @@ class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm):
tipo = TipoDocumentoAdministrativo.objects.get(
id=tipo_documento)
raise ValidationError(
_('{}/{} ({}) já existente!'.format(numero_documento,
_('{}/{} ({}) já existente! '
'Você diferenciar preenchendo o campo complemento'.format(numero_documento,
ano_documento,
tipo)))

76
sapl/protocoloadm/models.py

@ -1,10 +1,13 @@
import re
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
import reversion
from sapl.base.models import Autor
from sapl.base.models import Autor, AppConfig as SaplAppConfig
from sapl.materia.models import TipoMateriaLegislativa, UnidadeTramitacao,\
MateriaLegislativa
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, texto_upload_path,
@ -250,19 +253,67 @@ class DocumentoAdministrativo(models.Model):
verbose_name_plural = _('Documentos Administrativos')
ordering = ('id',)
def __str__(self):
return _('%(tipo)s - %(assunto)s') % {
'tipo': self.tipo, 'assunto': self.assunto
@classmethod
def mask_to_str(cls, values, mask):
erro = set()
pattern = '({[^{}]+}|{[ /.-]*})'
campos_escolhidos = re.findall(pattern, mask)
campos_permitidos = {
'{.}', '{/}', '{-}',
'{sigla}',
'{nome}',
'{numero}',
'{ano}',
'{complemento}',
'{assunto}',
}
condicionais = {
'{.}': '.',
'{/}': '/',
'{-}': '-',
}
@property
def epigrafe(self):
return _('%(tipo)s - %(numero)s/%(ano)s') % {
'tipo': self.tipo,
'numero': self.numero,
'ano': self.ano
erro = set(campos_escolhidos) - campos_permitidos
if erro:
mask = '{sigla}{numero}/{ano}{-}{complemento} - {nome}'
campos_escolhidos = re.findall(pattern, mask)
for i, k in enumerate(campos_escolhidos):
if k in values.keys():
if i > 0 and campos_escolhidos[i - 1] in condicionais:
mask = mask.replace(
campos_escolhidos[i - 1],
condicionais[campos_escolhidos[i - 1]]if values[k] else '', 1)
mask = mask.replace(k, values[k], 1)
elif k in condicionais:
if i > 0 and campos_escolhidos[i - 1] in condicionais:
mask = mask.replace(
campos_escolhidos[i - 1],
'', 1)
if i + 1 == len(campos_escolhidos):
mask = mask.replace(k, '', 1)
return mask, erro
@cached_property
def _identificacao_de_documento(self):
mask = SaplAppConfig.attr('identificacao_de_documentos')
values = {
'{sigla}': self.tipo.sigla,
'{nome}': self.tipo.descricao,
'{numero}': f'{self.numero:0>3}',
'{ano}': f'{self.ano}',
'{complemento}': self.complemento,
'{assunto}': self.assunto
}
return DocumentoAdministrativo.mask_to_str(values, mask)[0]
def __str__(self):
return self._identificacao_de_documento
def delete(self, using=None, keep_parents=False):
texto_integral = self.texto_integral
result = super().delete(using=using, keep_parents=keep_parents)
@ -487,11 +538,6 @@ class VinculoDocAdminMateria(models.Model):
def __str__(self):
return f'Vinculo: {self.documento} - {self.materia}'
return _('Vinculo %(documento)s // %(materia)s') % {
'documento': self.documento.epigrafe,
'materia': self.materia
}
@reversion.register()
class AcompanhamentoDocumento(models.Model):

2
sapl/protocoloadm/views.py

@ -1783,7 +1783,7 @@ class VinculoDocAdminMateriaCrud(MasterDetailCrud):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = self.object.documento.epigrafe
context['title'] = self.object.documento
return context
def get_initial(self):

6
sapl/templates/base/AppConfig.html

@ -1,6 +0,0 @@
{% extends "base.html" %}
{% load i18n crispy_forms_tags menus%}
{% block base_content %}
{% crispy form %}
{% endblock base_content %}

38
sapl/templates/base/appconfig_form.html

@ -0,0 +1,38 @@
{% extends "base.html" %}
{% load i18n crispy_forms_tags menus%}
{% block base_content %}
{% crispy form %}
{% endblock base_content %}
{% block extra_js %}
<script language="Javascript">
function json_simular_identificacao_de_documento() {
var jsidd = $("#id_identificacao_de_documentos").val()
$.get("",
{ jsidd: jsidd},
function(data, status) {
$("#jsidd").remove()
$("#div_id_identificacao_de_documentos").closest('.row').after($('<div class="row"/>').append($('<div class="col-12"/>').append(
$(`<div id="jsidd" class="alert alert-${data.error.length > 0 ? "danger":"info"}"/>`).html(
`
Resultado: <strong>${data.jsidd}</strong>
${data.error.length > 0 ? '<br><br>Erro(s):': ''}
${data.error.length > 0 ? data.error: ''}
`
)
)))
});
}
var fields = ["#id_identificacao_de_documentos",];
for (i = 0; i < fields.length; i++){
$(fields[i]).keyup(function() {
json_simular_identificacao_de_documento();
});
}
$(document).ready( function() {
json_simular_identificacao_de_documento();
});
</script>
{% endblock %}

34
sapl/templates/base/layouts.yaml

@ -20,40 +20,29 @@ UserDetail:
AppConfig:
{% trans 'Configurações Gerais' %}:
- esfera_federacao documentos_administrativos sapl_as_sapn
- esfera_federacao sapl_as_sapn
#{% trans 'Módulo Parlamentares' %}:
#{% trans 'Módulo Mesa Diretora' %}:
#{% trans 'Módulo Comissões' %}:
#{% trans 'Módulo Bancadas Parlamentares' %}:
# {% trans 'Módulo Normas Jurídicas' %}:
{% trans 'Módulo Administrativo' %}:
- documentos_administrativos tramitacao_documento
- protocolo_manual sequencia_numeracao_protocolo inicio_numeracao_protocolo
- identificacao_de_documentos
{% trans 'Módulo Proposições' %}:
- sequencia_numeracao_proposicao sequencia_numeracao_protocolo inicio_numeracao_protocolo
- protocolo_manual receber_recibo_proposicao
- proposicao_incorporacao_obrigatoria escolher_numero_materia_proposicao
- sequencia_numeracao_proposicao receber_recibo_proposicao proposicao_incorporacao_obrigatoria escolher_numero_materia_proposicao
{% trans 'Módulo Matéria Legislativa' %}:
- tramitacao_origem_fixa tramitacao_materia tramitacao_documento
- tramitacao_origem_fixa:7 tramitacao_materia
# {% trans 'Módulo Normas Jurídicas' %}:
{% trans 'Módulo Textos Articulados' %}:
- texto_articulado_proposicao texto_articulado_materia texto_articulado_norma
#{% trans 'Módulo Sessão Plenária' %}:
#{% trans 'Módulo LexML' %}:
#{% trans 'Módulo Administrativo' %}:
{% trans 'Estatísticas de acesso' %}:
- estatisticas_acesso_normas
{% trans 'Assinaturas' %}:
{% trans 'Módulo Sessão Plenária' %}:
- assinatura_ata
{% trans 'Módulo Painel' %}:
@ -61,9 +50,14 @@ AppConfig:
- cronometro_ordem cronometro_consideracoes
- mostrar_brasao_painel
{% trans 'Estatísticas de acesso' %}:
- estatisticas_acesso_normas
{% trans 'Segurança' %}:
- google_recaptcha_site_key google_recaptcha_secret_key
#{% trans 'Módulo LexML' %}:
TipoAutor:
{% trans 'Tipo Autor' %}:
- descricao

1
sapl/templates/materia/layouts.yaml

@ -29,6 +29,7 @@ MateriaLegislativa:
- data_apresentacao numero_protocolo tipo_apresentacao
- tipo_autor autor
- texto_original
{% trans 'Outras Informações' %}:
- apelido dias_prazo polemica
- objeto regime_tramitacao em_tramitacao

20
sapl/templates/materia/materialegislativa_detail.html

@ -40,12 +40,22 @@
</br>
{% endfor %}
{% endif %}
{% if object.normajuridica_set.last %}
<p class="control-label">&emsp; Norma Jurídica Relacionada</p>
<div class="actions btn-group btn-group-sm" role="group">
&emsp;&emsp;<a href="{% url 'sapl.norma:normajuridica_detail' object.normajuridica_set.last.id %}">
<br>
<div class="row">
<div class="col-12">
<div id="div_id_docadmvinculados" class="form-group">
<p class="control-label">Norma Jurídica Relacionada</p>
<div class="controls">
<div class="form-control-static">
<a href="{% url 'sapl.norma:normajuridica_detail' object.normajuridica_set.last.id %}">
{{ object.normajuridica_set.last }}</a>
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% if object.docadmvinculados.all.exists %}
@ -55,13 +65,13 @@
<div id="div_id_docadmvinculados" class="form-group">
<p class="control-label">Documentos Administrativos Públicos Vinculados a Matéria</p>
<div class="controls">
<div class="form-control-static">
<div class="form-control-static p-2">
{% for vinculodocadmmateria in object.documentoadministrativo_vinculado_set.all %}
{% if not vinculodocadmmateria.documento.restrito or vinculodocadmmateria.documento.restrito and not user.is_anonymous %}
<strong>Data Anexação:</strong> {{vinculodocadmmateria.data_anexacao}} {% if vinculodocadmmateria.data_desanexacao %} - {{vinculodocadmmateria.data_desanexacao}}{% endif %}
<br><strong>Documento:</strong>
<a href="{% url 'sapl.protocoloadm:documentoadministrativo_detail' vinculodocadmmateria.documento.id %}">
{{ vinculodocadmmateria.documento.epigrafe }}
{{ vinculodocadmmateria.documento }}
</a>
<br>{{ vinculodocadmmateria.documento.assunto}}
{% if vinculodocadmmateria.documento.restrito %}

2
sapl/templates/protocoloadm/documentoadministrativo_filter.html

@ -37,7 +37,7 @@
{% if request.user.is_anonymous and not d.restrito or not request.user.is_anonymous%}
<tr>
<td>
<strong><a href="{% url 'sapl.protocoloadm:documentoadministrativo_detail' d.id %}">{{d.tipo.sigla}} {{d.numero}}{%if d.complemento %}-{{d.complemento}}{% endif %}/{{d.ano}} - {{d.tipo}}</strong></a></br>
<a href="{% url 'sapl.protocoloadm:documentoadministrativo_detail' d.id %}"><strong>{{d}}</strong></a></br>
<strong>Interessado:</strong>&nbsp;{{ d.interessado|default_if_none:"Não informado"}}
</br>
<strong>Assunto:</strong>&nbsp;{{ d.assunto|safe }}

2
sapl/templates/protocoloadm/layouts.yaml

@ -9,7 +9,7 @@ DocumentoAdministrativo:
- numero complemento ano
- data protocolo
- assunto
- interessado autor tramitacao
- interessado autor tramitacao:2
- texto_integral|urlize
- documento_anexado_set__documento_principal|m2m_urlize_for_detail
- documento_principal_set__documento_anexado|m2m_urlize_for_detail

3
sapl/utils.py

@ -1,6 +1,6 @@
from functools import wraps
import hashlib
from itertools import groupby
from itertools import groupby, chain
import logging
from operator import itemgetter
import os
@ -26,6 +26,7 @@ from django.core.files.uploadedfile import UploadedFile
from django.core.mail import get_connection
from django.db import models
from django.db.models import Q
from django.db.models.fields.related import ForeignKey
from django.forms import BaseForm
from django.forms.widgets import SplitDateTimeWidget
from django.utils import six, timezone

Loading…
Cancel
Save