Sistema de Apoio ao Processo Legislativo
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

969 lines
35 KiB

from django.contrib.auth.models import Group
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models.functions import Concat
from django.template import defaultfilters
from django.utils import formats, timezone
from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
import reversion
from sapl.base.models import SEQUENCIA_NUMERACAO, Autor
from sapl.comissoes.models import Comissao
from sapl.compilacao.models import (PerfilEstruturalTextoArticulado,
TextoArticulado)
from sapl.parlamentares.models import Parlamentar
#from sapl.protocoloadm.models import Protocolo
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, SaplGenericForeignKey,
SaplGenericRelation, restringe_tipos_de_arquivo_txt,
texto_upload_path)
EM_TRAMITACAO = [(1, 'Sim'),
(0, 'Não')]
def grupo_autor():
try:
grupo = Group.objects.get(name='Autor')
except Group.DoesNotExist:
return None
return grupo.id
@reversion.register()
class TipoProposicao(models.Model):
descricao = models.CharField(
max_length=50,
verbose_name=_('Descrição'),
unique=True,
error_messages={
'unique': _('Já existe um Tipo de Proposição com esta descrição.')
})
content_type = models.ForeignKey(
ContentType, default=None,
on_delete=models.PROTECT,
verbose_name=_('Conversão de Meta-Tipos'),
help_text=_("""
Quando uma proposição é incorporada, ela é convertida de proposição
para outro elemento dentro do Sapl. Existem alguns elementos que
uma proposição pode se tornar. Defina este meta-tipo e em seguida
escolha um Tipo Correspondente!
""")
)
object_id = models.PositiveIntegerField(
blank=True, null=True, default=None)
tipo_conteudo_related = SaplGenericForeignKey(
'content_type', 'object_id', verbose_name=_('Tipo Correspondente'))
perfis = models.ManyToManyField(
PerfilEstruturalTextoArticulado,
blank=True, verbose_name=_('Perfis Estruturais de Textos Articulados'),
help_text=_("""
Mesmo que em Configurações da Aplicação nas
Tabelas Auxiliares esteja definido que Proposições possam
utilizar Textos Articulados, ao gerar uma proposição,
a solução de Textos Articulados será disponibilizada se
o Tipo escolhido para a Proposição estiver associado a ao
menos um Perfil Estrutural de Texto Articulado.
"""))
class Meta:
verbose_name = _('Tipo de Proposição')
verbose_name_plural = _('Tipos de Proposições')
def __str__(self):
return self.descricao
@reversion.register()
class TipoMateriaLegislativa(models.Model):
sigla = models.CharField(max_length=5, verbose_name=_('Sigla'))
descricao = models.CharField(max_length=50, verbose_name=_('Descrição '))
# XXX o que é isso ?
num_automatica = models.BooleanField(default=False)
# XXX o que é isso ?
quorum_minimo_votacao = models.PositiveIntegerField(blank=True, null=True)
tipo_proposicao = SaplGenericRelation(
TipoProposicao,
related_query_name='tipomaterialegislativa_set',
fields_search=(
('descricao', '__icontains'),
('sigla', '__icontains')
))
sequencia_numeracao = models.CharField(
max_length=1,
blank=True,
verbose_name=_('Sequência de numeração'),
choices=SEQUENCIA_NUMERACAO)
class Meta:
verbose_name = _('Tipo de Matéria Legislativa')
verbose_name_plural = _('Tipos de Matérias Legislativas')
ordering = ['descricao']
def __str__(self):
return self.descricao
@reversion.register()
class RegimeTramitacao(models.Model):
descricao = models.CharField(max_length=50, verbose_name=_('Descrição'))
class Meta:
verbose_name = _('Regime de Tramitação')
verbose_name_plural = _('Regimes de Tramitação')
def __str__(self):
return self.descricao
@reversion.register()
class Origem(models.Model):
sigla = models.CharField(max_length=10, verbose_name=_('Sigla'))
nome = models.CharField(max_length=50, verbose_name=_('Nome'))
class Meta:
verbose_name = _('Origem')
verbose_name_plural = _('Origens')
def __str__(self):
return self.nome
TIPO_APRESENTACAO_CHOICES = Choices(('O', 'oral', _('Oral')),
('E', 'escrita', _('Escrita')))
def materia_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath=instance.ano)
def anexo_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath=instance.materia.ano)
@reversion.register()
class MateriaLegislativa(models.Model):
tipo = models.ForeignKey(
TipoMateriaLegislativa,
on_delete=models.PROTECT,
verbose_name=TipoMateriaLegislativa._meta.verbose_name)
numero = models.PositiveIntegerField(verbose_name=_('Número'))
ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'),
choices=RANGE_ANOS)
numero_protocolo = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Número do Protocolo'))
data_apresentacao = models.DateField(
verbose_name=_('Data de Apresentação'))
tipo_apresentacao = models.CharField(
max_length=1, blank=True,
verbose_name=_('Tipo de Apresentação'),
choices=TIPO_APRESENTACAO_CHOICES)
regime_tramitacao = models.ForeignKey(
RegimeTramitacao,
on_delete=models.PROTECT,
verbose_name=_('Regime Tramitação'))
data_publicacao = models.DateField(
blank=True, null=True, verbose_name=_('Data de Publicação'))
tipo_origem_externa = models.ForeignKey(
TipoMateriaLegislativa,
blank=True,
null=True,
related_name='tipo_origem_externa_set',
on_delete=models.PROTECT,
verbose_name=_('Tipo'))
numero_origem_externa = models.CharField(
max_length=10, blank=True, verbose_name=_('Número'))
ano_origem_externa = models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name=_('Ano'), choices=RANGE_ANOS)
data_origem_externa = models.DateField(
blank=True, null=True, verbose_name=_('Data'))
local_origem_externa = models.ForeignKey(
Origem, blank=True, null=True,
on_delete=models.PROTECT, verbose_name=_('Local de Origem'))
apelido = models.CharField(
max_length=50, blank=True, verbose_name=_('Apelido'))
dias_prazo = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Dias Prazo'))
data_fim_prazo = models.DateField(
blank=True, null=True, verbose_name=_('Data Fim Prazo'))
em_tramitacao = models.BooleanField(
verbose_name=_('Em Tramitação?'),
default=False,
choices=YES_NO_CHOICES)
polemica = models.NullBooleanField(
blank=True, verbose_name=_('Matéria Polêmica?'))
objeto = models.CharField(
max_length=150, blank=True, verbose_name=_('Objeto'))
complementar = models.NullBooleanField(
blank=True, verbose_name=_('É Complementar?'))
ementa = models.TextField(verbose_name=_('Ementa'))
indexacao = models.TextField(
blank=True, verbose_name=_('Indexação'))
observacao = models.TextField(
blank=True, verbose_name=_('Observação'))
resultado = models.TextField(blank=True)
# XXX novo
anexadas = models.ManyToManyField(
'self',
blank=True,
through='Anexada',
symmetrical=False,
related_name='anexo_de',
through_fields=(
'materia_principal',
'materia_anexada'))
texto_original = models.FileField(
blank=True,
null=True,
upload_to=materia_upload_path,
verbose_name=_('Texto Original'),
validators=[restringe_tipos_de_arquivo_txt])
texto_articulado = GenericRelation(
TextoArticulado, related_query_name='texto_articulado')
proposicao = GenericRelation(
'Proposicao', related_query_name='proposicao')
autores = models.ManyToManyField(
Autor,
through='Autoria',
through_fields=('materia', 'autor'),
symmetrical=False,)
data_ultima_atualizacao = models.DateTimeField(
blank=True, null=True,
auto_now=True,
verbose_name=_('Data'))
class Meta:
verbose_name = _('Matéria Legislativa')
verbose_name_plural = _('Matérias Legislativas')
unique_together = (("tipo", "numero", "ano"),)
permissions = (("can_access_impressos", "Can access impressos"),)
def __str__(self):
return _('%(tipo)s%(numero)s de %(ano)s') % {
'tipo': self.tipo, 'numero': self.numero, 'ano': self.ano}
@property
def epigrafe(self):
return _('%(tipo)s%(numero)s de %(data)s') % {
'tipo': self.tipo,
'numero': self.numero,
'data': defaultfilters.date(
self.data_apresentacao,
"d \d\e F \d\e Y"
)}
def data_entrada_protocolo(self):
'''
hack: recuperar a data de entrada do protocolo sem gerar
dependência circular
'''
from sapl.protocoloadm.models import Protocolo
if self.ano and self.numero_protocolo:
protocolo = Protocolo.objects.filter(
ano=self.ano,
numero=self.numero_protocolo).first()
if protocolo:
if protocolo.timestamp:
return protocolo.timestamp.date()
elif protocolo.timestamp_data_hora_manual:
return protocolo.timestamp_data_hora_manual.date()
elif protocolo.data:
return protocolo.data
return ''
def delete(self, using=None, keep_parents=False):
if self.texto_original:
self.texto_original.delete()
for p in self.proposicao.all():
p.conteudo_gerado_related = None
p.cancelado = True
p.save()
return models.Model.delete(
self, using=using, keep_parents=keep_parents)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if not self.pk and self.texto_original:
texto_original = self.texto_original
self.texto_original = None
models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)
self.texto_original = texto_original
return models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)
@reversion.register()
class Autoria(models.Model):
autor = models.ForeignKey(Autor,
verbose_name=_('Autor'),
on_delete=models.PROTECT)
materia = models.ForeignKey(
MateriaLegislativa, on_delete=models.CASCADE,
verbose_name=_('Matéria Legislativa'))
primeiro_autor = models.BooleanField(verbose_name=_('Primeiro Autor'),
choices=YES_NO_CHOICES,
default=False)
class Meta:
verbose_name = _('Autoria')
verbose_name_plural = _('Autorias')
unique_together = (('autor', 'materia'), )
ordering = ('-primeiro_autor', 'autor__nome')
def __str__(self):
return _('Autoria: %(autor)s - %(materia)s') % {
'autor': self.autor, 'materia': self.materia}
@reversion.register()
class AcompanhamentoMateria(models.Model):
usuario = models.CharField(max_length=50)
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE)
email = models.EmailField(
max_length=100, verbose_name=_('E-mail'))
data_cadastro = models.DateField(auto_now_add=True)
hash = models.CharField(max_length=8)
confirmado = models.BooleanField(default=False)
class Meta:
verbose_name = _('Acompanhamento de Matéria')
verbose_name_plural = _('Acompanhamentos de Matéria')
def __str__(self):
if self.data_cadastro is None:
return _('%(materia)s - %(email)s') % {
'materia': self.materia,
'email': self.email
}
else:
return _('%(materia)s - %(email)s - Registrado em: %(data)s') % {
'materia': self.materia,
'email': self.email,
'data': str(self.data_cadastro.strftime('%d/%m/%Y'))
}
@reversion.register()
class Anexada(models.Model):
materia_principal = models.ForeignKey(
MateriaLegislativa, related_name='materia_principal_set',
on_delete=models.CASCADE,
verbose_name=_('Matéria Principal'))
materia_anexada = models.ForeignKey(
MateriaLegislativa, related_name='materia_anexada_set',
on_delete=models.CASCADE,
verbose_name=_('Matéria Anexada'))
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 = _('Anexada')
verbose_name_plural = _('Anexadas')
def __str__(self):
return _('Principal: %(materia_principal)s'
' - Anexada: %(materia_anexada)s') % {
'materia_principal': self.materia_principal,
'materia_anexada': self.materia_anexada}
@reversion.register()
class AssuntoMateria(models.Model):
assunto = models.CharField(
max_length=50,
verbose_name=_('Assunto'))
dispositivo = models.CharField(
max_length=200,
blank=True,
verbose_name=_('Descrição do Dispositivo Legal'))
class Meta:
verbose_name = _('Assunto de Matéria')
verbose_name_plural = _('Assuntos de Matéria')
def __str__(self):
return self.assunto
@reversion.register()
class DespachoInicial(models.Model):
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE)
comissao = models.ForeignKey(Comissao, on_delete=models.CASCADE)
class Meta:
verbose_name = _('Despacho Inicial')
verbose_name_plural = _('Despachos Iniciais')
def __str__(self):
return _('%(materia)s - %(comissao)s') % {
'materia': self.materia,
'comissao': self.comissao}
@reversion.register()
class TipoDocumento(models.Model):
descricao = models.CharField(
max_length=50, verbose_name=_('Tipo Documento'))
tipo_proposicao = SaplGenericRelation(
TipoProposicao,
related_query_name='tipodocumento_set',
fields_search=(
('descricao', '__icontains'),
))
class Meta:
verbose_name = _('Tipo de Documento')
verbose_name_plural = _('Tipos de Documento')
ordering = ['descricao']
def __str__(self):
return self.descricao
@reversion.register()
class DocumentoAcessorio(models.Model):
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE)
tipo = models.ForeignKey(TipoDocumento,
on_delete=models.PROTECT,
verbose_name=_('Tipo'))
nome = models.CharField(max_length=50, verbose_name=_('Nome'))
data = models.DateField(blank=True, null=True,
default=None, verbose_name=_('Data'))
autor = models.CharField(
max_length=50, blank=True, verbose_name=_('Autor'))
ementa = models.TextField(blank=True, verbose_name=_('Ementa'))
indexacao = models.TextField(blank=True)
arquivo = models.FileField(
blank=True,
null=True,
upload_to=anexo_upload_path,
verbose_name=_('Texto Integral'),
validators=[restringe_tipos_de_arquivo_txt])
proposicao = GenericRelation(
'Proposicao', related_query_name='proposicao')
data_ultima_atualizacao = models.DateTimeField(
blank=True, null=True,
auto_now=True,
verbose_name=_('Data'))
class Meta:
verbose_name = _('Documento Acessório')
verbose_name_plural = _('Documentos Acessórios')
def __str__(self):
return _('%(tipo)s - %(nome)s de %(data)s por %(autor)s') % {
'tipo': self.tipo,
'nome': self.nome,
'data': self.data,
'autor': self.autor}
def delete(self, using=None, keep_parents=False):
if self.arquivo:
self.arquivo.delete()
for p in self.proposicao.all():
p.conteudo_gerado_related = None
p.save()
return models.Model.delete(
self, using=using, keep_parents=keep_parents)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if not self.pk and self.arquivo:
arquivo = self.arquivo
self.arquivo = None
models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)
self.arquivo = arquivo
return models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)
@reversion.register()
class MateriaAssunto(models.Model):
# TODO M2M ??
assunto = models.ForeignKey(
AssuntoMateria,
on_delete=models.CASCADE,
verbose_name=_('Assunto'))
materia = models.ForeignKey(
MateriaLegislativa,
on_delete=models.CASCADE,
verbose_name=_('Matéria'))
class Meta:
verbose_name = _('Relação Matéria - Assunto')
verbose_name_plural = _('Relações Matéria - Assunto')
def __str__(self):
return _('%(materia)s - %(assunto)s') % {
'materia': self.materia, 'assunto': self.assunto}
@reversion.register()
class Numeracao(models.Model):
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE)
tipo_materia = models.ForeignKey(
TipoMateriaLegislativa,
on_delete=models.PROTECT,
verbose_name=_('Tipo de Matéria'))
numero_materia = models.CharField(max_length=5,
verbose_name=_('Número'))
ano_materia = models.PositiveSmallIntegerField(verbose_name=_('Ano'),
choices=RANGE_ANOS)
data_materia = models.DateField(verbose_name=_('Data'), null=True)
class Meta:
verbose_name = _('Numeração')
verbose_name_plural = _('Numerações')
ordering = ('materia',
'tipo_materia',
'numero_materia',
'ano_materia',
'data_materia',)
def __str__(self):
return _('%(numero)s/%(ano)s') % {
'numero': self.numero_materia,
'ano': self.ano_materia}
@reversion.register()
class Orgao(models.Model):
nome = models.CharField(max_length=60, verbose_name=_('Nome'))
sigla = models.CharField(max_length=10, verbose_name=_('Sigla'))
unidade_deliberativa = models.BooleanField(
choices=YES_NO_CHOICES,
verbose_name=(_('Unidade Deliberativa')),
default=False)
endereco = models.CharField(
max_length=100, blank=True, verbose_name=_('Endereço'))
telefone = models.CharField(
max_length=50, blank=True, verbose_name=_('Telefone'))
autor = SaplGenericRelation(Autor,
related_query_name='orgao_set',
fields_search=(
('nome', '__icontains'),
('sigla', '__icontains')
))
class Meta:
verbose_name = _('Órgão')
verbose_name_plural = _('Órgãos')
ordering = ['nome']
def __str__(self):
return _(
'%(nome)s - %(sigla)s') % {'nome': self.nome, 'sigla': self.sigla}
@reversion.register()
class TipoFimRelatoria(models.Model):
descricao = models.CharField(
max_length=50, verbose_name=_('Tipo Fim Relatoria'))
class Meta:
verbose_name = _('Tipo Fim de Relatoria')
verbose_name_plural = _('Tipos Fim de Relatoria')
def __str__(self):
return self.descricao
@reversion.register()
class Relatoria(models.Model):
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE)
parlamentar = models.ForeignKey(Parlamentar,
on_delete=models.CASCADE,
verbose_name=_('Parlamentar'))
tipo_fim_relatoria = models.ForeignKey(
TipoFimRelatoria,
blank=True,
null=True,
on_delete=models.PROTECT,
verbose_name=_('Motivo Fim Relatoria'))
comissao = models.ForeignKey(
Comissao, blank=True, null=True,
on_delete=models.CASCADE, verbose_name=_('Comissão'))
data_designacao_relator = models.DateField(
verbose_name=_('Data Designação'))
data_destituicao_relator = models.DateField(
blank=True, null=True, verbose_name=_('Data Destituição'))
class Meta:
verbose_name = _('Relatoria')
verbose_name_plural = _('Relatorias')
def __str__(self):
return _('%(materia)s - %(tipo)s - %(data)s') % {
'materia': self.materia,
'tipo': self.tipo_fim_relatoria,
'data': self.data_designacao_relator}
@reversion.register()
class Parecer(models.Model):
relatoria = models.ForeignKey(Relatoria, on_delete=models.CASCADE)
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE)
tipo_conclusao = models.CharField(max_length=3, blank=True)
tipo_apresentacao = models.CharField(
max_length=1, choices=TIPO_APRESENTACAO_CHOICES)
parecer = models.TextField(blank=True)
class Meta:
verbose_name = _('Parecer')
verbose_name_plural = _('Pareceres')
def __str__(self):
return _('%(relatoria)s - %(tipo)s') % {
'relatoria': self.relatoria, 'tipo': self.tipo_apresentacao
}
@reversion.register()
class Proposicao(models.Model):
autor = models.ForeignKey(Autor,
null=True,
blank=True,
on_delete=models.PROTECT)
tipo = models.ForeignKey(TipoProposicao, on_delete=models.PROTECT,
blank=False,
null=True,
verbose_name=_('Tipo'))
# XXX data_envio was not null, but actual data said otherwise!!!
data_envio = models.DateTimeField(
blank=False, null=True, verbose_name=_('Data de Envio'))
data_recebimento = models.DateTimeField(
blank=True, null=True, verbose_name=_('Data de Recebimento'))
data_devolucao = models.DateTimeField(
blank=True, null=True, verbose_name=_('Data de Devolução'))
descricao = models.TextField(verbose_name=_('Descrição'))
justificativa_devolucao = models.CharField(
max_length=200,
blank=True,
verbose_name=_('Justificativa da Devolução'))
ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'),
default=None, blank=True, null=True,
choices=RANGE_ANOS)
numero_proposicao = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Número'))
hash_code = models.CharField(verbose_name=_('Código do Documento'),
max_length=200,
blank=True)
"""
FIXME Campo não é necessário na modelagem e implementação atual para o
módulo de proposições.
E - Enviada é tratado pela condição do campo data_envio - se None n enviado
se possui uma data, enviada
R - Recebida é uma condição do campo data_recebimento - se None não receb.
se possui uma data, enviada, recebida e incorporada
I - A incorporação é automática ao ser recebida
e ainda possui a condição de Devolvida onde o campo data_devolucao é
direfente de None, fornecedo a informação para o usuário da data que o
responsável devolveu bem como a justificativa da devolução.
Essa informação fica disponível para o Autor até que ele envie novamente
sua proposição ou resolva excluir.
"""
# ind_enviado and ind_devolvido collapsed as char field (status)
status = models.CharField(blank=True,
max_length=1,
choices=(('E', 'Enviada'),
('R', 'Recebida'),
('I', 'Incorporada')),
verbose_name=_('Status Proposição'))
texto_original = models.FileField(
upload_to=materia_upload_path,
blank=True,
null=True,
verbose_name=_('Texto Original'),
validators=[restringe_tipos_de_arquivo_txt])
texto_articulado = GenericRelation(
TextoArticulado, related_query_name='texto_articulado')
materia_de_vinculo = models.ForeignKey(
MateriaLegislativa, blank=True, null=True,
on_delete=models.CASCADE,
verbose_name=_('Matéria anexadora'),
related_name=_('proposicao_set'))
content_type = models.ForeignKey(
ContentType, default=None, blank=True, null=True,
verbose_name=_('Tipo de Material Gerado'))
object_id = models.PositiveIntegerField(
blank=True, null=True, default=None)
conteudo_gerado_related = SaplGenericForeignKey(
'content_type', 'object_id', verbose_name=_('Conteúdo Gerado'))
observacao = models.TextField(
blank=True, verbose_name=_('Observação'))
cancelado = models.BooleanField(verbose_name=_('Cancelada ?'),
choices=YES_NO_CHOICES,
default=False)
"""# Ao ser recebida, irá gerar uma nova matéria ou um documento acessorio
# de uma já existente
materia_gerada = models.ForeignKey(
MateriaLegislativa, blank=True, null=True,
related_name=_('materia_gerada'))
documento_gerado = models.ForeignKey(
DocumentoAcessorio, blank=True, null=True)"""
@property
def perfis(self):
return self.tipo.perfis.all()
@property
def title_type(self):
return '%s nº _____ %s' % (
self.tipo, formats.date_format(
self.data_envio if self.data_envio else timezone.now(),
"\d\e d \d\e F \d\e Y"))
class Meta:
ordering = ['-data_recebimento']
verbose_name = _('Proposição')
verbose_name_plural = _('Proposições')
unique_together = (('content_type', 'object_id'), )
permissions = (
('detail_proposicao_enviada',
_('Pode acessar detalhes de uma proposição enviada.')),
('detail_proposicao_devolvida',
_('Pode acessar detalhes de uma proposição devolvida.')),
('detail_proposicao_incorporada',
_('Pode acessar detalhes de uma proposição incorporada.')),
)
def __str__(self):
if self.ano and self.numero_proposicao:
return '%s %s/%s' % (Proposicao._meta.verbose_name,
self.numero_proposicao,
self.ano)
else:
if len(self.descricao) < 30:
descricao = self.descricao[:28] + ' ...'
else:
descricao = self.descricao
return '%s %s/%s' % (Proposicao._meta.verbose_name,
self.id,
descricao)
@property
def epigrafe(self):
return _('%(tipo)s%(numero)s de %(data)s') % {
'tipo': self.tipo,
'numero': self.numero_proposicao,
'data': defaultfilters.date(
self.data_envio if self.data_envio else timezone.now(),
"d \d\e F \d\e Y"
)}
def delete(self, using=None, keep_parents=False):
if self.texto_original:
self.texto_original.delete()
return models.Model.delete(
self, using=using, keep_parents=keep_parents)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if not self.pk and self.texto_original:
texto_original = self.texto_original
self.texto_original = None
models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)
self.texto_original = texto_original
return models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)
@reversion.register()
class StatusTramitacao(models.Model):
INDICADOR_CHOICES = Choices(('F', 'fim', _('Fim')),
('R', 'retorno', _('Retorno')))
sigla = models.CharField(max_length=10, verbose_name=_('Sigla'))
descricao = models.CharField(max_length=60, verbose_name=_('Descrição'))
indicador = models.CharField(
blank=True,
max_length=1, verbose_name=_('Indicador da Tramitação'),
choices=INDICADOR_CHOICES)
class Meta:
verbose_name = _('Status de Tramitação')
verbose_name_plural = _('Status de Tramitação')
ordering = ['descricao']
def __str__(self):
return _('%(descricao)s') % {
'descricao': self.descricao}
class UnidadeTramitacaoManager(models.Manager):
"""
Esta classe permite ordenar alfabeticamente a unidade de tramitacao
através da concatenação de 3 fields
"""
def get_queryset(self):
return super(UnidadeTramitacaoManager, self).get_queryset().annotate(
nome_composto=Concat('orgao__nome',
'comissao__sigla',
'parlamentar__nome_parlamentar')
).order_by('nome_composto')
@reversion.register()
class UnidadeTramitacao(models.Model):
comissao = models.ForeignKey(
Comissao, blank=True, null=True,
on_delete=models.PROTECT, verbose_name=_('Comissão'))
orgao = models.ForeignKey(
Orgao, blank=True, null=True,
on_delete=models.PROTECT, verbose_name=_('Órgão'))
parlamentar = models.ForeignKey(
Parlamentar, blank=True, null=True,
on_delete=models.PROTECT, verbose_name=_('Parlamentar'))
objects = UnidadeTramitacaoManager()
class Meta:
verbose_name = _('Unidade de Tramitação')
verbose_name_plural = _('Unidades de Tramitação')
def __str__(self):
if self.orgao and self.comissao and self.parlamentar:
return _('%(comissao)s - %(orgao)s - %(parlamentar)s') % {
'comissao': self.comissao, 'orgao': self.orgao,
'parlamentar': self.parlamentar
}
elif self.orgao and self.comissao and not self.parlamentar:
return _('%(comissao)s - %(orgao)s') % {
'comissao': self.comissao, 'orgao': self.orgao
}
elif self.orgao and not self.comissao and self.parlamentar:
return _('%(orgao)s - %(parlamentar)s') % {
'orgao': self.orgao, 'parlamentar': self.parlamentar
}
elif not self.orgao and self.comissao and self.parlamentar:
return _('%(comissao)s - %(parlamentar)s') % {
'comissao': self.comissao, 'parlamentar': self.parlamentar
}
elif not self.orgao and self.comissao and not self.parlamentar:
return _('%(comissao)s') % {'comissao': self.comissao}
elif self.orgao and not self.comissao and not self.parlamentar:
return _('%(orgao)s') % {'orgao': self.orgao}
else:
return _('%(parlamentar)s') % {'parlamentar': self.parlamentar}
@reversion.register()
class Tramitacao(models.Model):
TURNO_CHOICES = Choices(
('P', 'primeiro', _('Primeiro')),
('S', 'segundo', _('Segundo')),
('U', 'unico', _('Único')),
('L', 'suplementar', _('Suplementar')),
('F', 'final', _('Final')),
('A', 'votacao_unica', _('Votação única em Regime de Urgência')),
('B', 'primeira_votacao', _('1ª Votação')),
('C', 'segunda_terceira_votacao', _('2ª e 3ª Votação')),
('D', 'deliberacao', _('Deliberação')),
('E', 'primeira_segunda_votacao_urgencia', _('1ª e 2ª votações em regime de urgência'))
)
status = models.ForeignKey(StatusTramitacao, on_delete=models.PROTECT,
# TODO PÓS MIGRACAO INICIAL (vide #1381)
# não nulo quando todas as
# bases tiverem sido corrigidas
null=True,
verbose_name=_('Status'))
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE)
# TODO: Remover os campos de data
# TODO: pois timestamp supre a necessidade
timestamp = models.DateTimeField(default=timezone.now)
data_tramitacao = models.DateField(verbose_name=_('Data Tramitação'))
unidade_tramitacao_local = models.ForeignKey(
UnidadeTramitacao,
related_name='tramitacoes_origem',
on_delete=models.PROTECT,
verbose_name=_('Unidade Local'))
data_encaminhamento = models.DateField(
blank=True, null=True, verbose_name=_('Data Encaminhamento'))
unidade_tramitacao_destino = models.ForeignKey(
UnidadeTramitacao,
# TODO PÓS MIGRACAO INICIAL (vide #1381)
# não nulo quando todas as
# bases tiverem sido corrigidas
null=True,
related_name='tramitacoes_destino',
on_delete=models.PROTECT,
verbose_name=_('Unidade Destino'))
urgente = models.BooleanField(verbose_name=_('Urgente ?'),
choices=YES_NO_CHOICES,
default=False)
turno = models.CharField(
max_length=1, blank=True, verbose_name=_('Turno'),
choices=TURNO_CHOICES)
texto = models.TextField(verbose_name=_('Texto da Ação'))
data_fim_prazo = models.DateField(
blank=True, null=True, verbose_name=_('Data Fim Prazo'))
class Meta:
verbose_name = _('Tramitação')
verbose_name_plural = _('Tramitações')
def __str__(self):
return _('%(materia)s | %(status)s | %(data)s') % {
'materia': self.materia,
'status': self.status,
'data': self.data_tramitacao.strftime("%d/%m/%Y")}