diff --git a/compilacao/forms.py b/compilacao/forms.py index 1b8d39c9f..eed197ca4 100644 --- a/compilacao/forms.py +++ b/compilacao/forms.py @@ -464,16 +464,100 @@ class PublicacaoForm(ModelForm): pass -class DispositivoForm(ModelForm): +class DispositivoIntegerField(forms.IntegerField): + + def __init__(self, field_name=None, *args, **kwargs): + + if 'required' not in kwargs: + kwargs.setdefault('required', False) + + self.widget = forms.NumberInput( + attrs={'title': Dispositivo._meta.get_field( + field_name).verbose_name, + 'onchange': 'atualizaRotulo()'}) + + super(DispositivoIntegerField, self).__init__( + min_value=0, *args, **kwargs) + + +class DispositivoEdicaoBasicaForm(ModelForm): + + texto = forms.CharField( + widget=forms.Textarea, + required=False) + + dispositivo1 = DispositivoIntegerField( + label=('1ª %s' % _('Variação')), + field_name='dispositivo1') + dispositivo2 = DispositivoIntegerField( + label=('2ª'), + field_name='dispositivo2') + dispositivo3 = DispositivoIntegerField( + label=('3ª'), + field_name='dispositivo3') + dispositivo4 = DispositivoIntegerField( + label=('4ª'), + field_name='dispositivo4') + dispositivo5 = DispositivoIntegerField( + label=('5ª'), + field_name='dispositivo5') + + rotulo = forms.CharField(label=_('Rótulo Resultante')) class Meta: model = Dispositivo - fields = [] + fields = ( + 'dispositivo0', + 'dispositivo1', + 'dispositivo2', + 'dispositivo3', + 'dispositivo4', + 'dispositivo5', + 'rotulo', + 'texto') + + widgets = { + 'dispositivo0': forms.NumberInput( + attrs={'title': _('Valor 0(zero) é permitido apenas ' + 'para Dispositivos com tipos variáveis.'), + 'onchange': 'atualizaRotulo()'})} def __init__(self, *args, **kwargs): + + layout = [] + + rotulo_fieldset = to_row([ + ('dispositivo0', 3), + ('dispositivo1', 2), + ('dispositivo2', 1), + ('dispositivo3', 1), + ('dispositivo4', 1), + ('dispositivo5', 1), + ('rotulo', 3) + ]) + + layout.append( + Fieldset( + _('Montagem do Rótulo'), + rotulo_fieldset, + css_class="col-md-12")) + + # Campo Texto + row_texto = to_row([('texto', 12)]) + css_class_texto = "col-md-12" + if 'instance' in kwargs and\ + kwargs['instance'].tipo_dispositivo.dispositivo_de_articulacao: + css_class_texto = "col-md-12 hidden" + layout.append( + Fieldset( + Dispositivo._meta.get_field('texto').verbose_name, + row_texto, + css_class=css_class_texto)) + self.helper = FormHelper() - """self.helper.layout = SaplFormLayout( - Fieldset(Publicacao._meta.verbose_name, - row1, row2, row3, css_class="col-md-12"))""" + if layout: + self.helper.layout = SaplFormLayout(*layout) + else: + self.helper.layout = SaplFormLayout() - super(PublicacaoForm, self).__init__(*args, **kwargs) + super(DispositivoEdicaoBasicaForm, self).__init__(*args, **kwargs) diff --git a/compilacao/migrations/0044_auto_20160301_1816.py b/compilacao/migrations/0045_auto_20160311_1117.py similarity index 60% rename from compilacao/migrations/0044_auto_20160301_1816.py rename to compilacao/migrations/0045_auto_20160311_1117.py index fcca5b769..07645df62 100644 --- a/compilacao/migrations/0044_auto_20160301_1816.py +++ b/compilacao/migrations/0045_auto_20160311_1117.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2016-03-01 21:16 +# Generated by Django 1.9 on 2016-03-11 14:17 from __future__ import unicode_literals from django.db import migrations, models @@ -9,7 +9,7 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('compilacao', '0043_auto_20160110_1733'), + ('compilacao', '0044_auto_20160307_0918'), ] operations = [ @@ -23,4 +23,14 @@ class Migration(migrations.Migration): name='dispositivo_substituido', field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='compilacao.Dispositivo', verbose_name='Dispositivo Substituido'), ), + migrations.AlterField( + model_name='dispositivo', + name='inconstitucionalidade', + field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Declaração de Inconstitucionalidade'), + ), + migrations.AlterField( + model_name='dispositivo', + name='texto', + field=models.TextField(blank=True, default='', verbose_name='Texto na Norma Original'), + ), ] diff --git a/compilacao/models.py b/compilacao/models.py index f45e94f64..026b4496e 100644 --- a/compilacao/models.py +++ b/compilacao/models.py @@ -135,6 +135,19 @@ class TextoArticulado(TimestampedMixin): 'numero': self.numero, 'data': defaultfilters.date(self.data, "d \d\e F \d\e Y")} + def organizar_ordem_de_dispositivos(self): + dpts = Dispositivo.objects.filter(ta=self) + + ordem_max = dpts.last().ordem + + dpts.update(ordem=F('ordem') + ordem_max) + + count = 0 + for d in dpts: + count += Dispositivo.INTERVALO_ORDEM + d.ordem = count + d.save() + class TipoNota(models.Model): sigla = models.CharField( @@ -543,7 +556,7 @@ class Dispositivo(BaseModel, TimestampedMixin): texto = models.TextField( blank=True, default='', - verbose_name=_('Texto')) + verbose_name=_('Texto na Norma Original')) texto_atualizador = models.TextField( blank=True, default='', @@ -562,7 +575,7 @@ class Dispositivo(BaseModel, TimestampedMixin): inconstitucionalidade = models.BooleanField( default=False, choices=utils.YES_NO_CHOICES, - verbose_name=_('Inconstitucionalidade')) + verbose_name=_('Declaração de Inconstitucionalidade')) # Relevant attribute only in altering norms visibilidade = models.BooleanField( default=False, diff --git a/compilacao/templatetags/compilacao_filters.py b/compilacao/templatetags/compilacao_filters.py index 6372ba127..6aab329a5 100644 --- a/compilacao/templatetags/compilacao_filters.py +++ b/compilacao/templatetags/compilacao_filters.py @@ -1,8 +1,8 @@ from django import template from django.core.signing import Signer from django.db.models import Q -from django.utils.translation import ugettext_lazy as _ from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ from compilacao.models import Dispositivo, TipoDispositivo @@ -125,7 +125,7 @@ def nomenclatura(d): result = '(' + d.tipo_dispositivo.nome + ' ' + \ d.rotulo + ')' else: - result = '(' + d.tipo_dispositivo.nome + \ + result = '(' + d.tipo_dispositivo.nome + ' ' + \ d.rotulo_padrao() + ')' return result diff --git a/compilacao/urls.py b/compilacao/urls.py index 0a2948be8..d70445d2a 100644 --- a/compilacao/urls.py +++ b/compilacao/urls.py @@ -33,8 +33,10 @@ urlpatterns_compilacao = [ url(r'^(?P[0-9]+)/text/(?P[0-9]+)/refresh', views.DispositivoSimpleEditView.as_view(), name='dispositivo_refresh'), - url(r'^(?P[0-9]+)/text/(?P[0-9]+)/edit', - views.DispositivoEditView.as_view(), name='dispositivo_edit'), + url(r'^(?P[0-9]+)/text/(?P[0-9]+)/edit', + views.DispositivoEdicaoBasicaView.as_view(), name='dispositivo_edit'), + + url(r'^(?P[0-9]+)/text/(?P[0-9]+)/actions', views.ActionsEditView.as_view(), name='dispositivo_actions'), diff --git a/compilacao/utils.py b/compilacao/utils.py index ca00f66b4..ef517e08d 100644 --- a/compilacao/utils.py +++ b/compilacao/utils.py @@ -18,6 +18,8 @@ def int_to_roman(int_value): def int_to_letter(int_value): result = '' + if not int_value: + return '0' int_value -= 1 while int_value >= 26: rest = int_value % 26 diff --git a/compilacao/views.py b/compilacao/views.py index 557ae2c49..f631d48bc 100644 --- a/compilacao/views.py +++ b/compilacao/views.py @@ -1,6 +1,6 @@ +import sys from collections import OrderedDict from datetime import datetime, timedelta -import sys from braces.views import FormMessagesMixin from django import forms @@ -20,8 +20,8 @@ from django.views.generic.detail import DetailView from django.views.generic.edit import CreateView, DeleteView, UpdateView from django.views.generic.list import ListView -from compilacao.forms import (NotaForm, PublicacaoForm, TaForm, TipoTaForm, - VideForm, DispositivoForm) +from compilacao.forms import (DispositivoEdicaoBasicaForm, NotaForm, + PublicacaoForm, TaForm, TipoTaForm, VideForm) from compilacao.models import (Dispositivo, Nota, PerfilEstruturalTextoArticulado, Publicacao, TextoArticulado, TipoDispositivo, TipoNota, @@ -29,7 +29,6 @@ from compilacao.models import (Dispositivo, Nota, VeiculoPublicacao, Vide) from crud.base import Crud, CrudListView, make_pagination - DISPOSITIVO_SELECT_RELATED = ( 'tipo_dispositivo', 'ta_publicado', @@ -1014,7 +1013,18 @@ class ActionsEditMixin: return JsonResponse(action(context), safe=False) def set_dvt(self, context): - return {} + dvt = Dispositivo.objects.get(pk=context['dispositivo_id']) + + if dvt.is_relative_auto_insert(): + dvt = dvt.dispositivo_pai + + try: + Dispositivo.objects.all().update(dispositivo_vigencia=dvt) + return {'message': str(_('Dispositivo de Vigência atualizado ' + 'com sucesso!!!'))} + except: + return {'message': str(_('Ocorreu um erro na atualização do ' + 'Dispositivo de Vigência'))} def delete_item_dispositivo(self, context): return self.delete_bloco_dispositivo(context, bloco=False) @@ -1040,11 +1050,15 @@ class ActionsEditMixin: else: data['pk'] = '' + ta_base = base.ta + # TODO: a linha abaixo causa atualização da tela inteira... # retirar a linha abaixo e identificar atualizações pontuais data['pai'] = [-1, ] - data['message'] = str(self.remover_dispositivo(base, bloco)) + + ta_base.organizar_ordem_de_dispositivos() + return data def remover_dispositivo(self, base, bloco): @@ -1068,10 +1082,7 @@ class ActionsEditMixin: p.fim_eficacia = None for d in base.dispositivos_filhos_set.all(): - if d.tipo_dispositivo.possiveis_pais.filter( - pai=base.tipo_dispositivo, - perfil__padrao=True, - filho_de_insercao_automatica=True).exists(): + if d.is_relative_auto_insert(): self.remover_dispositivo(d, bloco) elif not bloco: p.dispositivos_filhos_set.add(d) @@ -1084,10 +1095,7 @@ class ActionsEditMixin: # independente da escolha do usuário d_nivel_old = d.nivel - if d.tipo_dispositivo.possiveis_pais.filter( - pai=base.tipo_dispositivo, - perfil__padrao=True, - filho_de_insercao_automatica=True).exists(): + if d.is_relative_auto_insert(): continue # encontrar possível pai que será o primeiro parent @@ -1170,7 +1178,7 @@ class ActionsEditMixin: order_by('ordem').filter( ta_id=base.ta_id, ordem__gt=base.ordem, - nivel__lte=base.nivel) + nivel__lte=base.nivel).first() if proximo_independente_base: dcc = Dispositivo.objects.order_by('ordem').filter( @@ -1980,7 +1988,47 @@ class PublicacaoDeleteView(CompMixin, DeleteView): kwargs={'ta_id': self.kwargs['ta_id']}) -class DispositivoEditView(CompMixin, UpdateView): +class DispositivoEdicaoBasicaView(UpdateView): model = Dispositivo - form_class = DispositivoForm - template_name = "crud/form.html" + form_class = DispositivoEdicaoBasicaForm + + @property + def cancel_url(self): + return reverse_lazy( + 'compilacao:ta_text_edit', + kwargs={'ta_id': self.kwargs['ta_id']}) + '#' + str(self.object.pk) + + def get_success_url(self): + return reverse_lazy( + 'compilacao:dispositivo_edit', + kwargs={'ta_id': self.kwargs['ta_id'], 'pk': self.kwargs['pk']}) + + def get_context_data(self, **kwargs): + return UpdateView.get_context_data(self, **kwargs) + + def run_actions(self, request): + if 'action' in request.GET and\ + request.GET['action'] == 'atualiza_rotulo': + try: + d = Dispositivo.objects.get(pk=self.kwargs['pk']) + d.dispositivo0 = int(request.GET['dispositivo0']) + d.dispositivo1 = int(request.GET['dispositivo1']) + d.dispositivo2 = int(request.GET['dispositivo2']) + d.dispositivo3 = int(request.GET['dispositivo3']) + d.dispositivo4 = int(request.GET['dispositivo4']) + d.dispositivo5 = int(request.GET['dispositivo5']) + d.rotulo = d.rotulo_padrao() + except: + return True, JsonResponse({'message': str( + _('Ocorreu erro na atualização do rótulo'))}, safe=False) + return True, JsonResponse({'rotulo': d.rotulo}, safe=False) + + return False, '' + + def get(self, request, *args, **kwargs): + + flag_action, render_json_response = self.run_actions(request) + if flag_action: + return render_json_response + + return UpdateView.get(self, request, *args, **kwargs) diff --git a/static/js/app.js b/static/js/app.js index 2ae9c1c57..71edb18ac 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -1,8 +1,24 @@ +function initTinymce() { + removeTinymce(); + tinymce.init({ + mode : "textareas", + force_br_newlines : false, + force_p_newlines : false, + forced_root_block : '', + plugins: ["table save code"], + menubar: "edit format table tools", + toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent", + tools: "inserttable", + border_css: "/static/styles/style_tinymce.css", + content_css: "/static/styles/style_tinymce.css" + }); +} -tinymce.init({ - mode : "exact", - elements : "biografia-parlamentar,casa-informacoes" - }); +function removeTinymce() { + while (tinymce.editors.length > 0) { + tinymce.remove(tinymce.editors[0]); + } +} function refreshDatePicker() { $.datepicker.setDefaults($.datepicker.regional['pt-BR']); @@ -96,4 +112,5 @@ $(document).ready(function(){ refreshDatePicker(); refreshMask(); autorModal(); + initTinymce(); }); diff --git a/static/js/compilacao.js b/static/js/compilacao.js index b36783c66..3c4a3f859 100644 --- a/static/js/compilacao.js +++ b/static/js/compilacao.js @@ -1,20 +1,3 @@ -function initTinymce() { - - tinymce.init({ - mode : "textareas", - force_br_newlines : false, - force_p_newlines : false, - forced_root_block : '', - plugins: ["table save code"], - menubar: "edit format table tools", - toolbar: "save | undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent", - tools: "inserttable", - save_onsavecallback: onSubmitEditForm, - border_css: "/static/styles/compilacao_tinymce.css", - content_css: "/static/styles/compilacao_tinymce.css" - }); -} - function SetCookie(cookieName,cookieValue,nDays) { var today = new Date(); var expire = new Date(); diff --git a/static/js/compilacao_edit.js b/static/js/compilacao_edit.js index e23e547d6..51b7b8cee 100644 --- a/static/js/compilacao_edit.js +++ b/static/js/compilacao_edit.js @@ -1,3 +1,19 @@ +function initTinymceForEdit() { + + tinymce.init({ + mode : "textareas", + force_br_newlines : false, + force_p_newlines : false, + forced_root_block : '', + plugins: ["table save code"], + menubar: "edit format table tools", + toolbar: "save | undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent", + tools: "inserttable", + save_onsavecallback: onSubmitEditForm, + border_css: "/static/styles/compilacao_tinymce.css", + content_css: "/static/styles/compilacao_tinymce.css" + }); +} var editortype = "textarea"; var gets = 0; @@ -95,7 +111,6 @@ var clickUpdateDispositivo = function(event, __pk_refresh, __pk_edit, __action, } } url = pk_refresh+'/refresh?edit='+pk_edit+url; - } else if (_action.startsWith('add_')) { @@ -109,7 +124,6 @@ var clickUpdateDispositivo = function(event, __pk_refresh, __pk_edit, __action, else if (_action.startsWith('set_')) { url = pk_refresh+'/actions?action='+_action; - $("#message_block").css("display", "block"); } @@ -148,7 +162,7 @@ var clickUpdateDispositivo = function(event, __pk_refresh, __pk_edit, __action, } if ( _editortype == 'tinymce' ) { - initTinymce(); + initTinymceForEdit(); } else if (_editortype == 'textarea') { $('.csform form').submit(onSubmitEditForm); @@ -190,15 +204,10 @@ var clickUpdateDispositivo = function(event, __pk_refresh, __pk_edit, __action, $("#message_block").css("display", "block"); clearEditSelected(); if (data.pk != null) { - if (data.message != null && data.message != '') { - - modalMessage(data.message, 'alert-danger', function() { + if (!modalMessage(data.message, 'alert-danger', function() { //refreshScreenFocusPk(data); - }); - } - else { + })) refreshScreenFocusPk(data); - } } else { alert('Erro exclusão de Dispositivo!'); @@ -207,6 +216,7 @@ var clickUpdateDispositivo = function(event, __pk_refresh, __pk_edit, __action, else { clearEditSelected(); reloadFunctionClicks(); + modalMessage(data.message, 'alert-success', null); } }).always(function() { @@ -215,18 +225,22 @@ var clickUpdateDispositivo = function(event, __pk_refresh, __pk_edit, __action, } function modalMessage(message, alert, closeFunction) { - $('#modal-message #message').html(message); - $('#modal-message').modal('show'); - if (closeFunction != null) { + if (message != null && message != '') { + $('#modal-message #message').html(message); + $('#modal-message').modal('show'); $('#modal-message, #modal-message .alert button').off(); $('#modal-message .alert').removeClass('alert-success alert-info alert-warning alert-danger alert-danger'); $('#modal-message .alert').addClass(alert); - $('#modal-message').on('hidden.bs.modal', closeFunction); + + if (closeFunction != null) + $('#modal-message').on('hidden.bs.modal', closeFunction); + $('#modal-message .alert button').on('click', function() { $('#modal-message').modal('hide'); }); + return true; } - + return false; } function refreshScreenFocusPk(data) { diff --git a/static/js/compilacao_notas.js b/static/js/compilacao_notas.js index dd5a02c91..97a4709f2 100644 --- a/static/js/compilacao_notas.js +++ b/static/js/compilacao_notas.js @@ -7,7 +7,7 @@ function onEventsDneExec(pk, model) { refreshDatePicker() - $('#dne'+pk+" #button-id-submit-form").click(onSubmitEditForm); + $('#dne'+pk+" #button-id-submit-form").click(onSubmitEditNVForm); $('#dne'+pk+" .btn-close-container").click(function(){ $(this).closest('.dne-nota').removeClass('dne-nota'); $(this).closest('.dne-form').html(''); @@ -88,7 +88,7 @@ var onChangeParamNorma = function(event) { }); } -var onSubmitEditForm = function(event) { +var onSubmitEditNVForm = function(event) { var url = ''; var model = 'nota'; diff --git a/static/styles/compilacao.scss b/static/styles/compilacao.scss index ce3434db1..d0f7b86d3 100644 --- a/static/styles/compilacao.scss +++ b/static/styles/compilacao.scss @@ -919,6 +919,7 @@ a:link:after, a:visited:after { display: table; li { display: table-cell; + padding: 0 0.5em; } } @@ -1196,6 +1197,26 @@ a:link:after, a:visited:after { } } +.cp-nav-parents { + & > .dropdown-menu { + left: 0; + right: auto; + &::before { + content: ''; + position: absolute; + top: -11px; + width: 100%; + height: 11px; + } + } + &:hover { + & > .dropdown-menu { + display: block; + } + } +} + + .class_color_container { background: #ddd !important; @@ -1204,7 +1225,7 @@ a:link:after, a:visited:after { clear:both; } .mce-panel { - border: 0px solid #ccc !important; + /*border: 0px solid #ccc !important;*/ } .mce-btn button:hover { background-color: rgba(0,0,0,0.1) !important; diff --git a/static/styles/compilacao_tinymce.css b/static/styles/style_tinymce.css similarity index 98% rename from static/styles/compilacao_tinymce.css rename to static/styles/style_tinymce.css index cf39b89ad..d2909b025 100644 --- a/static/styles/compilacao_tinymce.css +++ b/static/styles/style_tinymce.css @@ -1,5 +1,3 @@ - - .mce-content-body { font-family: "Open Sans" "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif; font-style: normal; diff --git a/templates/compilacao/dispositivo_form.html b/templates/compilacao/dispositivo_form.html new file mode 100644 index 000000000..cb17c9cfa --- /dev/null +++ b/templates/compilacao/dispositivo_form.html @@ -0,0 +1,67 @@ +{% extends "base.html" %} +{% load i18n crispy_forms_tags %} +{% load compilacao_filters %} +{% load staticfiles %} +{% load sass_tags %} + +{% block head_content %}{{block.super}} + +{% endblock %} + +{% block title %} +

{{object.ta}}
{% trans 'Edição de Dispositivo' %}

+{% endblock%} +{% block base_content %} +
+ {%for parent in object.get_parents_asc %} + {%with node=parent active=False template_name='compilacao/dispositivo_form_parents.html' %} + {%include template_name%} + {%endwith%} + {%endfor %} + + {%with node=object active=True template_name='compilacao/dispositivo_form_parents.html' %} + {%include template_name%} + {%endwith%} +
+

+ {% crispy form %} +{% endblock %} + +{% block foot_js %} + {{block.super}} + +{% endblock %} + +{% block extra_js %} + +{% endblock %} diff --git a/templates/compilacao/dispositivo_form_parents.html b/templates/compilacao/dispositivo_form_parents.html new file mode 100644 index 000000000..7006d2581 --- /dev/null +++ b/templates/compilacao/dispositivo_form_parents.html @@ -0,0 +1,28 @@ +{% load i18n %} +{% load compilacao_filters %} + +{% if not node.dispositivos_filhos_set.exists %} + + {{node|nomenclatura}} + +{% else %} + +
+ + {{node|nomenclatura}} + + + + + +
+ +{% endif %} diff --git a/templates/compilacao/form.html b/templates/compilacao/form.html deleted file mode 100644 index e25d5d3ab..000000000 --- a/templates/compilacao/form.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "base.html" %} -{% load i18n crispy_forms_tags %} - -{% block base_content %} - {% crispy form %} -{% endblock %} diff --git a/templates/compilacao/text_edit.html b/templates/compilacao/text_edit.html index 30cce516a..d201c2a98 100644 --- a/templates/compilacao/text_edit.html +++ b/templates/compilacao/text_edit.html @@ -45,6 +45,61 @@ {% include 'compilacao/text_edit_bloco.html'%} + + {% endblock base_content %} {% block foot_js %} diff --git a/templates/compilacao/text_edit_bloco.html b/templates/compilacao/text_edit_bloco.html index 30bc6a2d5..c5568fe21 100644 --- a/templates/compilacao/text_edit_bloco.html +++ b/templates/compilacao/text_edit_bloco.html @@ -22,7 +22,7 @@
  • E
  • E+
  • {%endif%} -
  • E*
  • +
  • E*
  • C
  • @@ -87,8 +87,19 @@
      -
    • p: {{dpt.dispositivo_substituido_id}}, n: {{dpt.dispositivo_subsequente_id}}, Ordem: {{dpt.ordem}}, Nivel: {{dpt.nivel}}, Número: {{dpt.get_numero_completo}}
    • -
    • .
    • +
    • p: {{dpt.dispositivo_substituido_id|default:''}}, n: {{dpt.dispositivo_subsequente_id|default:''}}, Ordem: {{dpt.ordem}}, Nivel: {{dpt.nivel}}, Número: {{dpt.get_numero_completo}}
    • + + {% if dpt.dispositivo_vigencia %} +
    • {% field_verbose_name dpt 'dispositivo_vigencia'%}: {{dpt.dispositivo_vigencia|nomenclatura}}
    • + {% endif %} + +
    • + + ? + +
    • + +