Browse Source

Novos campos e aba de anexos em Solicitações de eventos. Gertiq #163934

pull/166/head
Sesóstris Vieira 1 year ago
parent
commit
67dff817c0
  1. 382
      sigi/apps/eventos/admin.py
  2. 104
      sigi/apps/eventos/migrations/0041_solicitacao_data_analise_solicitacao_justificativa_and_more.py
  3. 38
      sigi/apps/eventos/migrations/0042_atualiza_status_solicitacao.py
  4. 81
      sigi/apps/eventos/models.py
  5. 2
      sigi/menu_conf.yaml

382
sigi/apps/eventos/admin.py

@ -5,6 +5,7 @@ from moodle import Moodle
from django.db.models import Q
from django.conf import settings
from django.contrib import admin, messages
from django.core.exceptions import ValidationError
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render, redirect
from django.template import Template, Context
@ -25,6 +26,7 @@ from sigi.apps.eventos.models import (
Modulo,
TipoEvento,
Solicitacao,
AnexoSolicitacao,
ItemSolicitado,
Funcao,
Evento,
@ -42,54 +44,7 @@ from sigi.apps.utils.mixins import (
)
class SolicitacaoStatusFilter(admin.SimpleListFilter):
title = _("status")
parameter_name = "status"
def lookups(self, request, model_admin):
return (
("aberto", _("Aberto")),
("analise", _("Análise")),
("inconcluso", _("Inconcluso (aberto + análise)")),
("concluido", _("Concluído")),
)
def queryset(self, request, queryset):
if self.value() == "aberto":
return queryset.exclude(
itemsolicitado__status__in=[
ItemSolicitado.STATUS_AUTORIZADO,
ItemSolicitado.STATUS_REJEITADO,
]
).distinct()
elif self.value() == "analise":
return (
queryset.filter(
itemsolicitado__status=ItemSolicitado.STATUS_SOLICITADO
)
.filter(
itemsolicitado__status__in=[
ItemSolicitado.STATUS_AUTORIZADO,
ItemSolicitado.STATUS_REJEITADO,
]
)
.distinct()
)
elif self.value() == "inconcluso":
return queryset.exclude(
id__in=Solicitacao.objects.exclude(
itemsolicitado__status=ItemSolicitado.STATUS_SOLICITADO
).only("id")
)
elif self.value() == "concluido":
return queryset.exclude(
itemsolicitado__status=ItemSolicitado.STATUS_SOLICITADO
).distinct()
return queryset
class SolicitacaoResource(LabeledResourse):
status = Field(column_name="status")
oficinas = Field(column_name="oficinas solicitadas")
oficinas_uf = Field(column_name="número de oficinas realizadas na UF")
@ -114,7 +69,7 @@ class SolicitacaoResource(LabeledResourse):
export_order = fields
def dehydrate_status(self, obj):
return obj.get_status()
return obj.get_status_display()
def dehydrate_oficinas(self, obj):
return ", ".join(
@ -234,6 +189,11 @@ class ItemSolicitadoInline(admin.StackedInline):
autocomplete_fields = ("tipo_evento",)
class AnexoSolicitacaoInline(admin.TabularInline):
model = AnexoSolicitacao
readonly_fields = ("data_pub",)
@admin.register(TipoEvento)
class TipoEventoAdmin(admin.ModelAdmin):
list_display = ["nome", "categoria"]
@ -248,7 +208,7 @@ class SolicitacaoAdmin(CartExportMixin, admin.ModelAdmin):
list_display = (
"casa",
"get_sigad_url",
"get_status",
"status",
"senador",
"data_pedido",
"data_recebido_coperi",
@ -266,7 +226,7 @@ class SolicitacaoAdmin(CartExportMixin, admin.ModelAdmin):
"casa__municipio__uf__regiao",
"senador",
"itemsolicitado__tipo_evento",
SolicitacaoStatusFilter,
"status",
)
list_select_related = ["casa", "casa__municipio", "casa__municipio__uf"]
list_display_links = ("casa",)
@ -277,95 +237,129 @@ class SolicitacaoAdmin(CartExportMixin, admin.ModelAdmin):
"senador",
)
date_hierarchy = "data_pedido"
inlines = (ItemSolicitadoInline,)
fieldsets = (
(
None,
{
"fields": [
"casa",
"senador",
"num_processo",
"descricao",
"data_pedido",
"data_recebido_coperi",
]
},
),
(
_("Autorização"),
{
"fields": [
"status",
"servidor",
"data_analise",
"justificativa",
]
},
),
(
_("Contato da Casa"),
{
"fields": [
"contato",
"email_contato",
"telefone_contato",
"whatsapp_contato",
]
},
),
(
_("Participação esperada"),
{"fields": ["estimativa_casas", "estimativa_servidores"]},
),
)
readonly_fields = ("servidor", "data_analise")
inlines = (ItemSolicitadoInline, AnexoSolicitacaoInline)
autocomplete_fields = ("casa",)
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
if hasattr(request.user, "servidor"):
servidor = request.user.servidor
def save_model(self, request, obj, form, change):
if change:
old_obj = Solicitacao.objects.get(id=obj.id)
else:
servidor = None
agora = timezone.localtime()
old_obj = obj
if (
obj.status != Solicitacao.STATUS_SOLICITADO
and obj.status != old_obj.status
):
obj.servidor = (
request.user.servidor
if hasattr(request.user, "servidor")
else None
)
obj.data_analise = timezone.localtime()
return super().save_model(request, obj, form, change)
for item in instances:
if (
item.status == ItemSolicitado.STATUS_SOLICITADO
and item.evento is not None
):
item.evento.status = Evento.STATUS_ACONFIRMAR
self.message_user(
request,
_(
f"Status do evento {item.evento} alterado para "
f"{item.evento.get_status_display()}"
),
messages.INFO,
)
elif item.status == ItemSolicitado.STATUS_AUTORIZADO:
item.servidor = servidor
item.data_analise = agora
if item.evento is None:
item.evento = Evento(
tipo_evento=item.tipo_evento,
nome=_(
f"{item.tipo_evento} em {item.solicitacao.casa}"[
:100
]
),
descricao=_(
f"{item.tipo_evento} em {item.solicitacao.casa}"
),
virtual=item.virtual,
solicitante=item.solicitacao.senador,
num_processo=item.solicitacao.num_processo,
data_pedido=item.solicitacao.data_pedido,
data_recebido_coperi=item.solicitacao.data_recebido_coperi,
data_inicio=item.inicio_desejado,
data_termino=item.inicio_desejado
+ datetime.timedelta(days=item.tipo_evento.duracao),
casa_anfitria=item.solicitacao.casa,
observacao=_(
f"Autorizado por {servidor} com a justificativa '{item.justificativa}"
def save_formset(self, request, form, formset, change):
if formset.model == ItemSolicitado:
obj = form.instance
instances = formset.save(commit=False)
if hasattr(request.user, "servidor"):
servidor = request.user.servidor
else:
servidor = None
agora = timezone.localtime()
for item in instances:
if (
obj.status == Solicitacao.STATUS_SOLICITADO
and item.status != ItemSolicitado.STATUS_SOLICITADO
):
item.status = ItemSolicitado.STATUS_SOLICITADO
self.message_user(
request,
_(
f"O item {item} teve o status mudado para "
"SOLICITADO porque a solicitação ainda não foi "
"autorizada"
),
status=Evento.STATUS_CONFIRMADO,
contato=item.solicitacao.contato,
telefone=item.solicitacao.telefone_contato,
messages.WARNING,
)
if (
obj.status == Solicitacao.STATUS_REJEITADO
and item.status != ItemSolicitado.STATUS_REJEITADO
):
item.status = ItemSolicitado.STATUS_REJEITADO
self.message_user(
request,
_(f"Evento {item.evento} criado automaticamente."),
messages.INFO,
)
else:
item.evento.status = Evento.STATUS_CONFIRMADO
item.evento.observacao += _(
f"\nConfirmado por {servidor} com a justificativa: {item.justificativa}"
_(
f"O item {item} teve o status mudado para "
"REJEITADO porque a solicitação inteira foi "
"rejeitada"
),
messages.WARNING,
)
item.evento.data_cancelamento = None
item.evento.motivo_cancelamento = ""
if (
obj.status == Solicitacao.STATUS_CONCLUIDO
and item.status == ItemSolicitado.STATUS_SOLICITADO
):
item.status = ItemSolicitado.STATUS_REJEITADO
self.message_user(
request,
_(
f"Status do evento {item.evento} alterado para "
f"{item.evento.get_status_display()}"
f"O item {item} teve o status mudado para "
"REJEITADO porque a solicitação foi concluída e "
"ele ainda estava em aberto"
),
messages.INFO,
)
elif item.status == ItemSolicitado.STATUS_REJEITADO:
item.servidor = servidor
item.data_analise = agora
if item.evento is not None:
item.evento.status = Evento.STATUS_CANCELADO
item.evento.observacao += _(
f"\nCancelado por {servidor} com a justificativa: {item.justificativa}"
)
item.evento.data_cancelamento = timezone.localdate()
item.evento.motivo_cancelamento = _(
f"\nCancelado por {servidor} com a justificativa: {item.justificativa}"
messages.WARNING,
)
if (
item.status == ItemSolicitado.STATUS_SOLICITADO
and item.evento is not None
):
item.evento.status = Evento.STATUS_ACONFIRMAR
self.message_user(
request,
_(
@ -374,31 +368,121 @@ class SolicitacaoAdmin(CartExportMixin, admin.ModelAdmin):
),
messages.INFO,
)
if item.evento:
item.evento.tipo_evento = item.tipo_evento
item.evento.nome = _(
f"{item.tipo_evento} em {item.solicitacao.casa}"[:100]
)
item.evento.descricao = _(
f"{item.tipo_evento} em {item.solicitacao.casa}"
)
item.evento.virtual = item.virtual
item.evento.solicitante = item.solicitacao.senador
item.evento.num_processo = item.solicitacao.num_processo
item.evento.data_pedido = item.solicitacao.data_pedido
item.evento.data_recebido_coperi = (
item.solicitacao.data_recebido_coperi
)
item.evento.data_inicio = item.inicio_desejado
item.evento.data_termino = (
item.inicio_desejado
+ datetime.timedelta(days=item.tipo_evento.duracao)
elif item.status == ItemSolicitado.STATUS_AUTORIZADO:
item.servidor = servidor
item.data_analise = agora
if item.evento is None:
item.evento = Evento(
tipo_evento=item.tipo_evento,
nome=_(
f"{item.tipo_evento} em {item.solicitacao.casa}"[
:100
]
),
descricao=_(
f"{item.tipo_evento} em {item.solicitacao.casa}"
),
virtual=item.virtual,
solicitante=item.solicitacao.senador,
num_processo=item.solicitacao.num_processo,
data_pedido=item.solicitacao.data_pedido,
data_recebido_coperi=item.solicitacao.data_recebido_coperi,
data_inicio=item.inicio_desejado,
data_termino=item.inicio_desejado
+ datetime.timedelta(
days=item.tipo_evento.duracao
),
casa_anfitria=item.solicitacao.casa,
observacao=_(
f"Autorizado por {servidor} com a justificativa '{item.justificativa}"
),
status=Evento.STATUS_CONFIRMADO,
contato=item.solicitacao.contato,
telefone=item.solicitacao.telefone_contato,
)
self.message_user(
request,
_(f"Evento {item.evento} criado automaticamente."),
messages.INFO,
)
else:
item.evento.status = Evento.STATUS_CONFIRMADO
item.evento.observacao += _(
f"\nConfirmado por {servidor} com a justificativa: {item.justificativa}"
)
item.evento.data_cancelamento = None
item.evento.motivo_cancelamento = ""
self.message_user(
request,
_(
f"Status do evento {item.evento} alterado para "
f"{item.evento.get_status_display()}"
),
messages.INFO,
)
elif item.status == ItemSolicitado.STATUS_REJEITADO:
item.servidor = servidor
item.data_analise = agora
if item.evento is not None:
item.evento.status = Evento.STATUS_CANCELADO
item.evento.observacao += _(
f"\nCancelado por {servidor} com a justificativa: {item.justificativa}"
)
item.evento.data_cancelamento = timezone.localdate()
item.evento.motivo_cancelamento = _(
f"\nCancelado por {servidor} com a justificativa: {item.justificativa}"
)
self.message_user(
request,
_(
f"Status do evento {item.evento} alterado para "
f"{item.evento.get_status_display()}"
),
messages.INFO,
)
if item.evento:
item.evento.tipo_evento = item.tipo_evento
item.evento.nome = _(
f"{item.tipo_evento} em {item.solicitacao.casa}"[:100]
)
item.evento.descricao = _(
f"{item.tipo_evento} em {item.solicitacao.casa}"
)
item.evento.virtual = item.virtual
item.evento.solicitante = item.solicitacao.senador
item.evento.num_processo = item.solicitacao.num_processo
item.evento.data_pedido = item.solicitacao.data_pedido
item.evento.data_recebido_coperi = (
item.solicitacao.data_recebido_coperi
)
item.evento.data_inicio = item.inicio_desejado
item.evento.data_termino = (
item.inicio_desejado
+ datetime.timedelta(days=item.tipo_evento.duracao)
)
item.evento.casa_anfitria = item.solicitacao.casa
item.evento.contato = item.solicitacao.contato
item.evento.telefone = item.solicitacao.telefone_contato
item.evento.save()
item.save()
if (
obj.status == Solicitacao.STATUS_AUTORIZADO
and not obj.itemsolicitado_set.filter(
status=ItemSolicitado.STATUS_SOLICITADO
).exists()
):
obj.status = Solicitacao.STATUS_CONCLUIDO
obj.save()
self.message_user(
request,
_(
"Status da solicitação alterado automaticamente para "
"Concluído pois não há mais itens a serem analisados"
),
messages.INFO,
)
item.evento.casa_anfitria = item.solicitacao.casa
item.evento.contato = item.solicitacao.contato
item.evento.telefone = item.solicitacao.telefone_contato
item.evento.save()
item.save()
return super().save_formset(request, form, formset, change)
@admin.display(description=_("Oficinas solicitadas"))

104
sigi/apps/eventos/migrations/0041_solicitacao_data_analise_solicitacao_justificativa_and_more.py

@ -0,0 +1,104 @@
# Generated by Django 4.2.4 on 2023-09-13 22:15
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
("servidores", "0013_servidor_moodle_userid"),
("eventos", "0040_alter_itemsolicitado_data_analise_and_more"),
]
operations = [
migrations.AddField(
model_name="solicitacao",
name="data_analise",
field=models.DateTimeField(
blank=True,
editable=False,
null=True,
verbose_name="data de autorização/rejeição",
),
),
migrations.AddField(
model_name="solicitacao",
name="justificativa",
field=models.TextField(blank=True, verbose_name="Justificativa"),
),
migrations.AddField(
model_name="solicitacao",
name="servidor",
field=models.ForeignKey(
blank=True,
editable=False,
help_text="Servidor que autorizou ou rejeitou a realização do evento",
limit_choices_to={"externo": False},
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="servidores.servidor",
verbose_name="servidor analisador",
),
),
migrations.AddField(
model_name="solicitacao",
name="status",
field=models.CharField(
choices=[
("S", "Solicitado"),
("A", "Autorizado"),
("R", "Rejeitado"),
("C", "Concluído"),
],
default="S",
max_length=1,
verbose_name="Status",
),
),
migrations.CreateModel(
name="AnexoSolicitacao",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"arquivo",
models.FileField(
max_length=500,
upload_to="apps/eventos/solicitacao/anexo/arquivo",
),
),
(
"descricao",
models.CharField(max_length=70, verbose_name="descrição"),
),
(
"data_pub",
models.DateTimeField(
default=django.utils.timezone.localtime,
verbose_name="data da publicação do anexo",
),
),
(
"solicitacao",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="eventos.solicitacao",
verbose_name="evento",
),
),
],
options={
"verbose_name": "Anexo",
"verbose_name_plural": "Anexos",
"ordering": ("-data_pub",),
},
),
]

38
sigi/apps/eventos/migrations/0042_atualiza_status_solicitacao.py

@ -0,0 +1,38 @@
# Generated by Django 4.2.4 on 2023-09-13 22:20
from django.db import migrations
def forwards(apps, schema_editor):
Solicitacao = apps.get_model("eventos", "Solicitacao")
for s in Solicitacao.objects.all():
statuses = list(
s.itemsolicitado_set.values_list("status", flat=True).distinct(
"status"
)
)
if statuses == ["S"]:
s.status = "S"
elif statuses == ["A"]:
s.status = "C"
elif statuses == ["R"]:
s.status = "R"
elif "S" in statuses and ("A" in statuses or "R" in statuses):
s.status = "A"
s.save()
class Migration(migrations.Migration):
dependencies = [
(
"eventos",
"0041_solicitacao_data_analise_solicitacao_justificativa_and_more",
),
]
operations = [
migrations.RunPython(
forwards,
)
]

81
sigi/apps/eventos/models.py

@ -1,3 +1,4 @@
from collections.abc import Iterable
import datetime
import re
from tinymce.models import HTMLField
@ -71,6 +72,16 @@ class TipoEvento(models.Model):
class Solicitacao(models.Model):
STATUS_SOLICITADO = "S"
STATUS_AUTORIZADO = "A"
STATUS_REJEITADO = "R"
STATUS_CONCLUIDO = "C"
STATUS_CHOICES = (
(STATUS_SOLICITADO, _("Solicitado")),
(STATUS_AUTORIZADO, _("Autorizado")),
(STATUS_REJEITADO, _("Rejeitado")),
(STATUS_CONCLUIDO, _("Concluído")),
)
casa = models.ForeignKey(
Orgao, verbose_name=_("casa solicitante"), on_delete=models.PROTECT
)
@ -92,6 +103,33 @@ class Solicitacao(models.Model):
blank=True,
help_text=_("Data em que o pedido chegou na COPERI"),
)
status = models.CharField(
_("Status"),
max_length=1,
choices=STATUS_CHOICES,
default=STATUS_SOLICITADO,
)
servidor = models.ForeignKey(
Servidor,
verbose_name=_("servidor analisador"),
help_text=_(
"Servidor que autorizou ou rejeitou a realização do evento"
),
on_delete=models.PROTECT,
limit_choices_to={"externo": False},
blank=True,
null=True,
editable=False,
)
data_analise = models.DateTimeField(
_("data de autorização/rejeição"),
blank=True,
null=True,
editable=False,
)
justificativa = models.TextField(
verbose_name=_("Justificativa"), blank=True
)
contato = models.CharField(
_("pessoa de contato na Casa"), max_length=100, blank=True
)
@ -121,28 +159,6 @@ class Solicitacao(models.Model):
def __str__(self):
return _(f"{self.num_processo}: {self.casa} / Senador {self.senador}")
@admin.display(description="Status")
def get_status(self):
# TODO: Definir status do pedido com base no status de seus ítens:
# Aberto: Todos os itens estão em estado Solicitado
# Análise: Parte dos pedidos estão Autorizados/Rejeitados e o restante
# está Solicitado
# Concluído: Nenhum pedido está em estado Solicitado
item_status = set(
self.itemsolicitado_set.distinct("status").values_list(
"status", flat=True
)
)
if {ItemSolicitado.STATUS_SOLICITADO} == item_status:
return _("Aberto")
elif ItemSolicitado.STATUS_SOLICITADO in item_status and (
ItemSolicitado.STATUS_AUTORIZADO in item_status
or ItemSolicitado.STATUS_REJEITADO in item_status
):
return _("Análise")
else:
return _("Concluído")
@admin.display(description=_("SIGAD"), ordering="num_processo")
def get_sigad_url(self):
m = re.match(
@ -256,6 +272,27 @@ class ItemSolicitado(models.Model):
return _(f"{self.tipo_evento}: {self.get_status_display()}")
class AnexoSolicitacao(models.Model):
solicitacao = models.ForeignKey(
Solicitacao, on_delete=models.CASCADE, verbose_name=_("evento")
)
arquivo = models.FileField(
upload_to="apps/eventos/solicitacao/anexo/arquivo", max_length=500
)
descricao = models.CharField(_("descrição"), max_length=70)
data_pub = models.DateTimeField(
_("data da publicação do anexo"), default=timezone.localtime
)
class Meta:
ordering = ("-data_pub",)
verbose_name = _("Anexo")
verbose_name_plural = _("Anexos")
def __str__(self):
return _(f"{self.descricao} publicado em {self.data_pub}")
class Evento(models.Model):
STATUS_PLANEJAMENTO = "E"
STATUS_AGUARDANDOSIGAD = "G"

2
sigi/menu_conf.yaml

@ -76,7 +76,7 @@ main_menu:
children:
- title: Solicitações
view_name: admin:eventos_solicitacao_changelist
querystr: status=inconcluso
querystr: status__exact=S
- title: Todos os eventos
view_name: admin:eventos_evento_changelist
- title: Cursos

Loading…
Cancel
Save