Browse Source

Cômputo de custos de oficinas. Gertiq #168279

pull/169/head
Sesóstris Vieira 1 year ago
parent
commit
0daa7433ec
  1. 2
      sigi/apps/espacos/templates/espacos/snippets/agenda_cal.html
  2. 317
      sigi/apps/eventos/admin.py
  3. 50
      sigi/apps/eventos/migrations/0054_equipe_emissao_passagens_equipe_qtde_diarias_and_more.py
  4. 20
      sigi/apps/eventos/models.py
  5. 316
      sigi/apps/eventos/templates/admin/eventos/custos_eventos_report.html
  6. 285
      sigi/apps/eventos/templates/admin/eventos/custos_servidor_report.html
  7. 8
      sigi/apps/eventos/templates/admin/eventos/evento/change_form.html
  8. 163
      sigi/apps/eventos/templates/admin/eventos/evento/custos_report.html
  9. 4
      sigi/apps/utils/templatetags/sigi_tags.py
  10. 5
      sigi/templates/pdf/base_report.html

2
sigi/apps/espacos/templates/espacos/snippets/agenda_cal.html

@ -25,7 +25,6 @@
{% for espaco, reservas in semana.reservas.items %}
<tr class="linha-evento">
<th>{{ espaco.sigla }}</th>
{% setvar 0 as last_pos %}
{% for reserva, tupla in reservas %}
{% for x in ""|ljust:tupla.0|make_list %}<td></td>{% endfor %}
<td colspan="{{ tupla.1 }}" class="blue lighten-4">
@ -40,7 +39,6 @@
{% if forloop.last %}
{% for x in ""|ljust:tupla.2|make_list %}<td></td>{% endfor %}
{% endif %}
{% setvar last_pos|sum:tupla.1 as last_pos %}
{% empty %}
{% for x in "1234567"|make_list %}<td></td>{% endfor %}
{% endfor %}

317
sigi/apps/eventos/admin.py

@ -1,9 +1,24 @@
import datetime
import pandas as pd
import time
from moodle import Moodle
from typing import Any
from django.db import models
from django.db.models import F, OuterRef, Subquery, Count, Q
from django.db.models import (
F,
OuterRef,
Subquery,
Count,
Q,
Sum,
Avg,
Min,
Max,
Prefetch,
Case,
When,
)
from django.db.models.functions import ExtractDay, Cast
from django.conf import settings
from django.contrib import admin, messages
from django.core.exceptions import ValidationError
@ -38,10 +53,12 @@ from sigi.apps.eventos.models import (
Anexo,
)
from sigi.apps.eventos.forms import EventoAdminForm, SelecionaModeloForm
from sigi.apps.servidores.models import Servidor
from sigi.apps.utils import abreviatura
from sigi.apps.utils.filters import DateRangeFilter
from sigi.apps.utils.mixins import (
CartExportMixin,
CartExportReportMixin,
LabeledResourse,
ValueLabeledResource,
)
@ -833,7 +850,7 @@ class ModeloDeclaracaoAdmin(admin.ModelAdmin):
@admin.register(Evento)
class EventoAdmin(CartExportMixin, admin.ModelAdmin):
class EventoAdmin(CartExportReportMixin, admin.ModelAdmin):
form = EventoAdminForm
resource_class = EventoResource
fieldsets = (
@ -951,6 +968,7 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin):
CronogramaInline,
)
save_as = True
reports = ["custos_eventos_report", "custos_servidor_report"]
@admin.display(description=_("banner"))
def get_banner(self, obj):
@ -1071,6 +1089,11 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin):
self.admin_site.admin_view(self.update_participantes),
name="%s_%s_updateparticipantes" % model_info,
),
path(
"<path:object_id>/custos/",
self.admin_site.admin_view(self.custos_report),
name="%s_%s_custos" % model_info,
),
]
return my_urls + urls
@ -1283,6 +1306,296 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin):
content_type="application/pdf",
)
def custos_report(self, request, object_id):
evento = get_object_or_404(Evento, id=object_id)
change_url = (
reverse(
"admin:%s_%s_change" % self.get_model_info(), args=[object_id]
)
+ "?"
+ self.get_preserved_filters(request)
)
if not evento.equipe_set.filter(
Q(valor_diaria__gte=0) | Q(total_passagens__gte=0)
).exists():
self.message_user(
request,
_("Não há valores de diárias e passagens para este evento"),
messages.ERROR,
)
return redirect(change_url)
membros = Equipe.objects.filter(evento=evento).annotate(
antecedencia_passagens=(
ExtractDay(F("evento__data_inicio") - F("emissao_passagens"))
),
total_diarias=(F("qtde_diarias") * F("valor_diaria")),
total_gasto=(F("total_diarias") + F("total_passagens")),
)
my_decimal_field = models.DecimalField(max_digits=14, decimal_places=2)
total_equipe = membros.aggregate(
num_membros=Count("membro_id", distinct=True),
tot_qtde_diarias=Sum("qtde_diarias"),
media_qtde_diarias=Cast(
F("tot_qtde_diarias")
/ 1.0
/ F("num_membros"), # divide por 1.0 para forçar float
output_field=my_decimal_field,
),
tot_valor_diarias=Sum("total_diarias"),
media_diarias=Cast(
F("tot_valor_diarias") / F("tot_qtde_diarias"),
output_field=my_decimal_field,
),
tot_passagens=Sum("total_passagens"),
media_passagens=Cast(
F("tot_passagens") / F("num_membros"),
output_field=my_decimal_field,
),
tot_gastos=Sum("total_gasto"),
media_antecedencia=Avg("antecedencia_passagens"),
)
context = {
"evento": evento,
"membros": membros,
"total_equipe": total_equipe,
}
return WeasyTemplateResponse(
filename=f"custos{evento.nome.replace(' ','')}-{timezone.localdate()}.pdf",
request=request,
template="admin/eventos/evento/custos_report.html",
context=context,
content_type="application/pdf",
)
def custos_eventos_report(self, request):
my_decimal_field = models.DecimalField(max_digits=14, decimal_places=2)
equipe_qs = Equipe.objects.annotate(
total_diarias=(F("qtde_diarias") * F("valor_diaria")),
antecedencia=ExtractDay(
F("evento__data_inicio") - F("emissao_passagens")
),
)
eventos = (
self.get_queryset(request)
.annotate(
duracao_dias=(
ExtractDay(F("data_termino") - F("data_inicio")) + 1
),
qtde_diarias=Sum("equipe__qtde_diarias"),
vlr_tot_diarias=Sum(
F("equipe__qtde_diarias") * F("equipe__valor_diaria")
),
vlr_tot_passagens=Sum("equipe__total_passagens"),
custo_total=F("vlr_tot_diarias") + F("vlr_tot_passagens"),
custo_medio_participante=Cast(
Case(
When(total_participantes__lte=0, then=0),
default=F("custo_total") / F("total_participantes"),
output_field=my_decimal_field,
),
output_field=my_decimal_field,
),
custo_medio_membro=Cast(
F("custo_total") / Count("equipe__membro"),
output_field=my_decimal_field,
),
tot_membros=Count("equipe"),
)
.prefetch_related(
Prefetch(
"equipe_set", queryset=equipe_qs, to_attr="equipe_ext"
)
)
)
resumo = eventos.aggregate(
qtde_oficinas=Count("id"),
tot_participantes=Sum("total_participantes"),
media_participantes=Cast(
1.0 * F("tot_participantes") / F("qtde_oficinas"),
output_field=my_decimal_field,
),
min_participantes=Min("total_participantes"),
max_participantes=Max("total_participantes"),
tot_servidores=Sum("tot_membros"),
media_membros=Cast(
1.0 * Sum("tot_membros") / F("qtde_oficinas"),
output_field=my_decimal_field,
),
min_membros=Min("tot_membros"),
max_membros=Max("tot_membros"),
tot_dias=Sum("duracao_dias"),
media_dias=Cast(
1.0 * F("tot_dias") / F("qtde_oficinas"),
output_field=my_decimal_field,
),
tot_diarias=Sum("qtde_diarias"),
media_diarias=Cast(
1.0 * F("tot_diarias") / F("qtde_oficinas"),
output_field=my_decimal_field,
),
tot_custo_total=Sum("custo_total"),
tot_custo_diarias=Sum("vlr_tot_diarias"),
tot_custo_passagens=Sum("vlr_tot_passagens"),
media_custo_total=Cast(
F("tot_custo_total") / F("qtde_oficinas"),
output_field=my_decimal_field,
),
media_custo_diarias=Cast(
F("tot_custo_diarias") / F("qtde_oficinas"),
output_field=my_decimal_field,
),
media_custo_passagens=Cast(
F("tot_custo_passagens") / F("qtde_oficinas"),
output_field=my_decimal_field,
),
media_custo_participantes=Cast(
F("tot_custo_total") / F("tot_participantes"),
output_field=my_decimal_field,
),
media_custo_membro=Cast(
F("tot_custo_total") / Sum("tot_membros"),
output_field=my_decimal_field,
),
)
resumo.update(
eventos.aggregate(
media_antecedencia=Avg(
ExtractDay(
F("data_inicio") - F("equipe__emissao_passagens")
)
),
min_antecedencia=Min(
ExtractDay(
F("data_inicio") - F("equipe__emissao_passagens")
)
),
max_antecedencia=Max(
ExtractDay(
F("data_inicio") - F("equipe__emissao_passagens")
)
),
)
)
f_valor_diarias = F("equipe__qtde_diarias") * F("equipe__valor_diaria")
f_custo_total = (f_valor_diarias) + F("equipe__total_passagens")
extrato = (
self.get_queryset(request)
.order_by("casa_anfitria__municipio__uf__regiao")
.annotate(
regiao=F("casa_anfitria__municipio__uf__regiao"),
tot_diarias=Sum(f_valor_diarias),
tot_passagens=Sum("equipe__total_passagens"),
tot_custo=Sum(f_custo_total),
)
.values("regiao", "tot_diarias", "tot_passagens", "tot_custo")
)
df = (
pd.DataFrame(extrato)
.set_index("regiao")
.groupby("regiao")
.aggregate(["sum", "min", "max", "mean"])
.fillna(0)
)
custos_regiao = [
{
"nome": nome,
"extrato": df.loc[sigla] if sigla in df.index else None,
}
for sigla, nome in UnidadeFederativa.REGIAO_CHOICES
]
context = {
"eventos": eventos,
"resumo": resumo,
"custos_regiao": custos_regiao,
"title": _("Custos por eventos"),
}
return WeasyTemplateResponse(
filename=f"custos_eventos-{timezone.localdate()}.pdf",
request=request,
template="admin/eventos/custos_eventos_report.html",
context=context,
content_type="application/pdf",
)
custos_eventos_report.title = _("Custos por eventos")
def custos_servidor_report(self, request):
equipe_qs = Equipe.objects.filter(
evento__in=self.get_queryset(request)
)
f_total_diarias = F("equipe_evento__qtde_diarias") * F(
"equipe_evento__valor_diaria"
)
my_decimal_field = models.DecimalField(max_digits=14, decimal_places=2)
servidores = (
Servidor.objects.distinct()
.filter(equipe_evento__evento__in=self.get_queryset(request))
.prefetch_related(
Prefetch(
"equipe_evento", queryset=equipe_qs, to_attr="equipe_ext"
)
)
.annotate(
qtde_eventos=Count("equipe_evento"),
qtde_diarias=Sum("equipe_evento__qtde_diarias"),
media_diarias=Cast(
Sum(f_total_diarias / F("equipe_evento__qtde_diarias")),
output_field=my_decimal_field,
),
total_diarias=Sum(f_total_diarias),
total_passagens=Sum("equipe_evento__total_passagens"),
total_custo=Sum(
F("equipe_evento__total_passagens") + f_total_diarias
),
)
)
totais = (
Servidor.objects.distinct()
.filter(equipe_evento__evento__in=self.get_queryset(request))
.prefetch_related(
Prefetch(
"equipe_evento", queryset=equipe_qs, to_attr="equipe_ext"
)
)
.aggregate(
qtde_eventos=Count("equipe_evento"),
qtde_diarias=Sum("equipe_evento__qtde_diarias"),
media_diarias=Cast(
Avg(f_total_diarias / F("equipe_evento__qtde_diarias")),
output_field=my_decimal_field,
),
total_diarias=Sum(f_total_diarias),
total_passagens=Sum("equipe_evento__total_passagens"),
total_custo=Sum(
F("equipe_evento__total_passagens") + f_total_diarias
),
)
)
context = {
"servidores": servidores,
"totais": totais,
"title": _("Custos por servidor"),
}
return WeasyTemplateResponse(
filename=f"custos_servidor-{timezone.localdate()}.pdf",
request=request,
template="admin/eventos/custos_servidor_report.html",
context=context,
content_type="application/pdf",
)
custos_servidor_report.title = _("Custos por servidor")
def create_course(self, request, object_id):
evento = get_object_or_404(Evento, id=object_id)
change_url = (

50
sigi/apps/eventos/migrations/0054_equipe_emissao_passagens_equipe_qtde_diarias_and_more.py

@ -0,0 +1,50 @@
# Generated by Django 4.2.4 on 2023-11-13 13:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("eventos", "0053_visita_anfitria_senado_oficina_remove_convite"),
]
operations = [
migrations.AddField(
model_name="equipe",
name="emissao_passagens",
field=models.DateField(
blank=True,
null=True,
verbose_name="data de emissão das passagens",
),
),
migrations.AddField(
model_name="equipe",
name="qtde_diarias",
field=models.PositiveIntegerField(
blank=True, null=True, verbose_name="quantidade de diárias"
),
),
migrations.AddField(
model_name="equipe",
name="total_passagens",
field=models.DecimalField(
blank=True,
decimal_places=2,
max_digits=14,
null=True,
verbose_name="valor total das passagens",
),
),
migrations.AddField(
model_name="equipe",
name="valor_diaria",
field=models.DecimalField(
blank=True,
decimal_places=2,
max_digits=14,
null=True,
verbose_name="valor da diária (R$)",
),
),
]

20
sigi/apps/eventos/models.py

@ -757,6 +757,26 @@ class Equipe(models.Model):
assina_oficio = models.BooleanField(
_("Assina ofício de comparecimento"), default=False
)
qtde_diarias = models.PositiveIntegerField(
_("quantidade de diárias"), blank=True, null=True
)
valor_diaria = models.DecimalField(
_("valor da diária (R$)"),
max_digits=14,
decimal_places=2,
blank=True,
null=True,
)
emissao_passagens = models.DateField(
_("data de emissão das passagens"), blank=True, null=True
)
total_passagens = models.DecimalField(
_("valor total das passagens"),
max_digits=14,
decimal_places=2,
blank=True,
null=True,
)
observacoes = models.TextField(_("Observações"), blank=True)
class Meta:

316
sigi/apps/eventos/templates/admin/eventos/custos_eventos_report.html

@ -0,0 +1,316 @@
{% extends 'pdf/base_report.html' %}
{% load static i18n sigi_tags %}
{% block page_size %}A4 landscape{% endblock page_size %}
{% block extra_style %}
{{ block.super }}
aside {
margin-left: 8px;
font-size: 0.8em;
color: #666;
}
blockquote {
margin: 12px 0 12px;
padding-left: 1.5rem;
border-left: 5px solid #ee6e73;
font-size: 1.4em;
font-weight: bold;
}
tr:nth-child(even) {
background-color: initial;
}
.even-row {
background-color: #d2d2d2 !important;
}
.sessao-resumo {
align-items: stretch;
display: flex;
flex-wrap: wrap;
width: 100%;
margin-top: 24px;
}
.card-resumo {
background-color: #eeeeef;
border-radius: 2px;
box-sizing: border-box;
margin: 6px;
flex-basis: 49%;
padding: 0 6px 6px 6px;
position: relative;
width: 100%;
}
.card-resumo.full {
flex-basis: 98%;
}
.index-cell {
width: 2em;
text-align: center;
}
.label-resumo {
min-width: 30em;
}
{% endblock %}
{% block main_content %}
<table repeat="2">
<thead>
<tr>
<th rowspan="2">{% trans "Início / término" %}</th>
<th rowspan="2">{% trans "SIGAD" %}</th>
<th rowspan="2">{% trans "Evento" %}</th>
<th rowspan="2">{% trans "Casa anfitriã" %}</th>
<th rowspan="2">{% trans "Duração do evento (dias)" %}</th>
<th rowspan="2">{% trans "Total de participantes" %}</th>
<th colspan="7">{% trans "Equipe" %}</th>
<th colspan="3">{% trans "Custos" %}</th>
</tr>
<tr>
<th>{% trans "Nome" %}</th>
<th>{% trans "Função" %}</th>
<th>{% trans "Qtde de diárias" %}</th>
<th>{% trans "Valor total diárias" %}</th>
<th>{% trans "Valor total passagens" %}</th>
<th>{% trans "Emissão das passagens" %}</th>
<th>{% trans "Antecedência das passagens (dias)" %}</th>
<th>{% trans "Custo total" %}</th>
<th>{% trans "Custo médio participante" %}</th>
<th>{% trans "Custo médio equipe" %}</th>
</thead>
<tbody>
{% for evento in eventos %}
{% with equipe_count=evento.equipe_ext|length|default:1 %}
<tr class="{% cycle "" "even-row" as row_class %}">
<td rowspan="{{ equipe_count }}" class="center-align">
{% blocktranslate with inicio=evento.data_inicio|date:"SHORT_DATE_FORMAT" termino=evento.data_termino|date:"SHORT_DATE_FORMAT" %}
{{ inicio }} a {{ termino }}
{% endblocktranslate %}
</td>
<td rowspan="{{ equipe_count }}">{{ evento.num_processo }}</td>
<td rowspan="{{ equipe_count }}">
{% blocktranslate with nome=evento.nome turma=evento.turma %}
{{ nome }} - turma {{ turma }}
{% endblocktranslate %}
</td>
<td rowspan="{{ equipe_count }}">{{ evento.casa_anfitria }}</td>
<td rowspan="{{ equipe_count }}" class="right-align">{{ evento.duracao_dias|default:"-" }}</td>
<td rowspan="{{ equipe_count }}" class="right-align">{{ evento.total_participantes|default:"-" }}</td>
{% for membro in evento.equipe_ext %}
{% if not forloop.first %}<tr class="{{ row_class }}">{% endif %}
<td>{{ membro.membro.get_apelido }}</td>
<td>{{ membro.funcao }}</td>
<td class="right-align">{{ membro.qtde_diarias|default:"-" }}</td>
<td class="right-align">{{ membro.total_diarias|default:"-" }}</td>
<td class="right-align">{{ membro.total_passagens|default:"-" }}</td>
<td>{{ membro.emissao_passagens|default:"-" }}</td>
<td class="right-align">{{ membro.antecedencia|default:"-" }}</td>
{% if forloop.first %}
<td rowspan="{{ equipe_count }}" class="right-align">{{ evento.custo_total|default:"-" }}</td>
<td rowspan="{{ equipe_count }}" class="right-align">{{ evento.custo_medio_participante|default:"-" }}</td>
<td rowspan="{{ equipe_count }}"class="right-align">{{ evento.custo_medio_membro|default:"-" }}</td>
{% endif %}
</tr>
{% empty %}
<td colspan="7" class="center-align">{% trans "Equipe não definida" %}</td>
<td class="right-align">{{ evento.custo_total|default:"-" }}</td>
<td class="right-align">{{ evento.custo_medio_participante|default:"-" }}</td>
<td class="right-align">{{ evento.custo_medio_membro|default:"-" }}</td>
</tr>
{% endfor %}
{% endwith %}
{% endfor %}
</tbody>
</table>
{# Resumo do relatório #}
<div class="sessao-resumo">
<div class="card-resumo">
<blockquote>{% trans "Dados gerais" %}</blockquote>
<table>
<tr>
<th class="index-cell">{% cycle "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" as letra %}</th>
<th class="label-resumo">{% trans "Quantidade de eventos" %}</th>
<td class="right-align">{{ resumo.qtde_oficinas|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total de participantes" %}</th>
<td class="right-align">{{ resumo.tot_participantes|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Média de participantes por evento" %} [B / A]</th>
<td class="right-align">{{ resumo.media_participantes|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Mínimo de participantes" %}</th>
<td class="right-align">{{ resumo.min_participantes|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Máximo de participantes" %}</th>
<td class="right-align">{{ resumo.max_participantes|default:"-" }}</td>
</tr>
</table>
</div>
<div class="card-resumo">
<blockquote>{% trans "Equipes" %}</blockquote>
<table>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total de servidores em missão" %}</th>
<td class="right-align">{{ resumo.tot_servidores|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Tamanho médio das equipes [F / A]" %}</th>
<td class="right-align">{{ resumo.media_membros|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Menor equipe" %}</th>
<td class="right-align">{{ resumo.min_membros|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Maior equipe" %}</th>
<td class="right-align">{{ resumo.max_membros|default:"-" }}</td>
</tr>
</table>
</div>
<div class="card-resumo">
<blockquote>{% trans "Tempo" %}</blockquote>
<table>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total de dias de evento" %}</th>
<td class="right-align">{{ resumo.tot_dias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Duração média dos eventos (dias) [J / A]" %}</th>
<td class="right-align">{{ resumo.media_dias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total de diárias" %}</th>
<td class="right-align">{{ resumo.tot_diarias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Média de diárias por evento [L / A]" %}</th>
<td class="right-align">{{ resumo.media_diarias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Antecedência média na emissão de passagens" %}</th>
<td class="right-align">{{ resumo.media_antecedencia|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Menor antecedência" %}</th>
<td class="right-align">{{ resumo.min_antecedencia|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Maior antecedência" %}</th>
<td class="right-align">{{ resumo.max_antecedencia|default:"-" }}</td>
</tr>
</table>
</div>
<div class="card-resumo">
<blockquote>{% trans "Custos" %}</blockquote>
<table>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Custo total" %}</th>
<td class="right-align">{{ resumo.tot_custo_total|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total com diárias" %}</th>
<td class="right-align">{{ resumo.tot_custo_diarias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total com passagens" %}</th>
<td class="right-align">{{ resumo.tot_custo_passagens|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Custo médio dos eventos [Q / A]" %}</th>
<td class="right-align">{{ resumo.media_custo_total|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Custo médio de diárias por evento [R / A]" %}</th>
<td class="right-align">{{ resumo.media_custo_diarias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Custo médio de passagens por evento [S / A]" %}</th>
<td class="right-align">{{ resumo.media_custo_passagens|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Custo médio por participante [Q / B]" %}</th>
<td class="right-align">{{ resumo.media_custo_participantes|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Gasto médio por membro da equipe" %}</th>
<td class="right-align">{{ resumo.media_custo_membro|default:"-" }}</td>
</tr>
</table>
</div>
<div class="card-resumo full">
<blockquote>{% trans "Custos por região" %}</blockquote>
<table>
<thead>
<tr>
<th rowspan="2" class="center-align">{% trans "Região" %}</th>
<th colspan="4" class="center-align">{% trans "Custos com diárias" %}</th>
<th colspan="4" class="center-align">{% trans "Custos com passagens" %}</th>
<th colspan="4" class="center-align">{% trans "Custo total" %}</th>
</tr>
<tr>
<th class="right-align">{% trans "Mínimo" %}</th>
<th class="right-align">{% trans "Médio" %}</th>
<th class="right-align">{% trans "Máximo" %}</th>
<th class="right-align">{% trans "Total" %}</th>
<th class="right-align">{% trans "Mínimo" %}</th>
<th class="right-align">{% trans "Médio" %}</th>
<th class="right-align">{% trans "Máximo" %}</th>
<th class="right-align">{% trans "Total" %}</th>
<th class="right-align">{% trans "Mínimo" %}</th>
<th class="right-align">{% trans "Médio" %}</th>
<th class="right-align">{% trans "Máximo" %}</th>
<th class="right-align">{% trans "Total" %}</th>
</thead>
<tbody>
{% for data in custos_regiao %}
<tr>
<th>{{ data.nome }}</th>
<td class="right-align">{{ data.extrato.tot_diarias.min|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_diarias.mean|default:"-"|floatformat:2 }}</td>
<td class="right-align">{{ data.extrato.tot_diarias.max|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_diarias.sum|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_passagens.min|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_passagens.mean|default:"-"|floatformat:2 }}</td>
<td class="right-align">{{ data.extrato.tot_passagens.max|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_passagens.sum|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_custo.min|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_custo.mean|default:"-"|floatformat:2 }}</td>
<td class="right-align">{{ data.extrato.tot_custo.max|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_custo.sum|default:"-" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

285
sigi/apps/eventos/templates/admin/eventos/custos_servidor_report.html

@ -0,0 +1,285 @@
{% extends 'pdf/base_report.html' %}
{% load static i18n sigi_tags %}
{% block page_size %}A4 landscape{% endblock page_size %}
{% block extra_style %}
{{ block.super }}
aside {
margin-left: 8px;
font-size: 0.8em;
color: #666;
}
blockquote {
margin: 12px 0 12px;
padding-left: 1.5rem;
border-left: 5px solid #ee6e73;
font-size: 1.4em;
font-weight: bold;
}
tr:nth-child(even) {
background-color: initial;
}
.even-row {
background-color: #d2d2d2 !important;
}
.sessao-resumo {
align-items: stretch;
display: flex;
flex-wrap: wrap;
width: 100%;
margin-top: 24px;
}
.card-resumo {
background-color: #eeeeef;
border-radius: 2px;
box-sizing: border-box;
margin: 6px;
flex-basis: 49%;
padding: 0 6px 6px 6px;
position: relative;
width: 100%;
}
.card-resumo.full {
flex-basis: 98%;
}
.index-cell {
width: 2em;
text-align: center;
}
.label-resumo {
min-width: 30em;
}
{% endblock %}
{% block main_content %}
<table repeat="2">
<thead>
<tr>
<th>{% trans "ID saberes" %}</th>
<th>{% trans "Membro da equipe" %}</th>
<th>{% trans "Qtde eventos" %}</th>
<th>{% trans "Qtde diárias" %}</th>
<th>{% trans "Valor médio diária" %}</th>
<th>{% trans "Total diárias" %}</th>
<th>{% trans "Total passagens" %}</th>
<th>{% trans "Total" %}</th>
</tr>
</thead>
<tbody>
{% for servidor in servidores %}
<tr>
<td>{{ servidor.moodle_id }}</td>
<td>{{ servidor.nome_completo }}</td>
<td class="right-align">{{ servidor.qtde_eventos|default:"-" }}</td>
<td class="right-align">{{ servidor.qtde_diarias|default:"-" }}</td>
<td class="right-align">{{ servidor.media_diarias|default:"-" }}</td>
<td class="right-align">{{ servidor.total_diarias|default:"-" }}</td>
<td class="right-align">{{ servidor.total_passagens|default:"-" }}</td>
<td class="right-align">{{ servidor.total_custo|default:"-" }}</td>
</tr>
{% endfor %}
<tr>
<th colspan="2" class="right-align">{% trans "Totais" %}</th>
<th class="right-align">{{ totais.qtde_eventos|default:"-" }}</th>
<th class="right-align">{{ totais.qtde_diarias|default:"-" }}</th>
<th class="right-align">{{ totais.media_diarias|default:"-" }}</th>
<th class="right-align">{{ totais.total_diarias|default:"-" }}</th>
<th class="right-align">{{ totais.total_passagens|default:"-" }}</th>
<th class="right-align">{{ totais.total_custo|default:"-" }}</th>
</tr>
</tbody>
</table>
{# Resumo do relatório #}
<div class="sessao-resumo">
<div class="card-resumo">
<blockquote>{% trans "Dados gerais" %}</blockquote>
<table>
<tr>
<th class="index-cell">{% cycle "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" as letra %}</th>
<th class="label-resumo">{% trans "Quantidade de eventos" %}</th>
<td class="right-align">{{ resumo.qtde_oficinas|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total de participantes" %}</th>
<td class="right-align">{{ resumo.tot_participantes|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Média de participantes por evento" %} [B / A]</th>
<td class="right-align">{{ resumo.media_participantes|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Mínimo de participantes" %}</th>
<td class="right-align">{{ resumo.min_participantes|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Máximo de participantes" %}</th>
<td class="right-align">{{ resumo.max_participantes|default:"-" }}</td>
</tr>
</table>
</div>
<div class="card-resumo">
<blockquote>{% trans "Equipes" %}</blockquote>
<table>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total de servidores em missão" %}</th>
<td class="right-align">{{ resumo.tot_servidores|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Tamanho médio das equipes [F / A]" %}</th>
<td class="right-align">{{ resumo.media_membros|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Menor equipe" %}</th>
<td class="right-align">{{ resumo.min_membros|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Maior equipe" %}</th>
<td class="right-align">{{ resumo.max_membros|default:"-" }}</td>
</tr>
</table>
</div>
<div class="card-resumo">
<blockquote>{% trans "Tempo" %}</blockquote>
<table>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total de dias de evento" %}</th>
<td class="right-align">{{ resumo.tot_dias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Duração média dos eventos (dias) [J / A]" %}</th>
<td class="right-align">{{ resumo.media_dias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total de diárias" %}</th>
<td class="right-align">{{ resumo.tot_diarias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Média de diárias por evento [L / A]" %}</th>
<td class="right-align">{{ resumo.media_diarias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Antecedência média na emissão de passagens" %}</th>
<td class="right-align">{{ resumo.media_antecedencia|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Menor antecedência" %}</th>
<td class="right-align">{{ resumo.min_antecedencia|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Maior antecedência" %}</th>
<td class="right-align">{{ resumo.max_antecedencia|default:"-" }}</td>
</tr>
</table>
</div>
<div class="card-resumo">
<blockquote>{% trans "Custos" %}</blockquote>
<table>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Custo total" %}</th>
<td class="right-align">{{ resumo.tot_custo_total|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total com diárias" %}</th>
<td class="right-align">{{ resumo.tot_custo_diarias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Total com passagens" %}</th>
<td class="right-align">{{ resumo.tot_custo_passagens|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Custo médio dos eventos [Q / A]" %}</th>
<td class="right-align">{{ resumo.media_custo_total|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Custo médio de diárias por evento [R / A]" %}</th>
<td class="right-align">{{ resumo.media_custo_diarias|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Custo médio de passagens por evento [S / A]" %}</th>
<td class="right-align">{{ resumo.media_custo_passagens|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Custo médio por participante [Q / B]" %}</th>
<td class="right-align">{{ resumo.media_custo_participantes|default:"-" }}</td>
</tr>
<tr>
<th class="index-cell">{% cycle letra %}</th>
<th class="label-resumo">{% trans "Gasto médio por membro da equipe" %}</th>
<td class="right-align">{{ resumo.media_custo_membro|default:"-" }}</td>
</tr>
</table>
</div>
<div class="card-resumo full">
<blockquote>{% trans "Custos por região" %}</blockquote>
<table>
<thead>
<tr>
<th rowspan="2" class="center-align">{% trans "Região" %}</th>
<th colspan="4" class="center-align">{% trans "Custos com diárias" %}</th>
<th colspan="4" class="center-align">{% trans "Custos com passagens" %}</th>
<th colspan="4" class="center-align">{% trans "Custo total" %}</th>
</tr>
<tr>
<th class="right-align">{% trans "Mínimo" %}</th>
<th class="right-align">{% trans "Médio" %}</th>
<th class="right-align">{% trans "Máximo" %}</th>
<th class="right-align">{% trans "Total" %}</th>
<th class="right-align">{% trans "Mínimo" %}</th>
<th class="right-align">{% trans "Médio" %}</th>
<th class="right-align">{% trans "Máximo" %}</th>
<th class="right-align">{% trans "Total" %}</th>
<th class="right-align">{% trans "Mínimo" %}</th>
<th class="right-align">{% trans "Médio" %}</th>
<th class="right-align">{% trans "Máximo" %}</th>
<th class="right-align">{% trans "Total" %}</th>
</thead>
<tbody>
{% for data in custos_regiao %}
<tr>
<th>{{ data.nome }}</th>
<td class="right-align">{{ data.extrato.tot_diarias.min|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_diarias.mean|default:"-"|floatformat:2 }}</td>
<td class="right-align">{{ data.extrato.tot_diarias.max|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_diarias.sum|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_passagens.min|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_passagens.mean|default:"-"|floatformat:2 }}</td>
<td class="right-align">{{ data.extrato.tot_passagens.max|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_passagens.sum|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_custo.min|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_custo.mean|default:"-"|floatformat:2 }}</td>
<td class="right-align">{{ data.extrato.tot_custo.max|default:"-" }}</td>
<td class="right-align">{{ data.extrato.tot_custo.sum|default:"-" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

8
sigi/apps/eventos/templates/admin/eventos/evento/change_form.html

@ -21,6 +21,14 @@
</a>
</li>
{% endif %}
<li>
{% url opts|admin_urlname:'custos' object_id|admin_urlquote as tool_url %}
<a href="{% add_preserved_filters tool_url %}">
<i class="left material-icons" aria-hidden="true">attach_money</i>
{% trans "Custos" %}
</a>
</li>
<li>
{% url opts|admin_urlname:'declaracaoreport' object_id|admin_urlquote as tool_url %}
<a href="{% add_preserved_filters tool_url %}">

163
sigi/apps/eventos/templates/admin/eventos/evento/custos_report.html

@ -0,0 +1,163 @@
{% extends 'pdf/base_report.html' %}
{% load static i18n sigi_tags %}
{% block extra_style %}
{{ block.super }}
aside {
margin-left: 8px;
font-size: 0.8em;
color: #666;
}
blockquote {
margin: 20px 0 5px;
padding-left: 1.5rem;
border-left: 5px solid #ee6e73;
font-size: 1.4em;
font-weight: bold;
}
{% endblock %}
{% block main_content %}
<h3>{% trans 'Custos de realização do evento' %}</h3>
<h4>
{% blocktranslate with nome=evento.nome turma=evento.turma %}
{{ nome }} - turma {{ turma }}
{% endblocktranslate %}
</h4>
<!-- Identificação do evento ============================================= -->
<blockquote>{% trans "Identificação do evento" %}</blockquote>
<table>
<tr>
<th>{% trans 'Casa anfitriã' %}</th>
<td>{{ evento.casa_anfitria }}</td>
</tr>
<tr>
<th>{% trans 'Tipo' %}</th>
<td>
{% blocktranslate with tipo_evento=evento.tipo_evento virtual=evento.virtual|yesno:"- VIRTUAL,," %}
{{ tipo_evento }} {{ virtual }}
{% endblocktranslate %}
</td>
</tr>
<tr>
<th>{% trans 'Descrição' %}</th>
<td>{{ evento.descricao }}</td>
</tr>
<tr>
<th>{% trans 'Senador(a) solicitante' %}</th>
<td>{{ evento.solicitante }}</td>
</tr>
<tr>
<th>{% trans 'Data do pedido' %}</th>
<td>{{ evento.data_pedido }}</td>
</tr>
<tr>
<th>{% trans 'Data de recebimento na COPERI' %}</th>
<td>{{ evento.data_recebido_coperi }}</td>
</tr>
<tr>
<th>{% trans 'Período de realização' %}</th>
<td>
{% blocktranslate with inicio=evento.data_inicio termino=evento.data_termino %}
de {{ inicio }} até {{ termino }}
{% endblocktranslate %}
</td>
</tr>
</table>
<!-- Equipe ============================================================== -->
<blockquote>{% trans "Equipe participante" %}</blockquote>
</tr>
<table repeat="2">
<tr>
<th rowspan="2">{% trans "Nome" %}</th>
<th rowspan="2">{% trans "Função" %}</th>
<th colspan="3">{% trans "Diárias" %}</th>
<th colspan="3">{% trans "Passagens" %}</th>
<th rowspan="2">{% trans "Total gasto (A + B)" %}</th>
</tr>
<tr>
<th>{% trans "Quantidade" %}</th>
<th>{% trans "Valor unitário" %}</th>
<th>{% trans "Valor total (A)" %}</th>
<th>{% trans "Valor total (B)" %}</th>
<th>{% trans "Emissão" %}</th>
<th>{% trans "Dias de antecedência" %}<sup>*</sup></th>
</tr>
{% for membro in membros %}
<tr class="membro_row">
<td>{{ membro.membro.nome_completo }}</td>
<td>{{ membro.funcao }}</td>
<td class="right-align">{{ membro.qtde_diarias|default:"-" }}</td>
<td class="right-align">{{ membro.valor_diaria|default:"-" }}</td>
<td class="right-align">{{ membro.total_diarias|default:"-" }}</td>
<td class="right-align">{{ membro.total_passagens|default:"-" }}</td>
<td class="right-align">{{ membro.emissao_passagens|default:"-" }}</td>
<td class="right-align">{{ membro.antecedencia_passagens|default:"-" }}</td>
<td class="right-align">{{ membro.total_gasto|default:"-" }}</td>
</tr>
{% endfor %}
<tr>
<th colspan="2">{% trans "Total de gastos" %}</th>
<th class="right-align">{{ total_equipe.tot_qtde_diarias|default:"-" }}</th>
<th class="right-align">-</th>
<th class="right-align">{{ total_equipe.tot_valor_diarias|default:"-" }}</th>
<th class="right-align">{{ total_equipe.tot_passagens|default:"-" }}</th>
<th class="right-align">-</th>
<th class="right-align">-</th>
<th class="right-align">{{ total_equipe.tot_gastos|default:"-" }}</th>
</table>
<aside>* Número de dias entre a data de emissão das passagens e a data de início do evento</aside>
<!-- Custos ============================================================== -->
<blockquote>{% trans "Resumo dos custos" %}</blockquote>
<table>
<tr>
<th>A</th>
<th>{% trans 'Número de membros da equipe' %}</th>
<td class="right-align">{{ total_equipe.num_membros|default:"-" }}</td>
</tr>
<tr>
<th>B</th>
<th>{% trans 'Quantidade de diárias da missão' %}</th>
<td class="right-align">{{ total_equipe.tot_qtde_diarias|default:"-" }}</td>
</tr>
<tr>
<th>C</th>
<th>{% trans 'Total gasto com diárias' %}</th>
<td class="right-align">{{ total_equipe.tot_valor_diarias|default:"-" }}</td>
</tr>
<tr>
<th>D</th>
<th>{% trans 'Total gasto com passagens' %}</th>
<td class="right-align">{{ total_equipe.tot_passagens|default:"-" }}</td>
</tr>
<tr>
<th>E</th>
<th>{% trans 'Custo total do evento [ C + D ]' %}</th>
<td class="right-align bold">{{ total_equipe.tot_gastos|default:"-" }}</td>
</tr>
<tr>
<th colspan="3">{% trans "Médias" %}</th>
</tr>
<tr>
<th>F</th>
<th>{% trans 'Média de diárias por membro [ B / A ]' %}</th>
<td class="right-align">{{ total_equipe.media_qtde_diarias|default:"-" }}</td>
</tr>
<tr>
<th>G</th>
<th>{% trans 'Valor médio das diárias da equipe [ C / B ]' %}</th>
<td class="right-align">{{ total_equipe.media_diarias|default:"-" }}</td>
</tr>
<tr>
<th>H</th>
<th>{% trans 'Valor médio das passagens da equipe [ D / A ]' %}</th>
<td class="right-align">{{ total_equipe.media_passagens|default:"-" }}</td>
</tr>
<tr>
<th>I</th>
<th>{% trans 'Antecedência média na aquisição das passagens, em dias/fração **' %}</th>
<td class="right-align">{{ total_equipe.media_antecedencia|default:"-" }}</td>
</tr>
</table>
<aside>** Somatória da coluna 'Dias de antecedência' do quadro Equipe participante dividida pelo número de membros da equipe [A]</aside>
{% endblock %}

4
sigi/apps/utils/templatetags/sigi_tags.py

@ -34,5 +34,5 @@ def sum(value, arg):
@register.simple_tag
def setvar(val=None):
return val
def multiply(value, arg):
return value * arg

5
sigi/templates/pdf/base_report.html

@ -33,7 +33,7 @@ footer img {
}
table {
border: 2px white;
border: 2px solid white;
width: 100%;
}
th,td {
@ -58,6 +58,9 @@ tr:nth-child(even) {
.right-align {
text-align: right;
}
.bold {
font-weight: bold;
}
ul {
list-style-type: none;
margin: 0px;

Loading…
Cancel
Save