@ -1,13 +1,16 @@
from datetime import datetime
import os
from crispy_forms . bootstrap import Alert , InlineCheckboxes
from crispy_forms . bootstrap import Alert , InlineCheckboxes , FormActions , \
InlineRadios
from crispy_forms . helper import FormHelper
from crispy_forms . layout import HTML , Button , Column , Fieldset , Layout , Row , \
Field
Field , Submit
from django import forms
from django . contrib . contenttypes . models import ContentType
from django . core . exceptions import ObjectDoesNotExist , ValidationError
from django . core . files . base import File
from django . db import models , transaction
from django . db . models import Max
from django . forms import ModelForm , widgets
@ -15,19 +18,20 @@ from django.forms.forms import Form
from django . utils . translation import ugettext_lazy as _
import django_filters
from sapl import base
from sapl . base . models import Autor
from sapl . comissoes . models import Comissao
from sapl . crispy_layout_mixin import form_actions , to_row , to_column , \
SaplFormLayout
from sapl . materia . models import TipoProposicao
from sapl . materia . models import TipoProposicao , RegimeTramitacao , TipoDocumento
from sapl . norma . models import ( LegislacaoCitada , NormaJuridica ,
TipoNormaJuridica )
from sapl . parlamentares . models import Parlamentar
from sapl . protocoloadm . models import Protocolo
from sapl . settings import MAX_DOC_UPLOAD_SIZE
from sapl . utils import ( RANGE_ANOS , RangeWidgetOverride , autor_label ,
autor_modal , models_with_gr_for_model ,
ChoiceWithoutValidationField )
ChoiceWithoutValidationField , YES_NO_CHOICES )
import sapl
from . models import ( AcompanhamentoMateria , Anexada , Autoria ,
DespachoInicial , DocumentoAcessorio , MateriaLegislativa ,
@ -873,26 +877,36 @@ class ProposicaoForm(forms.ModelForm):
choices = TIPO_TEXTO_CHOICE ,
widget = widgets . CheckboxSelectMultiple ( ) )
materia_de_vinculo = forms . ModelChoiceField (
queryset = MateriaLegislativa . objects . all ( ) ,
widget = widgets . HiddenInput ( ) ,
required = False )
class Meta :
model = Proposicao
fields = [ ' tipo ' ,
' descricao ' ,
' texto_original ' ,
' materia_de_vinculo ' ,
' tipo_materia ' ,
' numero_materia ' ,
' ano_materia ' ,
' tipo_texto ' ]
widgets = {
' descricao ' : widgets . Textarea ( attrs = { ' rows ' : 4 } ) }
def __init__ ( self , * args , * * kwargs ) :
self . texto_articulado_proposicao = base . models . AppConfig . attr (
self . texto_articulado_proposicao = sapl . base . models . AppConfig . attr (
' texto_articulado_proposicao ' )
if not self . texto_articulado_proposicao :
self . tipo_texto = None
self . TIPO_TEXTO_CHOICE = None
if ' tipo_texto ' in self . Meta . fields :
self . Meta . fields . remove ( ' tipo_texto ' )
if ' tipo_texto ' in self . _meta . fields :
self . _meta . fields . remove ( ' tipo_texto ' )
else :
if ' tipo_texto ' not in self . _meta . fields :
self . _meta . fields . append ( ' tipo_texto ' )
fields = [
to_column ( ( Fieldset (
@ -914,8 +928,8 @@ class ProposicaoForm(forms.ModelForm):
fields . append (
to_column ( ( InlineCheckboxes ( ' tipo_texto ' ) , 5 ) ) , )
fields . append (
to_column ( ( ' texto_original ' , 7 ) ) , )
fields . append ( to_column ( (
' texto_original ' , 7 if self . texto_articulado_proposicao else 12 ) ) )
self . helper = FormHelper ( )
self . helper . layout = SaplFormLayout ( * fields )
@ -930,6 +944,14 @@ class ProposicaoForm(forms.ModelForm):
if self . instance . texto_articulado . exists ( ) :
self . fields [ ' tipo_texto ' ] . initial . append ( ' T ' )
if self . instance . materia_de_vinculo :
self . fields [
' tipo_materia ' ] . initial = self . instance . materia_de_vinculo . tipo
self . fields [
' numero_materia ' ] . initial = self . instance . materia_de_vinculo . numero
self . fields [
' ano_materia ' ] . initial = self . instance . materia_de_vinculo . ano
def clean_texto_original ( self ) :
texto_original = self . cleaned_data . get ( ' texto_original ' , False )
if texto_original :
@ -946,14 +968,327 @@ class ProposicaoForm(forms.ModelForm):
if tm and am and nm :
try :
materia = MateriaLegislativa . objects . get (
materia_de_vinculo = MateriaLegislativa . objects . get (
tipo_id = tm ,
ano = am ,
numero = nm
)
except ObjectDoesNotExist :
msg = _ ( ' Matéria Vinculada não existe! ' )
raise ValidationError ( msg )
raise ValidationError ( _ ( ' Matéria Vinculada não existe! ' ) )
else :
cd [ ' materia ' ] = materia
cd [ ' materia_de_vinculo ' ] = materia_de_vinculo
return cd
def save ( self , commit = True ) :
if self . instance . pk :
return super ( ) . save ( commit )
self . instance . ano = datetime . now ( ) . year
numero__max = Proposicao . objects . filter (
autor = self . instance . autor ,
ano = datetime . now ( ) . year ) . aggregate ( Max ( ' numero_proposicao ' ) )
numero__max = numero__max [ ' numero_proposicao__max ' ]
self . instance . numero_proposicao = (
numero__max + 1 ) if numero__max else 1
self . instance . save ( )
return self . instance
class ConfirmarProposicaoForm ( ProposicaoForm ) :
tipo_readonly = forms . CharField (
label = TipoProposicao . _meta . verbose_name ,
required = False , widget = widgets . TextInput (
attrs = { ' readonly ' : ' readonly ' } ) )
autor_readonly = forms . CharField (
label = Autor . _meta . verbose_name ,
required = False , widget = widgets . TextInput (
attrs = { ' readonly ' : ' readonly ' } ) )
justificativa_devolucao = forms . CharField (
required = False , widget = widgets . Textarea ( attrs = { ' rows ' : 5 } ) )
regime_tramitacao = forms . ModelChoiceField (
required = False , queryset = RegimeTramitacao . objects . all ( ) )
gerar_protocolo = forms . ChoiceField (
label = _ ( ' Gerar Protocolo na incorporação? ' ) ,
choices = YES_NO_CHOICES ,
widget = widgets . RadioSelect ( ) )
numero_de_paginas = forms . IntegerField ( required = False ,
label = _ ( ' Número de Páginas ' ) , )
class Meta :
model = Proposicao
fields = [
' data_envio ' ,
' descricao ' ,
' justificativa_devolucao ' ,
' gerar_protocolo ' ,
' numero_de_paginas '
]
widgets = {
' descricao ' : widgets . Textarea (
attrs = { ' readonly ' : ' readonly ' , ' rows ' : 4 } ) ,
' data_envio ' : widgets . DateTimeInput (
attrs = { ' readonly ' : ' readonly ' } ) ,
}
def __init__ ( self , * args , * * kwargs ) :
self . proposicao_incorporacao_obrigatoria = \
sapl . base . models . AppConfig . attr (
' proposicao_incorporacao_obrigatoria ' )
if self . proposicao_incorporacao_obrigatoria != ' C ' :
self . gerar_protocolo . required = False
if ' gerar_protocolo ' in self . _meta . fields :
self . _meta . fields . remove ( ' gerar_protocolo ' )
else :
if ' gerar_protocolo ' not in self . _meta . fields :
self . _meta . fields . append ( ' gerar_protocolo ' )
self . gerar_protocolo . required = False # FIXME True
if self . proposicao_incorporacao_obrigatoria == ' N ' :
if ' numero_de_paginas ' in self . _meta . fields :
self . _meta . fields . remove ( ' numero_de_paginas ' )
else :
if ' numero_de_paginas ' not in self . _meta . fields :
self . _meta . fields . append ( ' numero_de_paginas ' )
# esta chamada isola o __init__ de ProposicaoForm
super ( ProposicaoForm , self ) . __init__ ( * args , * * kwargs )
fields = [
Fieldset (
_ ( ' Dados Básicos ' ) ,
to_column ( ( ' tipo_readonly ' , 4 ) ) ,
to_column ( ( ' data_envio ' , 3 ) ) ,
to_column ( ( ' autor_readonly ' , 5 ) ) ,
to_column ( ( ' descricao ' , 12 ) ) ) ]
fields . append (
Fieldset ( _ ( ' Vinculado a Matéria Legislativa ' ) ,
to_column ( ( ' tipo_materia ' , 3 ) ) ,
to_column ( ( ' numero_materia ' , 2 ) ) ,
to_column ( ( ' ano_materia ' , 2 ) ) ,
to_column (
( Alert ( _ ( ' O responsável pela incorporação pode '
' alterar a anexação. Limpar os campos '
' de Vinculação gera um %s independente '
' sem anexação se for possível para esta '
' Proposição. Não sendo, a rotina de incorporação '
' não permitirá estes campos serem vazios. '
) % self . instance . tipo . conteudo ,
css_class = " alert-info " ,
dismiss = False ) , 5 ) ) ,
to_column (
( Alert ( ' ' ,
css_class = " ementa_materia hidden alert-info " ,
dismiss = False ) , 12 ) ) ) )
submit_incorporar = Submit ( ' incorporar ' , _ ( ' Incorporar ' ) )
itens_incorporacao = [ ' regime_tramitacao ' ]
if self . proposicao_incorporacao_obrigatoria == ' C ' :
itens_incorporacao . append ( InlineRadios ( ' gerar_protocolo ' ) )
if self . proposicao_incorporacao_obrigatoria != ' N ' :
itens_incorporacao . append ( ' numero_de_paginas ' )
fields . append (
Fieldset (
_ ( ' Registro de Incorporação ' ) ,
* [ to_column ( ( itens , 4 ) ) for itens in itens_incorporacao ] ,
FormActions ( submit_incorporar , css_class = ' pull-right ' )
) )
fields . append (
Fieldset (
_ ( ' Registro de Devolução ' ) ,
' justificativa_devolucao ' ,
FormActions ( Submit (
' devolver ' , _ ( ' Devolver ' ) ,
css_class = ' btn-danger ' ) , css_class = ' pull-right ' )
) )
self . helper = FormHelper ( )
self . helper . layout = Layout ( * fields )
self . fields [ ' tipo_readonly ' ] . initial = self . instance . tipo . descricao
self . fields [ ' autor_readonly ' ] . initial = str ( self . instance . autor )
if self . instance . materia_de_vinculo :
self . fields [
' tipo_materia ' ] . initial = self . instance . materia_de_vinculo . tipo
self . fields [
' numero_materia ' ] . initial = self . instance . materia_de_vinculo . numero
self . fields [
' ano_materia ' ] . initial = self . instance . materia_de_vinculo . ano
if self . proposicao_incorporacao_obrigatoria == ' C ' :
self . fields [ ' gerar_protocolo ' ] . initial = True
def clean ( self ) :
if ' incorporar ' in self . data :
cd = ProposicaoForm . clean ( self )
# FIXME em caso de incorporação validar regime e numero de páginas
if self . instance . tipo . conteudo . model_class ( ) == TipoDocumento and \
not cd [ ' materia_de_vinculo ' ] :
raise ValidationError (
_ ( ' Documentos não podem ser incorporados sem definir '
' para qual Matéria Legislativa ele se destina. ' ) )
elif ' devolver ' in self . data :
cd = self . cleaned_data
if ' justificativa_devolucao ' not in cd or \
not cd [ ' justificativa_devolucao ' ] :
# TODO Implementar notificação ao autor por email
raise ValidationError (
_ ( ' Adicione uma Justificativa para devolução. ' ) )
else :
raise ValidationError (
_ ( ' Dados de Confirmação invalidos. ' ) )
return cd
def save ( self , commit = False ) :
# TODO Implementar workflow entre protocolo e autores
cd = self . cleaned_data
if ' devolver ' in self . data :
self . instance . data_devolucao = datetime . now ( )
self . instance . data_recebimento = None
self . instance . data_envio = None
self . instance . save ( )
return self . instance
elif ' incorporar ' in self . data :
self . instance . justificativa_devolucao = ' '
self . instance . data_devolucao = None
self . instance . data_recebimento = datetime . now ( )
self . instance . save ( )
"""
TipoProposicao possui conteúdo genérico para a modelegam de tipos
relacionados e , a esta modelagem , qual o objeto que está associado .
Porem , cada registro a ser gerado pode possuir uma estrutura diferente ,
é os casos básicos já implementados ,
TipoDocumento e TipoMateriaLegislativa , que são modelos utilizados
em DocumentoAcessorio e MateriaLegislativa geradas ,
por sua vez a partir de uma Proposição .
Portanto para estas duas e para outras implementações que possam surgir
possuindo com matéria prima uma Proposição , dada sua estrutura ,
deverá contar também com uma implementação particular aqui no código
abaixo .
"""
proposicao = self . instance
conteudo_gerado = None
if self . instance . tipo . conteudo . model_class ( ) == TipoMateriaLegislativa :
numero__max = MateriaLegislativa . objects . filter (
tipo = proposicao . tipo . tipo_conteudo_related ,
ano = datetime . now ( ) . year ) . aggregate ( Max ( ' numero ' ) )
numero__max = numero__max [ ' numero__max ' ]
# dados básicos
materia = MateriaLegislativa ( )
materia . numero = ( numero__max + 1 ) if numero__max else 1
materia . tipo = proposicao . tipo . tipo_conteudo_related
materia . ementa = proposicao . descricao
materia . ano = datetime . now ( ) . year
materia . data_apresentacao = datetime . now ( )
materia . em_tramitacao = True
materia . regime_tramitacao = cd [ ' regime_tramitacao ' ]
materia . texto_original = File (
proposicao . texto_original ,
os . path . basename ( proposicao . texto_original . path ) )
materia . texto_articulo = proposicao . texto_articulado
materia . save ( )
conteudo_gerado = materia
# autoria
autoria = Autoria ( )
autoria . autor = proposicao . autor
autoria . materia = materia
autoria . primeiro_autor = True
autoria . save ( )
# Matéria de vinlculo
if proposicao . materia_de_vinculo :
anexada = Anexada ( )
anexada . materia_principal = proposicao . materia_de_vinculo
anexada . materia_anexada = materia
anexada . data_anexacao = datetime . now ( )
anexada . save ( )
elif self . instance . tipo . conteudo . model_class ( ) == TipoDocumento :
# dados básicos
doc = DocumentoAcessorio ( )
doc . materia = proposicao . materia_de_vinculo
"""
FIXME Esta forma de registrar autoria é falha .
Dificilmente o usuário que possui perfil de Autor será o autor
de um Documento Acessório .
Solução pode passar pela parametrização em TipoProposicao que
possibilite abrir ou não espaço , dado o Tipo , para quem está
incorporando a proposição rediga o nome do Autor do Doc Acessório .
"""
doc . autor = str ( proposicao . autor )
doc . tipo = proposicao . tipo . tipo_conteudo_related
doc . ementa = proposicao . descricao
""" FIXME verificar questão de nome e data de documento,
doc acessório . Possivelmente pode possuir data anterior a
data de envio e / ou recebimento dada a incorporação .
"""
doc . nome = str ( proposicao . tipo . tipo_conteudo_related ) [ : 30 ]
doc . data = proposicao . data_envio
doc . arquivo = proposicao . texto_original = File (
proposicao . texto_original ,
os . path . basename ( proposicao . texto_original . path ) )
doc . save ( )
conteudo_gerado = doc
proposicao . conteudo_gerado_related = conteudo_gerado
proposicao . save ( )
# Nunca gerar protocolo
if self . proposicao_incorporacao_obrigatoria == ' N ' :
return self . instance
# ocorre se proposicao_incorporacao_obrigatoria == 'C' (condicional)
# and gerar_protocolo == False
if ' gerar_protocolo ' in cd or not cd [ ' gerar_protocolo ' ] :
return self . instance
# resta a opção proposicao_incorporacao_obrigatoria == 'C'
# and gerar_protocolo == True
# ou, proposicao_incorporacao_obrigatoria == 'O'
# que são idênticas.
"""
apesar de TipoProposicao estar com conteudo e tipo conteudo genérico ,
aqui na incorporação de proposições , para gerar protocolo , cada caso
possível de conteudo em tipo de proposição deverá ser tratado
isoladamente justamente por Protocolo não estar generalizado com
GenericForeignKey
"""
# FIXME - Implementar protocolo
# protocolo = Protocolo()
# protocolo.ano =
return self . instance