modified: routes_admin.py
new file: templates/admin_report_comisiones.html modified: templates/macros/modals.html
This commit is contained in:
@@ -516,4 +516,83 @@ def register_admin_routes(app):
|
||||
dias_en_periodo=dias_en_periodo,
|
||||
data_por_dia=data_por_dia,
|
||||
totales_mes=totales_mes,
|
||||
dias_activos=dias_activos)
|
||||
dias_activos=dias_activos)
|
||||
|
||||
@app.route('/admin/reportes/modulo/<int:modulo_id>/comisiones')
|
||||
@admin_required
|
||||
def report_modulo_comisiones(modulo_id):
|
||||
mes_actual = date.today().month
|
||||
anio_actual = date.today().year
|
||||
|
||||
conn = get_db_connection()
|
||||
c = conn.cursor()
|
||||
|
||||
c.execute("SELECT name FROM modulos WHERE id = ?", (modulo_id,))
|
||||
modulo_info = c.fetchone()
|
||||
if not modulo_info:
|
||||
conn.close()
|
||||
flash("Módulo no encontrado.", "danger")
|
||||
return redirect(url_for('admin_reportes_index'))
|
||||
modulo_name = modulo_info[0]
|
||||
|
||||
# Fetch rendiciones with commission calculations for this module and month
|
||||
c.execute('''
|
||||
SELECT r.id, strftime('%d', r.fecha) as dia,
|
||||
w.id, w.name, w.tipo, r.worker_comision,
|
||||
cw.id, cw.name, cw.tipo, r.companion_comision,
|
||||
COALESCE((SELECT SUM(cantidad * comision_historica) FROM rendicion_items WHERE rendicion_id = r.id), 0) as total_comision
|
||||
FROM rendiciones r
|
||||
JOIN workers w ON r.worker_id = w.id
|
||||
LEFT JOIN workers cw ON r.companion_id = cw.id
|
||||
WHERE r.modulo_id = ? AND strftime('%m', r.fecha) = ? AND strftime('%Y', r.fecha) = ?
|
||||
ORDER BY r.fecha ASC
|
||||
''', (modulo_id, f'{mes_actual:02}', str(anio_actual)))
|
||||
|
||||
rendiciones = c.fetchall()
|
||||
conn.close()
|
||||
|
||||
workers_data = {}
|
||||
|
||||
for row in rendiciones:
|
||||
r_id, dia, w_id, w_name, w_tipo, w_com, c_id, c_name, c_tipo, c_com, total_com = row
|
||||
|
||||
w_share = 0
|
||||
c_share = 0
|
||||
|
||||
# Split logic
|
||||
if w_com and c_com:
|
||||
w_share = total_com / 2
|
||||
c_share = total_com / 2
|
||||
elif w_com:
|
||||
w_share = total_com
|
||||
elif c_com:
|
||||
c_share = total_com
|
||||
|
||||
# Process Titular Worker
|
||||
if w_id not in workers_data:
|
||||
workers_data[w_id] = {'name': w_name, 'tipo': w_tipo, 'dias': {}, 'total': 0, 'enabled': bool(w_com)}
|
||||
else:
|
||||
if w_com: workers_data[w_id]['enabled'] = True
|
||||
|
||||
workers_data[w_id]['dias'][dia] = workers_data[w_id]['dias'].get(dia, 0) + w_share
|
||||
workers_data[w_id]['total'] += w_share
|
||||
|
||||
# Process Companion (if any)
|
||||
if c_id:
|
||||
if c_id not in workers_data:
|
||||
workers_data[c_id] = {'name': c_name, 'tipo': c_tipo, 'dias': {}, 'total': 0, 'enabled': bool(c_com)}
|
||||
else:
|
||||
if c_com: workers_data[c_id]['enabled'] = True
|
||||
|
||||
workers_data[c_id]['dias'][dia] = workers_data[c_id]['dias'].get(dia, 0) + c_share
|
||||
workers_data[c_id]['total'] += c_share
|
||||
|
||||
# Sort alphabetically so the table doesn't shuffle randomly
|
||||
workers_data = dict(sorted(workers_data.items(), key=lambda item: item[1]['name']))
|
||||
dias_en_periodo = [f'{d:02}' for d in range(1, 32)]
|
||||
|
||||
return render_template('admin_report_comisiones.html',
|
||||
modulo_name=modulo_name,
|
||||
mes_nombre=f'{mes_actual:02}/{anio_actual}',
|
||||
workers_data=workers_data,
|
||||
dias_en_periodo=dias_en_periodo)
|
||||
85
templates/admin_report_comisiones.html
Normal file
85
templates/admin_report_comisiones.html
Normal file
@@ -0,0 +1,85 @@
|
||||
{% extends "macros/base.html" %}
|
||||
|
||||
{% block title %}Reporte: Comisiones - {{ modulo_name }}{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<style>
|
||||
.numeric-cell {
|
||||
text-align: right;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
.sticky-col {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
background-color: #f8f9fa !important;
|
||||
border-right: 2px solid #dee2e6;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<a href="{{ url_for('admin_reportes_index') }}" class="btn btn-outline-secondary btn-sm mb-2">
|
||||
<i class="bi bi-arrow-left"></i> Volver al Menú
|
||||
</a>
|
||||
<h2>Reporte de Comisiones</h2>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<div><strong class="text-primary fs-5">{{ modulo_name }}</strong></div>
|
||||
<div class="text-muted"><small>Período: {{ mes_nombre }}</small></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
{% if workers_data %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-hover table-sm mb-0 text-nowrap">
|
||||
<thead class="table-dark text-center align-middle">
|
||||
<tr>
|
||||
<th class="sticky-col py-2" style="width: 80px;">Día</th>
|
||||
{% for w_id, data in workers_data.items() %}
|
||||
<th class="py-2">
|
||||
{{ data.name }}<br>
|
||||
<span class="badge {% if data.tipo == 'Full Time' %}bg-success{% else %}bg-secondary{% endif %} fw-normal mt-1">{{ data.tipo }}</span>
|
||||
<br>
|
||||
<small class="{% if data.enabled %}text-info{% else %}text-danger{% endif %} fw-normal" style="font-size: 0.75em;">
|
||||
{% if data.enabled %}(Comisión Activa){% else %}(Sin Comisión){% endif %}
|
||||
</small>
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for dia in dias_en_periodo %}
|
||||
<tr>
|
||||
<td class="align-middle sticky-col numeric-cell fw-bold text-center">{{ dia }}</td>
|
||||
{% for w_id, data in workers_data.items() %}
|
||||
{% set comision = data.dias.get(dia, 0) %}
|
||||
<td class="align-middle numeric-cell {% if comision > 0 %}text-success fw-bold{% else %}text-muted{% endif %}">
|
||||
{{ ("$" ~ "{:,.0f}".format(comision).replace(',', '.')) if comision > 0 else "-" }}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot class="table-group-divider fw-bold bg-dark text-white sticky-bottom">
|
||||
<tr>
|
||||
<td class="align-middle sticky-col py-2 text-center">TOTAL</td>
|
||||
{% for w_id, data in workers_data.items() %}
|
||||
<td class="align-middle numeric-cell py-2 text-success fs-6">
|
||||
${{ "{:,.0f}".format(data.total).replace(',', '.') }}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info text-center mb-0">No hay trabajadores con rendiciones registradas para este módulo en el período actual.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -482,7 +482,7 @@
|
||||
<h5 class="card-title mb-0">Comisiones</h5>
|
||||
</div>
|
||||
<p class="text-muted small flex-grow-1">Cálculo de comisiones generadas por los trabajadores en este módulo.</p>
|
||||
<button class="btn btn-outline-success w-100 mt-3" onclick="alert('Reporte en construcción')">Generar Reporte</button>
|
||||
<a href="{{ url_for('report_modulo_comisiones', modulo_id=modulo_id) }}" class="btn btn-outline-success w-100 mt-3">Generar Reporte</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user