diff --git a/sigi/apps/eventos/admin.py b/sigi/apps/eventos/admin.py
index 35cf5ef..10d9e6e 100644
--- a/sigi/apps/eventos/admin.py
+++ b/sigi/apps/eventos/admin.py
@@ -1,6 +1,8 @@
import datetime
import time
+from typing import Any
from moodle import Moodle
+from django.db.models import Q
from django.conf import settings
from django.contrib import admin, messages
from django.http import HttpResponse
@@ -21,6 +23,8 @@ from sigi.apps.eventos.models import (
ModeloDeclaracao,
Modulo,
TipoEvento,
+ Solicitacao,
+ ItemSolicitado,
Funcao,
Evento,
Equipe,
@@ -33,6 +37,52 @@ from sigi.apps.utils.filters import EmptyFilter, DateRangeFilter
from sigi.apps.utils.mixins import CartExportMixin, ValueLabeledResource
+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 EventoResource(ValueLabeledResource):
# categoria_evento = Field(column_name="tipo_evento__categoria")
# status = Field(column_name="status")
@@ -120,6 +170,21 @@ class CronogramaInline(admin.StackedInline):
extra = 0
+class ItemSolicitadoInline(admin.StackedInline):
+ model = ItemSolicitado
+ fields = (
+ "tipo_evento",
+ "virtual",
+ "inicio_desejado",
+ "status",
+ "justificativa",
+ "servidor",
+ "evento",
+ )
+ readonly_fields = ("servidor", "evento")
+ extra = 1
+
+
@admin.register(TipoEvento)
class TipoEventoAdmin(admin.ModelAdmin):
list_display = ["nome", "categoria"]
@@ -128,6 +193,176 @@ class TipoEventoAdmin(admin.ModelAdmin):
inlines = [ChecklistInline]
+@admin.register(Solicitacao)
+class SolicitacaoAdmin(admin.ModelAdmin):
+ list_display = (
+ "num_processo",
+ "casa",
+ "senador",
+ "data_pedido",
+ "get_oficinas",
+ "get_municipio",
+ "get_uf",
+ "get_regiao",
+ "get_populacao",
+ "get_oficinas_uf",
+ "estimativa_casas",
+ "estimativa_servidores",
+ "get_status",
+ )
+ list_filter = (
+ "casa__municipio__uf",
+ "casa__municipio__uf__regiao",
+ "senador",
+ "itemsolicitado__tipo_evento",
+ SolicitacaoStatusFilter,
+ )
+ list_select_related = ["casa", "casa__municipio", "casa__municipio__uf"]
+ search_fields = (
+ "casa__search_text",
+ "casa__municipio__search_text",
+ "casa__municipio__uf__search_text",
+ "senador",
+ )
+ date_hierarchy = "data_pedido"
+ inlines = (ItemSolicitadoInline,)
+ 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
+ else:
+ servidor = None
+
+ 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
+ if item.evento is None:
+ item.evento = Evento(
+ tipo_evento=item.tipo_evento,
+ nome=f"{item.tipo_evento} em {item.solicitacao.casa}",
+ 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_inicio=item.inicio_desejado,
+ data_termino=item.inicio_desejado
+ + datetime.timedelta(days=item.tipo_evento.duracao),
+ casa_anfitria=item.solicitacao.casa,
+ municipio=item.solicitacao.casa.municipio,
+ 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
+ self.message_user(
+ request,
+ _(
+ f"Status do evento {item.evento} alterado para "
+ f"{item.evento.get_status_display()}"
+ ),
+ messages.INFO,
+ )
+ elif ItemSolicitado.STATUS_REJEITADO and item.evento is not None:
+ item.evento.status = Evento.STATUS_CANCELADO
+ item.evento.save()
+ 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}"
+ )
+ 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_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.municipio = item.solicitacao.casa.municipio
+ item.evento.observacao = f"Autorizado por {servidor} com a justificativa '{item.justificativa}"
+ 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"))
+ def get_oficinas(self, obj):
+ return mark_safe(
+ "
- "
+ + "
- ".join(
+ [i.tipo_evento.sigla for i in obj.itemsolicitado_set.all()]
+ )
+ + "
"
+ )
+
+ @admin.display(
+ description=_("Município"), ordering="casa__municipio__nome"
+ )
+ def get_municipio(self, obj):
+ return obj.casa.municipio.nome
+
+ @admin.display(description=_("UF"), ordering="casa__municipio__uf__nome")
+ def get_uf(self, obj):
+ return obj.casa.municipio.uf.nome
+
+ @admin.display(
+ description=_("Região"), ordering="casa__municipio__uf__regiao"
+ )
+ def get_regiao(self, obj):
+ return obj.casa.municipio.uf.get_regiao_display()
+
+ @admin.display(
+ description=_("População"), ordering="casa__municipio__populacao"
+ )
+ def get_populacao(self, obj):
+ return obj.casa.municipio.populacao
+
+ @admin.display(description=_("Oficinas atendidas/confirmadas na UF"))
+ def get_oficinas_uf(self, obj):
+ return Evento.objects.filter(
+ status__in=[Evento.STATUS_CONFIRMADO, Evento.STATUS_REALIZADO],
+ municipio__uf=obj.casa.municipio.uf,
+ ).count()
+
+
@admin.register(Funcao)
class FuncaoAdmin(admin.ModelAdmin):
list_display = (
diff --git a/sigi/apps/eventos/migrations/0037_tipoevento_duracao_tipoevento_sigla_solicitacao_and_more.py b/sigi/apps/eventos/migrations/0037_tipoevento_duracao_tipoevento_sigla_solicitacao_and_more.py
new file mode 100644
index 0000000..dd254b7
--- /dev/null
+++ b/sigi/apps/eventos/migrations/0037_tipoevento_duracao_tipoevento_sigla_solicitacao_and_more.py
@@ -0,0 +1,219 @@
+# Generated by Django 4.2.4 on 2023-08-30 19:21
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("servidores", "0013_servidor_moodle_userid"),
+ ("casas", "0027_alter_orgao_email"),
+ ("eventos", "0036_tipoevento_prefixo_turma_alter_evento_turma"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="tipoevento",
+ name="duracao",
+ field=models.PositiveIntegerField(
+ default=1, verbose_name="Duração (dias)"
+ ),
+ ),
+ migrations.AddField(
+ model_name="tipoevento",
+ name="sigla",
+ field=models.CharField(
+ blank=True, max_length=20, verbose_name="sigla"
+ ),
+ ),
+ migrations.CreateModel(
+ name="Solicitacao",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "senador",
+ models.CharField(
+ max_length=100, verbose_name="senador solicitante"
+ ),
+ ),
+ (
+ "num_processo",
+ models.CharField(
+ blank=True,
+ help_text="Formato:XXXXX.XXXXXX/XXXX-XX",
+ max_length=20,
+ verbose_name="número do processo SIGAD",
+ ),
+ ),
+ (
+ "descricao",
+ models.TextField(verbose_name="descrição da solicitação"),
+ ),
+ (
+ "data_pedido",
+ models.DateField(
+ help_text="Data em que o pedido do Gabinete chegou à COPERI",
+ verbose_name="Data do pedido",
+ ),
+ ),
+ (
+ "contato",
+ models.CharField(
+ max_length=100,
+ verbose_name="pessoa de contato na Casa",
+ ),
+ ),
+ (
+ "email_contato",
+ models.EmailField(
+ blank=True,
+ max_length=254,
+ verbose_name="e-mail do contato",
+ ),
+ ),
+ (
+ "telefone_contato",
+ models.CharField(
+ blank=True,
+ max_length=20,
+ verbose_name="telefone do contato",
+ ),
+ ),
+ (
+ "whatsapp_contato",
+ models.CharField(
+ blank=True,
+ max_length=20,
+ verbose_name="whatsapp do contato",
+ ),
+ ),
+ (
+ "estimativa_casas",
+ models.PositiveIntegerField(
+ help_text="estimativa de quantas Casas participarão dos eventos",
+ verbose_name="estimativa de Casas participantes",
+ ),
+ ),
+ (
+ "estimativa_servidores",
+ models.PositiveIntegerField(
+ help_text="estimativa de quantos Servidores participarão dos eventos",
+ verbose_name="estimativa de servidores participantes",
+ ),
+ ),
+ (
+ "casa",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.PROTECT,
+ to="casas.orgao",
+ verbose_name="casa solicitante",
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "Solicitação de eventos",
+ "verbose_name_plural": "Solicitações de eventos",
+ "ordering": ("-data_pedido",),
+ },
+ ),
+ migrations.CreateModel(
+ name="ItemSolicitado",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "virtual",
+ models.BooleanField(default=False, verbose_name="virtual"),
+ ),
+ (
+ "inicio_desejado",
+ models.DateField(
+ help_text="Data desejada para o início do evento. Pode ser solicitado pela Casa ou definido pela conveniência do Interlegis. Será usada como data de início do evento, caso seja autorizado.",
+ verbose_name="início desejado",
+ ),
+ ),
+ (
+ "status",
+ models.CharField(
+ choices=[
+ ("S", "Solicitado"),
+ ("A", "Autorizado"),
+ ("R", "Rejeitado"),
+ ],
+ default="S",
+ verbose_name="status",
+ ),
+ ),
+ (
+ "data_analise",
+ models.DateTimeField(
+ blank=True,
+ editable=False,
+ null=True,
+ verbose_name="data da autorização/rejeição",
+ ),
+ ),
+ (
+ "justificativa",
+ models.TextField(blank=True, verbose_name="Justificativa"),
+ ),
+ (
+ "evento",
+ models.ForeignKey(
+ editable=False,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ to="eventos.evento",
+ ),
+ ),
+ (
+ "servidor",
+ 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",
+ ),
+ ),
+ (
+ "solicitacao",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to="eventos.solicitacao",
+ ),
+ ),
+ (
+ "tipo_evento",
+ models.ForeignKey(
+ limit_choices_to={"casa_solicita": True},
+ on_delete=django.db.models.deletion.PROTECT,
+ to="eventos.tipoevento",
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "Evento solicitado",
+ "verbose_name_plural": "Eventos solicitados",
+ "ordering": ("status",),
+ },
+ ),
+ ]
diff --git a/sigi/apps/eventos/migrations/0038_migra_pedidos.py b/sigi/apps/eventos/migrations/0038_migra_pedidos.py
new file mode 100644
index 0000000..b057b45
--- /dev/null
+++ b/sigi/apps/eventos/migrations/0038_migra_pedidos.py
@@ -0,0 +1,141 @@
+# Generated by Django 4.2.4 on 2023-08-11 20:36
+import re
+from functools import reduce
+from django.db import migrations
+from django.utils import timezone
+from sigi.apps.utils import to_ascii
+
+
+def forwards(apps, schema_editor):
+ TipoEvento = apps.get_model("eventos", "TipoEvento")
+ Evento = apps.get_model("eventos", "Evento")
+ Solicitacao = apps.get_model("eventos", "Solicitacao")
+ ItemSolicitado = apps.get_model("eventos", "ItemSolicitado")
+
+ conjuncoes = [
+ "oficina",
+ "a",
+ "e",
+ "o",
+ "da",
+ "de",
+ "do",
+ "na",
+ "no",
+ "em",
+ "ao",
+ "-",
+ "com",
+ ]
+
+ # Siglas para TipoEvento com as iniciais de cada palavra do nome
+
+ for t in TipoEvento.objects.all():
+ t.sigla = "".join(
+ [s[:1] for s in t.nome.split(" ") if s.lower() not in conjuncoes]
+ )
+ t.save()
+
+ tipos = {
+ t.id: {
+ s
+ for s in to_ascii(t.nome.lower()).replace("/", " ").split(" ")
+ if s not in conjuncoes
+ }
+ for t in TipoEvento.objects.exclude(id=35)
+ }
+
+ conjugados = [
+ to_ascii(re.search("(\w+) e (\w+)", t.nome.lower()).group())
+ for t in TipoEvento.objects.exclude(id=35)
+ if " e " in t.nome
+ ]
+
+ # Tipo_evento_id 35 foi cadastrado como 'pedidos SIGAD' e todos os eventos
+ # deste tipo serão convertidos em Solicitacao
+
+ for e in Evento.objects.filter(tipo_evento_id=35).exclude(
+ casa_anfitria=None
+ ):
+ solicitacao = Solicitacao(
+ casa=e.casa_anfitria,
+ senador=e.solicitante,
+ num_processo=e.num_processo,
+ descricao=e.descricao,
+ data_pedido=e.data_pedido or timezone.localdate(),
+ contato=e.contato,
+ telefone_contato=e.telefone,
+ estimativa_casas=0,
+ estimativa_servidores=0,
+ )
+ solicitacao.save()
+
+ if e.num_processo:
+ for se in Evento.objects.filter(
+ num_processo=e.num_processo
+ ).exclude(id=e.id):
+ ItemSolicitado(
+ solicitacao=solicitacao,
+ tipo_evento=se.tipo_evento,
+ virtual=se.virtual,
+ inicio_desejado=se.data_inicio
+ or se.data_pedido
+ or timezone.localdate(),
+ status="A", # autorizado
+ justificativa="Automática na migração dos dados",
+ evento=se,
+ ).save()
+
+ descricoes = (
+ reduce(
+ lambda x, y: x.replace(y, y.replace(" e ", " ")),
+ conjugados,
+ to_ascii(
+ e.descricao.lower().replace("\r\n", "").replace(";", ",")
+ ),
+ )
+ .replace(" e ", ", ")
+ .replace("/", " ")
+ .replace(".", " ")
+ .replace("(", " ")
+ .replace(")", " ")
+ .split(",")
+ )
+
+ for d in descricoes:
+ termos = {
+ s.strip() for s in d.strip().split(" ") if s not in conjuncoes
+ }
+ similar = min(
+ [(id, len(termos.difference(t))) for id, t in tipos.items()],
+ key=lambda x: x[1],
+ )
+ if similar and similar[1] < len(termos):
+ if not ItemSolicitado.objects.filter(
+ solicitacao=solicitacao, tipo_evento_id=similar[0]
+ ).exists():
+ ItemSolicitado(
+ solicitacao=solicitacao,
+ tipo_evento_id=similar[0],
+ virtual=e.virtual,
+ inicio_desejado=e.data_inicio
+ or e.data_pedido
+ or timezone.localdate(),
+ status="S",
+ ).save()
+ e.delete()
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ (
+ "eventos",
+ "0037_tipoevento_duracao_tipoevento_sigla_solicitacao_and_more",
+ ),
+ ]
+
+ operations = [
+ migrations.RunPython(
+ forwards,
+ )
+ ]
diff --git a/sigi/apps/eventos/models.py b/sigi/apps/eventos/models.py
index f417f57..598519b 100644
--- a/sigi/apps/eventos/models.py
+++ b/sigi/apps/eventos/models.py
@@ -1,16 +1,17 @@
import datetime
import re
+from tinymce.models import HTMLField
+from django.contrib import admin
from django.core.validators import RegexValidator
+from django.core.exceptions import ValidationError
from django.db import models
-from django.db.models import Sum
+from django.db.models import Sum, Count
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext as _
-from sigi.apps.casas.models import Orgao
+from sigi.apps.casas.models import Orgao, Servidor
from sigi.apps.contatos.models import Municipio
from sigi.apps.servidores.models import Servidor
-from django.core.exceptions import ValidationError
-from tinymce.models import HTMLField
class TipoEvento(models.Model):
@@ -29,12 +30,14 @@ class TipoEvento(models.Model):
)
nome = models.CharField(_("Nome"), max_length=100)
+ sigla = models.CharField(_("sigla"), max_length=20, blank=True)
categoria = models.CharField(
_("Categoria"), max_length=1, choices=CATEGORIA_CHOICES
)
casa_solicita = models.BooleanField(
_("casa pode solicitar"), default=False
)
+ duracao = models.PositiveIntegerField(_("Duração (dias)"), default=1)
moodle_template_courseid = models.PositiveBigIntegerField(
_("Curso protótipo"),
blank=True,
@@ -66,6 +69,133 @@ class TipoEvento(models.Model):
return self.nome
+class Solicitacao(models.Model):
+ casa = models.ForeignKey(
+ Orgao, verbose_name=_("casa solicitante"), on_delete=models.PROTECT
+ )
+ senador = models.CharField(_("senador solicitante"), max_length=100)
+ num_processo = models.CharField(
+ _("número do processo SIGAD"),
+ max_length=20,
+ blank=True,
+ help_text=_("Formato:XXXXX.XXXXXX/XXXX-XX"),
+ )
+ descricao = models.TextField(_("descrição da solicitação"))
+ data_pedido = models.DateField(
+ _("Data do pedido"),
+ help_text=_("Data em que o pedido do Gabinete chegou à COPERI"),
+ )
+ contato = models.CharField(_("pessoa de contato na Casa"), max_length=100)
+ email_contato = models.EmailField(_("e-mail do contato"), blank=True)
+ telefone_contato = models.CharField(
+ _("telefone do contato"), max_length=20, blank=True
+ )
+ whatsapp_contato = models.CharField(
+ _("whatsapp do contato"), max_length=20, blank=True
+ )
+ estimativa_casas = models.PositiveIntegerField(
+ _("estimativa de Casas participantes"),
+ help_text=_("estimativa de quantas Casas participarão dos eventos"),
+ )
+ estimativa_servidores = models.PositiveIntegerField(
+ _("estimativa de servidores participantes"),
+ help_text=_(
+ "estimativa de quantos Servidores participarão dos eventos"
+ ),
+ )
+
+ class Meta:
+ ordering = ("-data_pedido",)
+ verbose_name = _("Solicitação de eventos")
+ verbose_name_plural = _("Solicitações de eventos")
+
+ 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")
+
+
+class ItemSolicitado(models.Model):
+ STATUS_SOLICITADO = "S"
+ STATUS_AUTORIZADO = "A"
+ STATUS_REJEITADO = "R"
+ STATUS_CHOICES = (
+ (STATUS_SOLICITADO, _("Solicitado")),
+ (STATUS_AUTORIZADO, _("Autorizado")),
+ (STATUS_REJEITADO, _("Rejeitado")),
+ )
+ solicitacao = models.ForeignKey(Solicitacao, on_delete=models.CASCADE)
+ tipo_evento = models.ForeignKey(
+ TipoEvento,
+ on_delete=models.PROTECT,
+ limit_choices_to={"casa_solicita": True},
+ )
+ virtual = models.BooleanField(_("virtual"), default=False)
+ inicio_desejado = models.DateField(
+ _("início desejado"),
+ help_text=_(
+ "Data desejada para o início do evento. Pode ser solicitado pela Casa ou definido pela conveniência do Interlegis. Será usada como data de início do evento, caso seja autorizado."
+ ),
+ )
+ status = models.CharField(
+ verbose_name=_("status"),
+ choices=STATUS_CHOICES,
+ default=STATUS_SOLICITADO,
+ )
+ data_analise = models.DateTimeField(
+ _("data da autorização/rejeição"),
+ blank=True,
+ null=True,
+ editable=False,
+ )
+ 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,
+ )
+ justificativa = models.TextField(
+ verbose_name=_("Justificativa"), blank=True
+ )
+ evento = models.ForeignKey(
+ "Evento", on_delete=models.SET_NULL, null=True, editable=False
+ )
+
+ class Meta:
+ ordering = ("status",)
+ verbose_name = _("Evento solicitado")
+ verbose_name_plural = _("Eventos solicitados")
+
+ def __str__(self):
+ return _(f"{self.tipo_evento}: {self.get_status_display()}")
+
+
class Evento(models.Model):
STATUS_PLANEJAMENTO = "E"
STATUS_AGUARDANDOSIGAD = "G"
diff --git a/sigi/menu_conf.yaml b/sigi/menu_conf.yaml
index 84ebf54..00b479a 100644
--- a/sigi/menu_conf.yaml
+++ b/sigi/menu_conf.yaml
@@ -74,6 +74,9 @@ main_menu:
- title: Eventos
icon: school
children:
+ - title: Solicitações
+ view_name: admin:eventos_solicitacao_changelist
+ querystr: status=inconcluso
- title: Todos os eventos
view_name: admin:eventos_evento_changelist
- title: Cursos
@@ -95,9 +98,6 @@ main_menu:
view_name: eventos_calendario
- title: Alocação de equipe
view_name: eventos_alocacaoequipe
- - title: Solicitações de eventos
- view_name: ocorrencias_painel
- querystr: tipo_categoria=E&status=1&status=2
- title: Servidores
icon: account_circle
children: