From be381b2401631f9a2ed150fc8db832aa2bbe2691 Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Wed, 7 Nov 2018 20:15:16 -0200 Subject: [PATCH 01/16] Ref funcionalidades dos Textos Articulados MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - corrige problema no buscador de dispositivos - armazena em localstorage o form de busca de dispositivos para futuras buscas ( - muda para multi-seleção de dispositivos no buscador - Impl de multi-seleção para registro de alteração... vários dispostivos podem ser selecionados ao mesmo tempo sem a necessidade de aberturas constantes do buscador de dispositivos. - Impl revogação em bloco a multi-bloco - disponibiliza pergunta se será uma revogação em bloco ou não - como o buscador agora pode selelcionar vários dispositivos, se a revogação for em bloco, cada dispositivo selecionado no buscador é um bloco e tudo dentro dele será revogado. Exemplo: Revogar em bloco [ X ] Sim [ ] Não [ X ] Seção 1 [ ] Art. 6º - [ ] Caput do art. 6º ... [ ] Parágrafo Único ... [ ] Seção 2 [ ] Art. 7º - [ X ] Caput do art. 7º ... [ ] I - ... [ ] II - ... [ ] § 1º [ ] § 2º O que a revogação em bloco revogará? Existem dois dispositivos selecionados - Seção 1 e Caput do art. 7 - mas foi marcado revogação em bloco... portanto, Seção 1, Art. 6 e seu caput, Parágrafo único, o Caput do Art 7, Inciso I, Inciso II... seleções ambiguas são descartadas, por exemplo: se Seção 1 está marcada, e o usuário marcou tb. o Parágrafo Único, o filtro de ambiguidade tranta a Seção 1 inteira, sendo que as seleções internas so ambiguidades descatadas revogação em bloco de multiplos blocos. --- sapl/compilacao/forms.py | 15 ++- sapl/compilacao/models.py | 22 ++-- sapl/compilacao/views.py | 141 +++++++++++++++++------ sapl/static/js/app.js | 39 ++++--- sapl/static/js/compilacao.js | 51 +++++++- sapl/static/js/compilacao_edit.js | 41 ++++++- sapl/templates/compilacao/ajax_form.html | 2 +- 7 files changed, 241 insertions(+), 70 deletions(-) diff --git a/sapl/compilacao/forms.py b/sapl/compilacao/forms.py index 6407f8925..f7889604a 100644 --- a/sapl/compilacao/forms.py +++ b/sapl/compilacao/forms.py @@ -1343,7 +1343,7 @@ class DispositivoRegistroAlteracaoForm(Form): 'dispositivo_alterado', data_sapl_ta='DispositivoSearch', data_field='dispositivo_alterado', - data_type_selection='radio', + data_type_selection='checkbox', template="compilacao/layout/dispositivo_radio.html") layout.append(Fieldset(_('Registro de Alteração - ' @@ -1378,6 +1378,14 @@ class DispositivoRegistroRevogacaoForm(Form): required=False, queryset=Dispositivo.objects.all()) + revogacao_em_bloco = forms.ChoiceField( + label=_( + 'Revogar todos os dispositivos internos dos ' + 'dispositivos abaixo selecionados?'), + choices=YES_NO_CHOICES, + widget=forms.RadioSelect(), + required=True) + dispositivo_search_form = forms.CharField(widget=forms.HiddenInput(), required=False) @@ -1391,11 +1399,12 @@ class DispositivoRegistroRevogacaoForm(Form): 'dispositivo_revogado', data_sapl_ta='DispositivoSearch', data_field='dispositivo_revogado', - data_type_selection='radio', + data_type_selection='checkbox', template="compilacao/layout/dispositivo_radio.html") layout.append(Fieldset(_('Registro de Revogação - ' 'Seleção do Dispositivo a ser Revogado'), + Field(InlineRadios('revogacao_em_bloco')), row_dispositivo, css_class="col-md-12")) layout.append(Field('dispositivo_search_form')) @@ -1439,7 +1448,7 @@ class DispositivoRegistroInclusaoForm(Form): 'dispositivo_base_para_inclusao', data_sapl_ta='DispositivoSearch', data_field='dispositivo_base_para_inclusao', - data_type_selection='radio', + data_type_selection='checkbox', template="compilacao/layout/dispositivo_radio.html") layout.append(Fieldset(_('Registro de Inclusão - ' diff --git a/sapl/compilacao/models.py b/sapl/compilacao/models.py index 3d5b7a679..bb504934b 100644 --- a/sapl/compilacao/models.py +++ b/sapl/compilacao/models.py @@ -187,7 +187,7 @@ class TextoArticulado(TimestampedMixin): ementa = models.TextField(verbose_name=_('Ementa')) observacao = models.TextField(blank=True, verbose_name=_('Observação')) numero = models.CharField( - max_length=8,verbose_name=_('Número')) + max_length=8, verbose_name=_('Número')) ano = models.PositiveSmallIntegerField(verbose_name=_('Ano')) tipo_ta = models.ForeignKey( TipoTextoArticulado, @@ -1386,28 +1386,30 @@ class Dispositivo(BaseModel, TimestampedMixin): return result - def criar_espaco(self, espaco_a_criar, local): + def criar_espaco(self, espaco_a_criar, local=None): if local == 'json_add_next': proximo_bloco = Dispositivo.objects.filter( ordem__gt=self.ordem, nivel__lte=self.nivel, - ta_id=self.ta_id)[:1] + ta_id=self.ta_id).first() elif local == 'json_add_in': - # FIXME: o exclude não deve estar limitado a uma class_css caput e - # sim a qualquer filho de inserção automática proximo_bloco = Dispositivo.objects.filter( ordem__gt=self.ordem, nivel__lte=self.nivel + 1, - ta_id=self.ta_id).exclude( - tipo_dispositivo__class_css='caput')[:1] + ta_id=self.ta_id).exclude(auto_inserido=True).first() + elif local == 'json_add_in_with_auto': + proximo_bloco = Dispositivo.objects.filter( + ordem__gt=self.ordem, + nivel__lte=self.nivel + 1, + ta_id=self.ta_id).first() else: proximo_bloco = Dispositivo.objects.filter( ordem__gte=self.ordem, - ta_id=self.ta_id)[:1] + ta_id=self.ta_id).first() - if proximo_bloco.exists(): - ordem = proximo_bloco[0].ordem + if proximo_bloco: + ordem = proximo_bloco.ordem proximo_bloco = Dispositivo.objects.order_by('-ordem').filter( ordem__gte=ordem, ta_id=self.ta_id) diff --git a/sapl/compilacao/views.py b/sapl/compilacao/views.py index 6d71344eb..a82f63556 100644 --- a/sapl/compilacao/views.py +++ b/sapl/compilacao/views.py @@ -9,10 +9,12 @@ from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ValidationError from django.core.signing import Signer from django.core.urlresolvers import reverse, reverse_lazy from django.db import transaction from django.db.models import Q +from django.db.models.query import QuerySet from django.http.response import (HttpResponse, HttpResponseRedirect, JsonResponse, Http404) from django.shortcuts import get_object_or_404, redirect @@ -1525,6 +1527,13 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): return data def remover_dispositivo(self, base, bloco): + + if base.tipo_dispositivo.dispositivo_de_alteracao: + bloco = False + for d in base.dispositivos_alterados_set.all(): + d.refresh_from_db() + self.remover_dispositivo(d, bloco) + username = self.request.user.username base_ordem = base.ordem if base.dispositivo_subsequente or base.dispositivo_substituido: @@ -1836,7 +1845,10 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): dpts = base.dispositivos_alterados_set.all().order_by( '-ordem_bloco_atualizador') for dpt in dpts: - self.remover_dispositivo(dpt, False) + try: + self.remover_dispositivo(dpt, False) + except Exception as e: + print(e) if base.pk: """ @@ -2585,17 +2597,20 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, return self.json_add_next_registra_inclusao( context, local_add='json_add_in') - def registra_revogacao(self, bloco_alteracao, dispositivo_a_revogar): + def registra_revogacao(self, bloco_alteracao, dsp_a_rev, em_bloco=False): + return self.registra_alteracao( bloco_alteracao, - dispositivo_a_revogar, - revogacao=True + dsp_a_rev, + revogacao=True, + em_bloco=em_bloco ) def registra_alteracao(self, bloco_alteracao, - dispositivo_a_alterar, - revogacao=False): + dsp_a_alterar, + revogacao=False, + em_bloco=False): """ Caracteristicas: 1 - Se é um dispositivo simples e sem subsequente @@ -2621,14 +2636,69 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, data.update({'pk': bloco_alteracao.pk, 'pai': [bloco_alteracao.pk, ]}) - history = dispositivo_a_alterar.history() + if isinstance(dsp_a_alterar, list): + dsps = Dispositivo.objects.filter(id__in=dsp_a_alterar) + dsps_ids = set() + for d in dsps: + ds = d + while ds.dispositivo_subsequente: + ds = ds.dispositivo_subsequente + dsps_ids.add(ds.pk) + + if em_bloco: + proximo_bloco = Dispositivo.objects.filter( + ordem__gt=ds.ordem, + nivel__lte=ds.nivel, + ta_id=ds.ta_id).first() + + params = { + 'ta_id': ds.ta_id, + 'nivel__gte': ds.nivel, + 'ordem__gte': ds.ordem, + 'dispositivo_subsequente__isnull': True + } + + if proximo_bloco: + params['ordem__lt'] = proximo_bloco.ordem + + bloco = Dispositivo.objects.filter( + **params).values_list('id', 'auto_inserido') + for id, auto in bloco: + if auto: + dsp_pai = Dispositivo.objects.filter( + pk=id + ).values_list('dispositivo_pai', flat=True).first() + if dsp_pai in dsps_ids: + dsps_ids.remove(dsp_pai) + dsps_ids.add(id) + + dsps_ids = Dispositivo.objects.filter( + id__in=dsps_ids + ).values_list('id', flat="True") + for dsp in dsps_ids: + with transaction.atomic(): + data.update( + self.registra_alteracao( + bloco_alteracao, + dsp, + revogacao + ) + ) + if 'message' in data and 'danger' in data['message']['type']: + return data + return data + + dsp_a_alterar = Dispositivo.objects.get( + pk=dsp_a_alterar) - for d in history: + history = dsp_a_alterar.history() + + for d in list(history): if d.inicio_vigencia <= bloco_alteracao.inicio_vigencia: - dispositivo_a_alterar = d + dsp_a_alterar = d break - if (dispositivo_a_alterar.inicio_vigencia > + if (dsp_a_alterar.inicio_vigencia > bloco_alteracao.inicio_vigencia): self.set_message( data, 'danger', @@ -2637,7 +2707,7 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, 'Alterador!'), time=10000) return data - if dispositivo_a_alterar.tipo_dispositivo.dispositivo_de_articulacao\ + if dsp_a_alterar.tipo_dispositivo.dispositivo_de_articulacao\ and not revogacao: self.set_message( data, 'warning', @@ -2647,13 +2717,13 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, 'para o dispositivo que se quer alterar.'), modal=True) ndp = Dispositivo.new_instance_based_on( - dispositivo_a_alterar, dispositivo_a_alterar.tipo_dispositivo) - ndp.auto_inserido = dispositivo_a_alterar.auto_inserido - ndp.rotulo = dispositivo_a_alterar.rotulo + dsp_a_alterar, dsp_a_alterar.tipo_dispositivo) + ndp.auto_inserido = dsp_a_alterar.auto_inserido + ndp.rotulo = dsp_a_alterar.rotulo ndp.publicacao = bloco_alteracao.publicacao if not revogacao: - ndp.texto = dispositivo_a_alterar.texto + ndp.texto = dsp_a_alterar.texto else: ndp.texto = Dispositivo.TEXTO_PADRAO_DISPOSITIVO_REVOGADO ndp.dispositivo_de_revogacao = True @@ -2668,15 +2738,15 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, ndp.inicio_vigencia = bloco_alteracao.inicio_eficacia try: - ordem = dispositivo_a_alterar.criar_espaco( - espaco_a_criar=1, local='json_add_in') + ordem = dsp_a_alterar.criar_espaco( + espaco_a_criar=1, local='json_add_in_with_auto') ndp.ordem = ordem ndp.dispositivo_atualizador = bloco_alteracao ndp.ta_publicado = bloco_alteracao.ta - p = dispositivo_a_alterar - n = dispositivo_a_alterar.dispositivo_subsequente + p = dsp_a_alterar + n = dsp_a_alterar.dispositivo_subsequente ndp.dispositivo_substituido = p ndp.dispositivo_subsequente = n @@ -2711,7 +2781,7 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, n.dispositivo_substituido = ndp n.save() - filhos_diretos = dispositivo_a_alterar.dispositivos_filhos_set + filhos_diretos = dsp_a_alterar.dispositivos_filhos_set for d in filhos_diretos.all(): d.dispositivo_pai = ndp d.save() @@ -2728,14 +2798,20 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, self.set_message( data, 'success', _('Dispositivo de Revogação adicionado com sucesso.')) - + # data.update({'pk': ndp.pk, + # 'pai': [bloco_alteracao.pk, ]}) + except ValidationError as ve: + self.set_message( + data, 'danger', + _('O dispositivo ({} - {}) já existe neste bloco.'.format( + ndp.tipo_dispositivo, + ndp.get_nomenclatura_completa())), time=10000) except Exception as e: username = self.request.user.username self.logger.error("user=" + username + ". " + str(e)) - print(e) - - data.update({'pk': ndp.pk, - 'pai': [bloco_alteracao.pk, ]}) + self.set_message( + data, 'danger', + _('Não é foi possível registrar sua solicitação!'), time=10000) return data @@ -2836,17 +2912,16 @@ class DispositivoDinamicEditView( formtype = request.POST['formtype'] if formtype == 'get_form_alteracao': - dispositivo_a_alterar = Dispositivo.objects.get( - pk=request.POST['dispositivo_alterado']) - - data = self.registra_alteracao(d, dispositivo_a_alterar) + data = self.registra_alteracao( + d, request.POST.getlist('dispositivo_alterado[]', [])) elif formtype == 'get_form_revogacao': - dispositivo_a_revogar = Dispositivo.objects.get( - pk=request.POST['dispositivo_revogado']) - - data = self.registra_revogacao(d, dispositivo_a_revogar) + data = self.registra_revogacao( + d, + request.POST.getlist('dispositivo_revogado[]', []), + request.POST.get("revogacao_em_bloco") == "True" + ) if formtype == 'get_form_inclusao': diff --git a/sapl/static/js/app.js b/sapl/static/js/app.js index 132d96e3b..c6a4d4259 100644 --- a/sapl/static/js/app.js +++ b/sapl/static/js/app.js @@ -178,30 +178,39 @@ function OptionalCustomFrontEnd() { instance.customCheckBoxAndRadioWithoutLabel = function() { $('[type=radio], [type=checkbox]').each(function() { - var _this = $(this); - var _label = _this.closest('label'); - - if (_label.length) - return; + let _this = $(this) + + if (this.id === undefined || this.id.length === 0) { + return + } + + let _label = _this.closest('label') - if (this.id) + if (_label.length === 0) { _label = $('label[for='+this.id+']'); - else { - _label = $('