Browse Source

Impl Issue #3326 criando vínculo entre matérias e docs adms (#3586)

* cria model VinculoDocAdminMateria

* impl display em detailview de matéria e documentos

* add unique_together a VinculoDocAdminMateria

* impl crud de vinculodocadminmateria

* impl vinculo em lote de docadm com matérias
pull/3588/head
LeandroJataí 2 years ago
committed by GitHub
parent
commit
63ad0dfae5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      sapl/crispy_layout_mixin.py
  2. 11
      sapl/crud/base.py
  3. 104
      sapl/protocoloadm/forms.py
  4. 35
      sapl/protocoloadm/migrations/0041_auto_20220804_2239.py
  5. 23
      sapl/protocoloadm/migrations/0042_auto_20220805_1236.py
  6. 63
      sapl/protocoloadm/models.py
  7. 9
      sapl/protocoloadm/urls.py
  8. 160
      sapl/protocoloadm/views.py
  9. 35
      sapl/templates/materia/materialegislativa_detail.html
  10. 24
      sapl/templates/protocoloadm/documentoadministrativo_detail.html
  11. 74
      sapl/templates/protocoloadm/em_lote/vinculodocadminmateria.html
  12. 10
      sapl/templates/protocoloadm/layouts.yaml
  13. 2
      sapl/templates/protocoloadm/subnav.yaml
  14. 38
      sapl/templates/protocoloadm/vinculodocadminmateria_form.html
  15. 14
      sapl/templates/protocoloadm/vinculodocadminmateria_list.html

2
sapl/crispy_layout_mixin.py

@ -170,7 +170,7 @@ def get_field_display(obj, fieldname):
display = '<div class="dont-break-out">{}</div>'.format(display) display = '<div class="dont-break-out">{}</div>'.format(display)
else: else:
display = str(value) display = str(value)
return verbose_name, display return verbose_name, display or '&nbsp;'
class CrispyLayoutFormMixin: class CrispyLayoutFormMixin:

11
sapl/crud/base.py

@ -1119,12 +1119,15 @@ class MasterDetailCrud(Crud):
root_pk = self.kwargs['pk'] if 'pkk' not in self.request.GET\ root_pk = self.kwargs['pk'] if 'pkk' not in self.request.GET\
else self.request.GET['pkk'] else self.request.GET['pkk']
kwargs.setdefault('root_pk', root_pk) kwargs.setdefault('root_pk', root_pk)
context = super(CrudBaseMixin, self).get_context_data(**kwargs)
if parent_object: title = '%s <small>(%s)</small>' % (
context['title'] = '%s <small>(%s)</small>' % ( self.object,
self.object, parent_object) parent_object
) if parent_object else ''
context = super(CrudBaseMixin, self).get_context_data(**kwargs)
if 'title' not in context and title:
context['title'] = title
return context return context
class ListView(Crud.ListView): class ListView(Crud.ListView):

104
sapl/protocoloadm/forms.py

@ -20,6 +20,7 @@ from sapl.crispy_layout_mixin import (form_actions, SaplFormHelper,
from sapl.materia.models import (MateriaLegislativa, from sapl.materia.models import (MateriaLegislativa,
TipoMateriaLegislativa, TipoMateriaLegislativa,
UnidadeTramitacao) UnidadeTramitacao)
from sapl.protocoloadm.models import VinculoDocAdminMateria
from sapl.utils import (AnoNumeroOrderingFilter, autor_label, autor_modal, from sapl.utils import (AnoNumeroOrderingFilter, autor_label, autor_modal,
choice_anos_com_documentoadministrativo, choice_anos_com_documentoadministrativo,
choice_anos_com_materias, timing, choice_anos_com_materias, timing,
@ -793,7 +794,7 @@ class TramitacaoAdmForm(ModelForm):
ip=tramitacao.ip, ip=tramitacao.ip,
ultima_edicao=tramitacao.ultima_edicao ultima_edicao=tramitacao.ultima_edicao
)) ))
## TODO: BULK UPDATE não envia Signal para Tramitacao # TODO: BULK UPDATE não envia Signal para Tramitacao
TramitacaoAdministrativo.objects.bulk_create(lista_tramitacao) TramitacaoAdministrativo.objects.bulk_create(lista_tramitacao)
return tramitacao return tramitacao
@ -915,7 +916,7 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm):
da.tramitacao = False if nova_tram_principal.status.indicador == "F" else True da.tramitacao = False if nova_tram_principal.status.indicador == "F" else True
da.save() da.save()
## TODO: refatorar? # TODO: refatorar?
return nova_tram_principal return nova_tram_principal
@ -1694,7 +1695,7 @@ class TramitacaoEmLoteAdmForm(ModelForm):
ip=tramitacao.ip, ip=tramitacao.ip,
ultima_edicao=tramitacao.ultima_edicao ultima_edicao=tramitacao.ultima_edicao
)) ))
## TODO: BULK UPDATE não envia Signal para Tramitacao # TODO: BULK UPDATE não envia Signal para Tramitacao
TramitacaoAdministrativo.objects.bulk_create(lista_tramitacao) TramitacaoAdministrativo.objects.bulk_create(lista_tramitacao)
return tramitacao return tramitacao
@ -1732,3 +1733,100 @@ class TramitacaoEmLoteAdmFilterSet(django_filters.FilterSet):
self.form.helper.layout = Layout( self.form.helper.layout = Layout(
Fieldset(_('Tramitação em Lote'), Fieldset(_('Tramitação em Lote'),
row1, row2, form_actions(label=_('Pesquisar')))) row1, row2, form_actions(label=_('Pesquisar'))))
class VinculoDocAdminMateriaForm(ModelForm):
logger = logging.getLogger(__name__)
tipo = forms.ModelChoiceField(
label='Tipo',
required=True,
queryset=TipoMateriaLegislativa.objects.all(),
empty_label='Selecione',
)
numero = forms.IntegerField(label='Número', required=True)
ano = forms.CharField(label='Ano', required=True)
class Meta:
model = VinculoDocAdminMateria
fields = ['tipo', 'numero', 'ano', 'data_anexacao', 'data_desanexacao']
def __init__(self, *args, **kwargs):
return super().__init__(*args, **kwargs)
def clean(self):
super().clean()
if not self.is_valid():
return self.cleaned_data
cleaned_data = self.cleaned_data
data_anexacao = cleaned_data['data_anexacao']
data_desanexacao = cleaned_data['data_desanexacao'] if cleaned_data['data_desanexacao'] else data_anexacao
if data_anexacao > data_desanexacao:
self.logger.error(
"Data de anexação posterior à data de desanexação.")
raise ValidationError(
_("Data de anexação posterior à data de desanexação."))
try:
self.logger.info("Tentando obter objeto MateriaLegislativa (numero={}, ano={}, tipo={})."
.format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo']))
materia = MateriaLegislativa.objects.get(
numero=cleaned_data['numero'],
ano=cleaned_data['ano'],
tipo=cleaned_data['tipo'])
except ObjectDoesNotExist:
msg = _('A {} {}/{} não existe no cadastro de matérias legislativas.'
.format(cleaned_data['tipo'], cleaned_data['numero'], cleaned_data['ano']))
self.logger.warning(
"A matéria a ser anexada não existe no cadastro de matérias legislativas.")
raise ValidationError(msg)
if VinculoDocAdminMateria.objects.filter(
documento=self.instance.documento, materia=materia
).exclude(pk=self.instance.pk).exists():
self.logger.error(
"Matéria já se encontra vinculada a este documento.")
raise ValidationError(
_('Matéria já se encontra vinculada a este documento'))
cleaned_data['materia'] = materia
return cleaned_data
def save(self, commit=False):
vinculo = super().save(commit)
vinculo.materia = self.cleaned_data['materia']
vinculo.save()
return vinculo
class VinculoDocAdminMateriaEmLoteFilterSet(django_filters.FilterSet):
class Meta(FilterOverridesMetaMixin):
model = MateriaLegislativa
fields = ['tipo', 'data_apresentacao']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.filters['tipo'].label = 'Tipo de Matéria'
self.filters['data_apresentacao'].label = 'Data (Inicial - Final)'
self.form.fields['tipo'].required = True
self.form.fields['data_apresentacao'].required = True
row1 = to_row([('tipo', 12)])
row2 = to_row([('data_apresentacao', 12)])
self.form.helper = SaplFormHelper()
self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout(
Fieldset(_('Pesquisa de Matérias'),
row1, row2, form_actions(label='Pesquisar')))

35
sapl/protocoloadm/migrations/0041_auto_20220804_2239.py

@ -0,0 +1,35 @@
# Generated by Django 2.2.28 on 2022-08-05 01:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('materia', '0081_auto_20220321_0934'),
('protocoloadm', '0040_auto_20220321_0934'),
]
operations = [
migrations.CreateModel(
name='VinculoDocAdminMateria',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('data_anexacao', models.DateField(verbose_name='Data Anexação')),
('data_desanexacao', models.DateField(blank=True, null=True, verbose_name='Data Desanexação')),
('documento', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='materialegislativa_vinculada_set', to='protocoloadm.DocumentoAdministrativo', verbose_name='Documento Administrativo')),
('materia', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documentoadministrativo_vinculado_set', to='materia.MateriaLegislativa', verbose_name='Matéria Legislativa')),
],
options={
'verbose_name': 'Vinculo entre Documento Administrativo e Matéria Legislativa',
'verbose_name_plural': 'Vinculos entre Documento Administrativo e Matéria Legislativa',
'ordering': ('id',),
},
),
migrations.AddField(
model_name='documentoadministrativo',
name='materiasvinculadas',
field=models.ManyToManyField(blank=True, related_name='docadmsvinculados', through='protocoloadm.VinculoDocAdminMateria', to='materia.MateriaLegislativa'),
),
]

23
sapl/protocoloadm/migrations/0042_auto_20220805_1236.py

@ -0,0 +1,23 @@
# Generated by Django 2.2.28 on 2022-08-05 15:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0081_auto_20220321_0934'),
('protocoloadm', '0041_auto_20220804_2239'),
]
operations = [
migrations.AlterField(
model_name='documentoadministrativo',
name='materiasvinculadas',
field=models.ManyToManyField(blank=True, related_name='docadmvinculados', through='protocoloadm.VinculoDocAdminMateria', to='materia.MateriaLegislativa'),
),
migrations.AlterUniqueTogether(
name='vinculodocadminmateria',
unique_together={('documento', 'materia')},
),
]

63
sapl/protocoloadm/models.py

@ -5,7 +5,8 @@ from model_utils import Choices
import reversion import reversion
from sapl.base.models import Autor from sapl.base.models import Autor
from sapl.materia.models import TipoMateriaLegislativa, UnidadeTramitacao from sapl.materia.models import TipoMateriaLegislativa, UnidadeTramitacao,\
MateriaLegislativa
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, texto_upload_path, from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, texto_upload_path,
get_settings_auth_user_model, get_settings_auth_user_model,
OverwriteStorage) OverwriteStorage)
@ -91,7 +92,8 @@ class Protocolo(models.Model):
blank=True blank=True
) )
de_proposicao = models.BooleanField(default=False) de_proposicao = models.BooleanField(default=False)
# Não foi utilizado auto_now_add=True em timestamp porque ele usa datetime.now que não é timezone aware. # Não foi utilizado auto_now_add=True em timestamp porque ele usa
# datetime.now que não é timezone aware.
timestamp = models.DateTimeField( timestamp = models.DateTimeField(
null=True, null=True,
blank=True, blank=True,
@ -160,7 +162,7 @@ class DocumentoAdministrativo(models.Model):
numero = models.PositiveIntegerField(verbose_name=_('Número')) numero = models.PositiveIntegerField(verbose_name=_('Número'))
complemento = models.CharField(max_length=10, blank=True, complemento = models.CharField(max_length=10, blank=True,
verbose_name=_('Complemento')) verbose_name=_('Complemento'))
ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'), ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'),
choices=RANGE_ANOS) choices=RANGE_ANOS)
@ -214,6 +216,17 @@ class DocumentoAdministrativo(models.Model):
) )
) )
materiasvinculadas = models.ManyToManyField(
MateriaLegislativa,
blank=True,
through='VinculoDocAdminMateria',
related_name='docadmvinculados',
through_fields=(
'documento',
'materia'
)
)
user = models.ForeignKey( user = models.ForeignKey(
get_settings_auth_user_model(), get_settings_auth_user_model(),
verbose_name=_('Usuário'), verbose_name=_('Usuário'),
@ -242,6 +255,14 @@ class DocumentoAdministrativo(models.Model):
'tipo': self.tipo, 'assunto': self.assunto 'tipo': self.tipo, 'assunto': self.assunto
} }
@property
def epigrafe(self):
return _('%(tipo)s - %(numero)s/%(ano)s') % {
'tipo': self.tipo,
'numero': self.numero,
'ano': self.ano
}
def delete(self, using=None, keep_parents=False): def delete(self, using=None, keep_parents=False):
texto_integral = self.texto_integral texto_integral = self.texto_integral
result = super().delete(using=using, keep_parents=keep_parents) result = super().delete(using=using, keep_parents=keep_parents)
@ -436,6 +457,42 @@ class Anexado(models.Model):
} }
@reversion.register()
class VinculoDocAdminMateria(models.Model):
documento = models.ForeignKey(
DocumentoAdministrativo, related_name='materialegislativa_vinculada_set',
on_delete=models.CASCADE,
verbose_name=_('Documento Administrativo')
)
materia = models.ForeignKey(
MateriaLegislativa, related_name='documentoadministrativo_vinculado_set',
on_delete=models.CASCADE,
verbose_name=_('Matéria Legislativa')
)
data_anexacao = models.DateField(verbose_name=_('Data Anexação'))
data_desanexacao = models.DateField(
blank=True, null=True, verbose_name=_('Data Desanexação')
)
class Meta:
verbose_name = _(
'Vinculo entre Documento Administrativo e Matéria Legislativa')
verbose_name_plural = _(
'Vinculos entre Documento Administrativo e Matéria Legislativa')
ordering = ('id',)
unique_together = (
('documento', 'materia'),
)
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() @reversion.register()
class AcompanhamentoDocumento(models.Model): class AcompanhamentoDocumento(models.Model):
usuario = models.CharField(max_length=50) usuario = models.CharField(max_length=50)

9
sapl/protocoloadm/urls.py

@ -25,7 +25,9 @@ from sapl.protocoloadm.views import (AcompanhamentoDocumentoView,
AnexadoCrud, DocumentoAnexadoEmLoteView, AnexadoCrud, DocumentoAnexadoEmLoteView,
PrimeiraTramitacaoEmLoteAdmView, PrimeiraTramitacaoEmLoteAdmView,
TramitacaoEmLoteAdmView, TramitacaoEmLoteAdmView,
apaga_protocolos_view) apaga_protocolos_view,
VinculoDocAdminMateriaCrud,
VinculoDocAdminMateriaEmLoteView)
from .apps import AppConfig from .apps import AppConfig
@ -36,7 +38,8 @@ urlpatterns_documento_administrativo = [
include(DocumentoAdministrativoCrud.get_urls() + include(DocumentoAdministrativoCrud.get_urls() +
AnexadoCrud.get_urls() + AnexadoCrud.get_urls() +
TramitacaoAdmCrud.get_urls() + TramitacaoAdmCrud.get_urls() +
DocumentoAcessorioAdministrativoCrud.get_urls())), DocumentoAcessorioAdministrativoCrud.get_urls() +
VinculoDocAdminMateriaCrud.get_urls())),
url(r'^docadm/pesq-doc-adm', url(r'^docadm/pesq-doc-adm',
PesquisarDocumentoAdministrativoView.as_view(), name='pesq_doc_adm'), PesquisarDocumentoAdministrativoView.as_view(), name='pesq_doc_adm'),
@ -46,6 +49,8 @@ urlpatterns_documento_administrativo = [
url(r'^docadm/(?P<pk>\d+)/anexado_em_lote', DocumentoAnexadoEmLoteView.as_view(), url(r'^docadm/(?P<pk>\d+)/anexado_em_lote', DocumentoAnexadoEmLoteView.as_view(),
name='anexado_em_lote'), name='anexado_em_lote'),
url(r'^docadm/(?P<pk>\d+)/vinculo-em-lote', VinculoDocAdminMateriaEmLoteView.as_view(),
name='vinculodocadminmateria_em_lote'),
] ]
urlpatterns_protocolo = [ urlpatterns_protocolo = [

160
sapl/protocoloadm/views.py

@ -35,7 +35,10 @@ from sapl.crud.base import (Crud, CrudAux, MasterDetailCrud, make_pagination,
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa, UnidadeTramitacao from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa, UnidadeTramitacao
from sapl.materia.views import gerar_pdf_impressos from sapl.materia.views import gerar_pdf_impressos
from sapl.parlamentares.models import Legislatura, Parlamentar from sapl.parlamentares.models import Legislatura, Parlamentar
from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo from sapl.protocoloadm.forms import VinculoDocAdminMateriaForm,\
VinculoDocAdminMateriaEmLoteFilterSet
from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo,\
VinculoDocAdminMateria
from sapl.relatorios.views import relatorio_doc_administrativos from sapl.relatorios.views import relatorio_doc_administrativos
from sapl.utils import (create_barcode, get_base_url, get_client_ip, from sapl.utils import (create_barcode, get_base_url, get_client_ip,
get_mime_type_from_file_extension, lista_anexados, get_mime_type_from_file_extension, lista_anexados,
@ -1126,7 +1129,7 @@ class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView):
context['object_list'] = [] context['object_list'] = []
else: else:
context['temp_object_list'] = context['object_list'].order_by( context['temp_object_list'] = context['object_list'].order_by(
'numero', '-ano') 'numero', '-ano')
context['object_list'] = [] context['object_list'] = []
for obj in context['temp_object_list']: for obj in context['temp_object_list']:
if not obj.pk == int(context['root_pk']): if not obj.pk == int(context['root_pk']):
@ -1156,7 +1159,6 @@ class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView):
if not ciclico: if not ciclico:
context['object_list'].append(obj) context['object_list'].append(obj)
context['numero_res'] = len(context['object_list']) context['numero_res'] = len(context['object_list'])
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else '' context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
@ -1300,7 +1302,6 @@ class TramitacaoAdmCrud(MasterDetailCrud):
return initial return initial
class ListView(DocumentoAdministrativoMixin, MasterDetailCrud.ListView): class ListView(DocumentoAdministrativoMixin, MasterDetailCrud.ListView):
def get_queryset(self): def get_queryset(self):
@ -1751,3 +1752,154 @@ def apaga_protocolos_view(request):
return JsonResponse({'type': 'success', 'msg': ''}) return JsonResponse({'type': 'success', 'msg': ''})
else: else:
return JsonResponse({'type': 'error', 'msg': 'Senha Incorreta'}) return JsonResponse({'type': 'error', 'msg': 'Senha Incorreta'})
class VinculoDocAdminMateriaCrud(MasterDetailCrud):
model = VinculoDocAdminMateria
parent_field = 'documento'
help_topic = 'vinculodocadminmateria'
public = [RP_LIST, RP_DETAIL]
class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = ['data_anexacao', ('materia', 'materia__ementa')]
@property
def verbose_name(self):
return _('Vinculo')
@property
def verbose_name_plural(self):
return _('Vinculos')
@property
def title(self):
return self.object.documento.epigrafe
class CreateView(MasterDetailCrud.CreateView):
form_class = VinculoDocAdminMateriaForm
class UpdateView(MasterDetailCrud.UpdateView):
form_class = VinculoDocAdminMateriaForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = self.object.documento.epigrafe
return context
def get_initial(self):
initial = super(UpdateView, self).get_initial()
initial['tipo'] = self.object.materia.tipo.id
initial['numero'] = self.object.materia.numero
initial['ano'] = self.object.materia.ano
return initial
class DetailView(MasterDetailCrud.DetailView):
@property
def layout_key(self):
return 'VinculoDocAdminMateriaDetail'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = self.object.documento.epigrafe
return context
class VinculoDocAdminMateriaEmLoteView(PermissionRequiredMixin, FilterView):
filterset_class = VinculoDocAdminMateriaEmLoteFilterSet
template_name = 'protocoloadm/em_lote/vinculodocadminmateria.html'
permission_required = ('protocoloadm.add_documentoadministrativo',)
def get_context_data(self, **kwargs):
context = super(VinculoDocAdminMateriaEmLoteView,
self).get_context_data(**kwargs)
context['root_pk'] = self.kwargs['pk']
context['subnav_template_name'] = 'protocoloadm/subnav.yaml'
context['title'] = _('Matérias Vinculadas em Lote')
# Verifica se os campos foram preenchidos
if not self.request.GET.get('tipo', " "):
msg = _('Por favor, selecione um tipo de matéria.')
messages.add_message(self.request, messages.ERROR, msg)
if not self.request.GET.get('data_apresentacao_0', " ") or not self.request.GET.get('data_apresentacao_1', " "):
msg = _('Por favor, preencha as datas.')
messages.add_message(self.request, messages.ERROR, msg)
return context
if not self.request.GET.get('data_apresentacao_0', " ") or not self.request.GET.get('data_apresentacao_1', " "):
msg = _('Por favor, preencha as datas.')
messages.add_message(self.request, messages.ERROR, msg)
return context
qr = self.request.GET.copy()
if not len(qr):
context['object_list'] = []
else:
context['object_list'] = context['object_list'].order_by(
'numero', '-ano')
documento = DocumentoAdministrativo.objects.get(
pk=self.kwargs['pk'])
not_list = [self.kwargs['pk']] + \
[m for m in documento.materiasvinculadas.values_list(
'id', flat=True)]
context['object_list'] = context['object_list'].exclude(
pk__in=not_list)
context['numero_res'] = len(context['object_list'])
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
context['show_results'] = show_results_filter_set(qr)
return context
def post(self, request, *args, **kwargs):
marcadas = request.POST.getlist('materia_id')
data_anexacao = datetime.strptime(
request.POST['data_anexacao'], "%d/%m/%Y").date()
if request.POST['data_desanexacao'] == '':
data_desanexacao = None
v_data_desanexacao = data_anexacao
else:
data_desanexacao = datetime.strptime(
request.POST['data_desanexacao'], "%d/%m/%Y").date()
v_data_desanexacao = data_desanexacao
if len(marcadas) == 0:
msg = _('Nenhuma máteria foi selecionada.')
messages.add_message(request, messages.ERROR, msg)
if data_anexacao > v_data_desanexacao:
msg = _('Data de anexação posterior à data de desanexação.')
messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs)
if data_anexacao > v_data_desanexacao:
msg = _('Data de anexação posterior à data de desanexação.')
messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs)
documento = DocumentoAdministrativo.objects.get(pk=kwargs['pk'])
for materia in MateriaLegislativa.objects.filter(id__in=marcadas):
v = VinculoDocAdminMateria()
v.documento = documento
v.materia = materia
v.data_anexacao = data_anexacao
v.data_desanexacao = data_desanexacao
v.save()
msg = _('Matéria(s) vinculadas(s).')
messages.add_message(request, messages.SUCCESS, msg)
success_url = reverse('sapl.protocoloadm:vinculodocadminmateria_list',
kwargs={'pk': kwargs['pk']})
return HttpResponseRedirect(success_url)

35
sapl/templates/materia/materialegislativa_detail.html

@ -1,6 +1,5 @@
{% extends "crud/detail.html" %} {% extends "crud/detail.html" %}
{% load i18n %} {% load i18n tz common_tags %}
{% load tz %}
{% block sub_actions %} {% block sub_actions %}
{{ block.super }} {{ block.super }}
@ -48,6 +47,38 @@
{{ object.normajuridica_set.last }}</a> {{ object.normajuridica_set.last }}</a>
</div> </div>
{% endif %} {% endif %}
{% if object.docadmvinculados.all.exists %}
{% if "documentos_administrativos"|get_config_attr == 'O' or "documentos_administrativos"|get_config_attr == 'R' and not user.is_anonymous %}
<div class="row">
<div class="col-12">
<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">
{% 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 }}
</a>
<br>{{ vinculodocadmmateria.documento.assunto}}
{% if vinculodocadmmateria.documento.restrito %}
<small class="text-danger">
(Documento Restrito)
</small>
{% endif %}
{% endif %}
{% if not forloop.last %}<hr>{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>
{% endif %}
{% endif %}
{% if object.audienciapublica_set.exists %} {% if object.audienciapublica_set.exists %}
<p class="control-label">&emsp; Audiência(s) Pública(s)</p> <p class="control-label">&emsp; Audiência(s) Pública(s)</p>
<div class="actions btn-group btn-group-sm" role="group"> <div class="actions btn-group btn-group-sm" role="group">

24
sapl/templates/protocoloadm/documentoadministrativo_detail.html

@ -3,6 +3,30 @@
{% block detail_content %} {% block detail_content %}
{{ block.super }} {{ block.super }}
{% if documentoadministrativo.materiasvinculadas.all.exists %}
<div class="row">
<div class="col-12">
<div id="div_id_materiasvinculadas" class="form-group">
<p class="control-label">Matérias Legislativas Vinculadas</p>
<div class="controls">
<div class="form-control-static">
{% for vinculodocadmmateria in object.materialegislativa_vinculada_set.all %}
<strong>Data Anexação:</strong> {{vinculodocadmmateria.data_anexacao}} {% if vinculodocadmmateria.data_desanexacao %} - {{vinculodocadmmateria.data_desanexacao}}{% endif %}
<br><strong>Matéria:</strong>
<a href="{% url 'sapl.materia:materialegislativa_detail' vinculodocadmmateria.materia.id %}">
{{ vinculodocadmmateria.materia }}
</a>
<br>{{vinculodocadmmateria.materia.ementa}}
{% if not forloop.last %}<hr>{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>
{% endif %}
{% if user.is_superuser %} {% if user.is_superuser %}
<div class="row"> <div class="row">
{% if documentoadministrativo.user %} {% if documentoadministrativo.user %}

74
sapl/templates/protocoloadm/em_lote/vinculodocadminmateria.html

@ -0,0 +1,74 @@
{% extends "crud/detail.html" %}
{% load i18n crispy_forms_tags %}
{% block actions %}{% endblock %}
{% block detail_content %}
{% if not show_results %}
{% crispy filter.form %}
{% endif %}
{% if show_results %}
{% if numero_res > 0 %}
{% if numero_res == 1 %}
<h3 style="text-align: right;">{% trans 'Pesquisa concluída com sucesso! Foi encontrada 1 matéria.'%}</h3>
{% else %}
<h3 style="text-align: right;">Foram encontradas {{ numero_res }} matérias.</h3>
{% endif %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Data Anexação*</label><input type="text" name="data_anexacao" class="form-control dateinput" required="True">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Data Desanexação</label><input type="text" name="data_desanexacao" class="form-control dateinput">
</div>
</div>
</div>
</fieldset>
<br />
<fieldset>
<legend>Matérias para Vincular em Lote</legend>
<table class="table table-striped table-hover">
<div class="controls">
<div class="checkbox">
<label for="id_check_all"><input type="checkbox" id="id_check_all" onchange="checkAll(this)" /> Marcar/Desmarcar Todos</label>
</div>
</div>
<thead><tr><th>Matéria</th></tr></thead>
<tbody>
{% for materia in object_list %}
<tr>
<td class="p-0">
<label for="mat_{{materia.id}}" class="d-flex w-100 p-3">
<input type="checkbox" id="mat_{{materia.id}}" name="materia_id" value="{{materia.id}}" {% if check %} checked {% endif %}/>
{{materia.tipo.sigla}} {{materia.numero}}/{{materia.ano}} - {{materia.tipo.descricao}}
</label>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
<input type="submit" value="Salvar" class="btn btn-primary"S>
</form>
{% else %}
<tr><td><h3 style="text-align: right;">Nenhuma matéria encontrada.</h3></td></tr>
{% endif %}
{% endif %}
{% endblock detail_content %}
{% block extra_js %}
<script language="JavaScript">
function checkAll(elem) {
let checkboxes = document.getElementsByName('materia_id');
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].type == 'checkbox')
checkboxes[i].checked = elem.checked;
}
}
</script>
{% endblock %}

10
sapl/templates/protocoloadm/layouts.yaml

@ -62,3 +62,13 @@ Protocolo:
- assunto_ementa - assunto_ementa
- autor - autor
- observacao - observacao
VinculoDocAdminMateria:
{% trans 'Matéria Vinculada' %}:
- tipo numero ano
- data_anexacao data_desanexacao
VinculoDocAdminMateriaDetail:
{% trans 'Matéria Vinculada' %}:
- materia|fk_urlize_for_detail data_anexacao:3 data_desanexacao:3
- documento

2
sapl/templates/protocoloadm/subnav.yaml

@ -7,3 +7,5 @@
url: tramitacaoadministrativo_list url: tramitacaoadministrativo_list
- title: {% trans 'Documento Acessório' %} - title: {% trans 'Documento Acessório' %}
url: documentoacessorioadministrativo_list url: documentoacessorioadministrativo_list
- title: {% trans 'Matérias Vinculadas' %}
url: vinculodocadminmateria_list

38
sapl/templates/protocoloadm/vinculodocadminmateria_form.html

@ -0,0 +1,38 @@
{% extends "crud/form.html" %}
{% load i18n %}
{% block extra_js %}
<script language="Javascript">
// document.getElementById("id_observacao").readOnly = true;
function recuperar_materia() {
var tipo_materia = $("#id_tipo").val()
var numero_materia = $("#id_numero").val()
var ano_materia = $("#id_ano").val()
if (tipo_materia && numero_materia && ano_materia){
$.get("/sessao/recuperar-materia",
{ tipo_materia: tipo_materia, numero_materia: numero_materia, ano_materia: ano_materia },
function(data, status) {
if ($(".ementa-materia").length === 0){
$("#div_id_tipo").closest('.row').after($('<div class="row"/>').append($('<div class="col-12"/>').append(
$('<div class="alert alert-info ementa-materia"/>').html(data.ementa)
)))
}
else {
$('.ementa-materia').html(data.ementa)
}
});
}
}
var fields = ["#id_tipo", "#id_numero", "#id_ano"];
for (i = 0; i < fields.length; i++){
$(fields[i]).change(function() {
recuperar_materia();
});
}
recuperar_materia();
$(document).ready( function() {
});
</script>
{% endblock %}

14
sapl/templates/protocoloadm/vinculodocadminmateria_list.html

@ -0,0 +1,14 @@
{% extends "crud/list.html" %}
{% load i18n %}
{% load common_tags %}
{% block more_buttons %}
{% if perms|get_add_perm:view %}
<a href="{% url 'sapl.protocoloadm:vinculodocadminmateria_em_lote' root_pk %}" class="btn btn-outline-primary">
{% trans "Adicionar Vínculos em Lote" %}
</a>
{% endif %}
{% endblock more_buttons %}
Loading…
Cancel
Save