class TipoProposicao(models.Model): descricao = models.CharField(max_length=50, verbose_name=_('Descrição')) # FIXME - para a rotina de migração - estes campos mudaram # retire o comentário quando resolver content_type = models.ForeignKey(ContentType, default=None, verbose_name=_('Definição de Tipo')) object_id = models.PositiveIntegerField(blank=True, null=True, default=None) tipo_conteudo_related = SaplGenericForeignKey( 'content_type', 'object_id', verbose_name=_('Seleção de Tipo')) 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') unique_together = (('content_type', 'object_id'), ) def __str__(self): return self.descricao
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.') }) exige_assinatura_digital = models.BooleanField( default=True, verbose_name=_('Exigir Assinatura Digital'), ) 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')) tipo_autores = models.ManyToManyField(TipoAutor, blank=True, verbose_name=_('Tipos de Autores'), help_text=_(""" Tipo de Autores que pode enviar este tipo de Proposição. """)) 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
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=_('Definição de Tipo')) object_id = models.PositiveIntegerField(blank=True, null=True, default=None) tipo_conteudo_related = SaplGenericForeignKey( 'content_type', 'object_id', verbose_name=_('Seleção de Tipo')) 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
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')) """ 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') # FIXME - para a rotina de migração - este campo mudou # retire o comentário quando resolver materia_de_vinculo = models.ForeignKey(MateriaLegislativa, blank=True, null=True, on_delete=models.CASCADE, verbose_name=_('Matéria anexadora'), related_name=_('proposicao_set')) # FIXME - para a rotina de migração - estes campos mudaram # retire o comentário quando resolver 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')) """# 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: 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) 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)
class Protocolo(models.Model): numero = models.PositiveIntegerField( blank=False, null=False, verbose_name=_('Número de Protocolo')) ano = models.PositiveSmallIntegerField(blank=False, null=False, choices=RANGE_ANOS, verbose_name=_('Ano do Protocolo')) data = models.DateField(null=True, blank=True, verbose_name=_('Data do Protocolo'), help_text=_('Informado manualmente')) hora = models.TimeField(null=True, blank=True, verbose_name=_('Hora do Protocolo'), help_text=_('Informado manualmente')) timestamp_data_hora_manual = models.DateTimeField(default=timezone.now) user_data_hora_manual = models.CharField( max_length=256, blank=True, verbose_name=_('IP'), help_text=_('Usuário que está realizando Protocolo e informando ' 'data e hora manualmente.')) ip_data_hora_manual = models.CharField( max_length=256, blank=True, verbose_name=_('IP'), help_text=_('Endereço IP da estação de trabalho ' 'do usuário que está realizando Protocolo e informando ' 'data e hora manualmente.')) # Não foi utilizado auto_now_add=True em timestamp porque # ele usa datetime.now que não é timezone aware. timestamp = models.DateTimeField( default=timezone.now, null=True, blank=True) tipo_protocolo = models.PositiveIntegerField( blank=True, null=True, verbose_name=_('Tipo de Protocolo')) tipo_processo = models.PositiveIntegerField() interessado = models.CharField( max_length=200, blank=True, verbose_name=_('Interessado')) tipo_processo = models.PositiveIntegerField() email = models.EmailField( blank=True, verbose_name=_('Email do Interessado')) comprovante_automatico_enviado = models.BooleanField( default=False, verbose_name=_('Comprovante Automático Enviado')) autor = models.ForeignKey(Autor, blank=True, null=True, on_delete=models.PROTECT) assunto_ementa = models.TextField(blank=True) tipo_documento = models.ForeignKey( TipoDocumentoAdministrativo, blank=True, null=True, on_delete=models.PROTECT, verbose_name=_('Tipo de Documento')) tipo_materia = models.ForeignKey( TipoMateriaLegislativa, blank=True, null=True, on_delete=models.PROTECT, verbose_name=_('Tipo de Matéria')) tipo_content_type = models.ForeignKey( ContentType, default=None, blank=True, null=True, verbose_name=_('Tipo de Material Gerado'), related_name='tipo_content_type_set', on_delete=PROTECT) tipo_object_id = models.PositiveIntegerField( blank=True, null=True, default=None) tipo_conteudo_protocolado = SaplGenericForeignKey( 'tipo_content_type', 'tipo_object_id', verbose_name=_('Tipo do Conteúdo Protocolado')) conteudo_content_type = models.ForeignKey( ContentType, default=None, blank=True, null=True, verbose_name=_('Tipo de Material Gerado'), related_name='conteudo_content_type_set', on_delete=PROTECT) conteudo_object_id = models.PositiveIntegerField( blank=True, null=True, default=None) conteudo_protocolado = SaplGenericForeignKey( 'conteudo_content_type', 'conteudo_object_id', verbose_name=_('Conteúdo Protocolado')) numero_paginas = models.PositiveIntegerField( blank=True, null=True, verbose_name=_('Número de Páginas')) observacao = models.TextField( blank=True, verbose_name=_('Observação')) anulado = models.BooleanField(default=False) user_anulacao = models.CharField(max_length=1000, blank=True) ip_anulacao = models.CharField(max_length=15, blank=True) justificativa_anulacao = models.CharField( max_length=260, blank=True, verbose_name=_('Motivo')) timestamp_anulacao = models.DateTimeField(blank=True, null=True) class Meta: verbose_name = _('Protocolo') verbose_name_plural = _('Protocolos') permissions = ( ('action_anular_protocolo', _('Permissão para Anular Protocolo')), ('action_homologar_protocolo', _('Permissão para Homologar Protocolo')), ) def __str__(self): return _('%(numero)s/%(ano)s') % { 'numero': self.numero, 'ano': self.ano } @property def epigrafe(self): return '{}/{} - {}'.format( self.numero, self.ano, formats.date_format( timezone.localtime(self.timestamp), "DATETIME_FORMAT" ) if self.timestamp else '{} - {}'.format( formats.date_format(self.data, "DATE_FORMAT"), formats.date_format(self.hora, 'H:i') ) ) def materia_vinculada(self): try: materia = MateriaLegislativa.objects.get( tipo=self.tipo_materia, ano=self.ano, numero_protocolo=self.numero ) except: return None return materia
class Proposicao(CommonMixin): FIELDFILE_NAME = ('texto_original', ) metadata = JSONField(verbose_name=_('Metadados'), blank=True, null=True, default=None, encoder=DjangoJSONEncoder) 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')) numero_materia_futuro = models.PositiveIntegerField( blank=True, null=True, verbose_name=_('Número Matéria')) 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 = PortalFileField( upload_to=materia_upload_path, blank=True, null=True, verbose_name=_('Texto Original'), storage=OverwriteStorage(), validators=[restringe_tipos_de_arquivo_txt], max_length=512) 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')) proposicao_vinculada = models.ForeignKey( 'self', blank=True, null=True, on_delete=models.CASCADE, verbose_name=_('Proposição Vinculada'), related_name=_('proposicao_vinculada_set')) content_type = models.ForeignKey(ContentType, default=None, blank=True, null=True, verbose_name=_('Tipo de Material Gerado'), on_delete=PROTECT) 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)""" user = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('Usuário'), on_delete=models.PROTECT, null=True, blank=True) ip = models.CharField(verbose_name=_('IP'), max_length=30, blank=True, default='') ultima_edicao = models.DateTimeField( verbose_name=_('Data e Hora da Edição'), 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 nº %(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)