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

317
sigi/apps/eventos/admin.py

@ -1,9 +1,24 @@
import datetime import datetime
import pandas as pd
import time import time
from moodle import Moodle from moodle import Moodle
from typing import Any from typing import Any
from django.db import models 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.conf import settings
from django.contrib import admin, messages from django.contrib import admin, messages
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -38,10 +53,12 @@ from sigi.apps.eventos.models import (
Anexo, Anexo,
) )
from sigi.apps.eventos.forms import EventoAdminForm, SelecionaModeloForm 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 import abreviatura
from sigi.apps.utils.filters import DateRangeFilter from sigi.apps.utils.filters import DateRangeFilter
from sigi.apps.utils.mixins import ( from sigi.apps.utils.mixins import (
CartExportMixin, CartExportMixin,
CartExportReportMixin,
LabeledResourse, LabeledResourse,
ValueLabeledResource, ValueLabeledResource,
) )
@ -833,7 +850,7 @@ class ModeloDeclaracaoAdmin(admin.ModelAdmin):
@admin.register(Evento) @admin.register(Evento)
class EventoAdmin(CartExportMixin, admin.ModelAdmin): class EventoAdmin(CartExportReportMixin, admin.ModelAdmin):
form = EventoAdminForm form = EventoAdminForm
resource_class = EventoResource resource_class = EventoResource
fieldsets = ( fieldsets = (
@ -951,6 +968,7 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin):
CronogramaInline, CronogramaInline,
) )
save_as = True save_as = True
reports = ["custos_eventos_report", "custos_servidor_report"]
@admin.display(description=_("banner")) @admin.display(description=_("banner"))
def get_banner(self, obj): def get_banner(self, obj):
@ -1071,6 +1089,11 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin):
self.admin_site.admin_view(self.update_participantes), self.admin_site.admin_view(self.update_participantes),
name="%s_%s_updateparticipantes" % model_info, 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 return my_urls + urls
@ -1283,6 +1306,296 @@ class EventoAdmin(CartExportMixin, admin.ModelAdmin):
content_type="application/pdf", 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): def create_course(self, request, object_id):
evento = get_object_or_404(Evento, id=object_id) evento = get_object_or_404(Evento, id=object_id)
change_url = ( 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_oficio = models.BooleanField(
_("Assina ofício de comparecimento"), default=False _("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) observacoes = models.TextField(_("Observações"), blank=True)
class Meta: 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> </a>
</li> </li>
{% endif %} {% 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> <li>
{% url opts|admin_urlname:'declaracaoreport' object_id|admin_urlquote as tool_url %} {% url opts|admin_urlname:'declaracaoreport' object_id|admin_urlquote as tool_url %}
<a href="{% add_preserved_filters 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 @register.simple_tag
def setvar(val=None): def multiply(value, arg):
return val return value * arg

5
sigi/templates/pdf/base_report.html

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

Loading…
Cancel
Save