Added new reports
This commit is contained in:
157
routes_admin.py
157
routes_admin.py
@@ -761,3 +761,160 @@ def register_admin_routes(app):
|
||||
mes_nombre=f'{mes_actual:02}/{anio_actual}',
|
||||
workers_data=workers_data,
|
||||
dias_en_periodo=dias_en_periodo)
|
||||
|
||||
|
||||
@app.route('/admin/reportes/modulo/<int:modulo_id>/centros_comerciales')
|
||||
@admin_required
|
||||
def report_modulo_centros_comerciales(modulo_id):
|
||||
import calendar
|
||||
from datetime import date
|
||||
|
||||
mes_actual = date.today().month
|
||||
anio_actual = date.today().year
|
||||
|
||||
# Obtenemos la cantidad real de días del mes
|
||||
_, num_dias = calendar.monthrange(anio_actual, mes_actual)
|
||||
|
||||
dias_en_periodo = []
|
||||
for d in range(1, num_dias + 1):
|
||||
dia_semana = date(anio_actual, mes_actual, d).weekday()
|
||||
dias_en_periodo.append({
|
||||
'num': f'{d:02}',
|
||||
'name': ['L', 'M', 'M', 'J', 'V', 'S', 'D'][dia_semana]
|
||||
})
|
||||
|
||||
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]
|
||||
|
||||
c.execute('''
|
||||
SELECT strftime('%d', r.fecha) as dia,
|
||||
SUM(r.boletas_debito + r.boletas_credito + r.boletas_mp) as trans_red_compra,
|
||||
SUM(r.boletas_efectivo) as boletas_efectivo,
|
||||
SUM(r.venta_debito + r.venta_credito + r.venta_mp + r.venta_efectivo) as venta_total
|
||||
FROM rendiciones r
|
||||
WHERE r.modulo_id = ? AND strftime('%m', r.fecha) = ? AND strftime('%Y', r.fecha) = ?
|
||||
GROUP BY dia
|
||||
''', (modulo_id, f'{mes_actual:02}', str(anio_actual)))
|
||||
|
||||
resultados = c.fetchall()
|
||||
conn.close()
|
||||
|
||||
data_por_dia = {f'{d:02}': {'red_compra': 0, 'efectivo': 0, 'total_trans': 0, 'venta_neta': 0} for d in range(1, num_dias + 1)}
|
||||
totales = {'red_compra': 0, 'efectivo': 0, 'total_trans': 0, 'venta_neta': 0}
|
||||
|
||||
for row in resultados:
|
||||
dia, red_compra, efectivo, venta_total = row
|
||||
if dia not in data_por_dia:
|
||||
continue
|
||||
|
||||
red_compra = red_compra or 0
|
||||
efectivo = efectivo or 0
|
||||
venta_total = venta_total or 0
|
||||
|
||||
total_trans = red_compra + efectivo
|
||||
venta_neta = round(venta_total / 1.19)
|
||||
|
||||
data_por_dia[dia] = {
|
||||
'red_compra': red_compra,
|
||||
'efectivo': efectivo,
|
||||
'total_trans': total_trans,
|
||||
'venta_neta': venta_neta
|
||||
}
|
||||
|
||||
totales['red_compra'] += red_compra
|
||||
totales['efectivo'] += efectivo
|
||||
totales['total_trans'] += total_trans
|
||||
totales['venta_neta'] += venta_neta
|
||||
|
||||
return render_template('admin_report_cc.html',
|
||||
modulo_name=modulo_name,
|
||||
mes_nombre=f'{mes_actual:02}/{anio_actual}',
|
||||
dias_en_periodo=dias_en_periodo,
|
||||
data_por_dia=data_por_dia,
|
||||
totales=totales)
|
||||
|
||||
@app.route('/admin/reportes/modulo/<int:modulo_id>/calculo_iva')
|
||||
@admin_required
|
||||
def report_modulo_calculo_iva(modulo_id):
|
||||
import calendar
|
||||
from datetime import date
|
||||
|
||||
mes_actual = date.today().month
|
||||
anio_actual = date.today().year
|
||||
|
||||
_, num_dias = calendar.monthrange(anio_actual, mes_actual)
|
||||
|
||||
dias_en_periodo = []
|
||||
for d in range(1, num_dias + 1):
|
||||
dia_semana = date(anio_actual, mes_actual, d).weekday()
|
||||
dias_en_periodo.append({
|
||||
'num': f'{d:02}',
|
||||
'name': ['L', 'M', 'M', 'J', 'V', 'S', 'D'][dia_semana]
|
||||
})
|
||||
|
||||
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]
|
||||
|
||||
c.execute('''
|
||||
SELECT strftime('%d', r.fecha) as dia,
|
||||
SUM(r.venta_efectivo) as venta_efectivo,
|
||||
SUM(r.venta_debito + r.venta_credito + r.venta_mp) as venta_tbk
|
||||
FROM rendiciones r
|
||||
WHERE r.modulo_id = ? AND strftime('%m', r.fecha) = ? AND strftime('%Y', r.fecha) = ?
|
||||
GROUP BY dia
|
||||
''', (modulo_id, f'{mes_actual:02}', str(anio_actual)))
|
||||
|
||||
resultados = c.fetchall()
|
||||
conn.close()
|
||||
|
||||
data_por_dia = {f'{d:02}': {'efectivo': 0, 'tbk': 0, 'total': 0, 'porcentaje': 0} for d in range(1, num_dias + 1)}
|
||||
totales = {'efectivo': 0, 'tbk': 0, 'total': 0, 'porcentaje': 0}
|
||||
|
||||
for row in resultados:
|
||||
dia, efectivo, tbk = row
|
||||
if dia not in data_por_dia:
|
||||
continue
|
||||
|
||||
efectivo = efectivo or 0
|
||||
tbk = tbk or 0
|
||||
total = efectivo + tbk
|
||||
|
||||
# Evitar dividir por cero cuando no se vende nada en el día
|
||||
porcentaje = round((efectivo / total) * 100) if total > 0 else 0
|
||||
|
||||
data_por_dia[dia] = {
|
||||
'efectivo': efectivo,
|
||||
'tbk': tbk,
|
||||
'total': total,
|
||||
'porcentaje': porcentaje
|
||||
}
|
||||
|
||||
totales['efectivo'] += efectivo
|
||||
totales['tbk'] += tbk
|
||||
totales['total'] += total
|
||||
|
||||
if totales['total'] > 0:
|
||||
totales['porcentaje'] = round((totales['efectivo'] / totales['total']) * 100)
|
||||
|
||||
return render_template('admin_report_iva.html',
|
||||
modulo_name=modulo_name,
|
||||
mes_nombre=f'{mes_actual:02}/{anio_actual}',
|
||||
dias_en_periodo=dias_en_periodo,
|
||||
data_por_dia=data_por_dia,
|
||||
totales=totales)
|
||||
96
templates/admin_report_cc.html
Normal file
96
templates/admin_report_cc.html
Normal file
@@ -0,0 +1,96 @@
|
||||
{% extends "macros/base.html" %}
|
||||
|
||||
{% block title %}Reporte: Centros Comerciales - {{ modulo_name }}{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<style>
|
||||
.table-container {
|
||||
max-height: 75vh;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.sticky-col {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
background-color: var(--bs-body-bg);
|
||||
border-right: 2px solid var(--bs-border-color) !important;
|
||||
}
|
||||
|
||||
thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background-color: var(--bs-body-bg);
|
||||
box-shadow: inset 0 -1px 0 var(--bs-border-color);
|
||||
}
|
||||
|
||||
thead th.sticky-col {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.numeric-cell {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
</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>Registro Centros Comerciales</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 p-0">
|
||||
<div class="table-responsive table-container custom-scrollbar">
|
||||
<table class="table table-bordered table-hover table-sm mb-0 text-center text-nowrap align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sticky-col py-3 align-middle bg-dark text-white" style="width: 10%;">FECHA</th>
|
||||
<th class="py-3 bg-info text-dark" style="width: 20%;">Nº TRANSACCION<br>RED COMPRA</th>
|
||||
<th class="py-3 bg-warning text-dark" style="width: 20%;">Nº DE BOLETAS<br>EFECTIVO</th>
|
||||
<th class="py-3 bg-primary text-white" style="width: 20%;">TOTAL<br>TRANSACCIONES</th>
|
||||
<th class="py-3 bg-success text-white text-end pe-4" style="width: 30%;">VENTA NETA</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for dia in dias_en_periodo %}
|
||||
{% set d = data_por_dia[dia.num] %}
|
||||
<tr>
|
||||
<td class="fw-bold sticky-col p-0 align-middle">
|
||||
<div class="d-flex justify-content-between align-items-center px-2 py-1 h-100">
|
||||
<span class="text-muted" style="font-weight: 500;">{{ dia.name }}</span>
|
||||
<span>{{ dia.num }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="numeric-cell bg-info-subtle text-info fw-bold">{{ d.red_compra }}</td>
|
||||
<td class="numeric-cell bg-warning-subtle text-warning fw-bold">{{ d.efectivo }}</td>
|
||||
<td class="numeric-cell bg-primary-subtle text-primary fw-bold">{{ d.total_trans }}</td>
|
||||
<td class="numeric-cell bg-success-subtle text-success fw-bold text-end pe-4">${{ "{:,.0f}".format(d.venta_neta).replace(',', '.') }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot class="fw-bold fs-6">
|
||||
<tr>
|
||||
<td class="sticky-col text-center py-2 bg-dark text-white">TOTALES</td>
|
||||
<td class="numeric-cell py-2 bg-info text-dark">{{ totales.red_compra }}</td>
|
||||
<td class="numeric-cell py-2 bg-warning text-dark">{{ totales.efectivo }}</td>
|
||||
<td class="numeric-cell py-2 bg-primary text-white">{{ totales.total_trans }}</td>
|
||||
<td class="numeric-cell py-2 bg-success text-white text-end pe-4">${{ "{:,.0f}".format(totales.venta_neta).replace(',', '.') }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
96
templates/admin_report_iva.html
Normal file
96
templates/admin_report_iva.html
Normal file
@@ -0,0 +1,96 @@
|
||||
{% extends "macros/base.html" %}
|
||||
|
||||
{% block title %}Reporte: Cálculo de IVA - {{ modulo_name }}{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<style>
|
||||
.table-container {
|
||||
max-height: 75vh;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.sticky-col {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
background-color: var(--bs-body-bg);
|
||||
border-right: 2px solid var(--bs-border-color) !important;
|
||||
}
|
||||
|
||||
thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background-color: var(--bs-body-bg);
|
||||
box-shadow: inset 0 -1px 0 var(--bs-border-color);
|
||||
}
|
||||
|
||||
thead th.sticky-col {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.numeric-cell {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
</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>Cálculo de IVA</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 p-0">
|
||||
<div class="table-responsive table-container custom-scrollbar">
|
||||
<table class="table table-bordered table-hover table-sm mb-0 text-center text-nowrap align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sticky-col py-3 align-middle bg-dark text-white" style="width: 10%;">FECHA</th>
|
||||
<th class="py-3 bg-success text-white" style="width: 25%;">VENTA EFECTIVO</th>
|
||||
<th class="py-3 bg-info text-dark" style="width: 25%;">VENTA TBK</th>
|
||||
<th class="py-3 bg-primary text-white" style="width: 25%;">VENTA TOTAL</th>
|
||||
<th class="py-3 bg-warning text-dark text-end pe-4" style="width: 15%;">% PROMEDIO VTAS<br>EN EFECTIVO</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for dia in dias_en_periodo %}
|
||||
{% set d = data_por_dia[dia.num] %}
|
||||
<tr>
|
||||
<td class="fw-bold sticky-col p-0 align-middle">
|
||||
<div class="d-flex justify-content-between align-items-center px-2 py-1 h-100">
|
||||
<span class="text-muted" style="font-weight: 500;">{{ dia.name }}</span>
|
||||
<span>{{ dia.num }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="numeric-cell bg-success-subtle text-success fw-bold">${{ "{:,.0f}".format(d.efectivo).replace(',', '.') }}</td>
|
||||
<td class="numeric-cell bg-info-subtle text-info fw-bold">${{ "{:,.0f}".format(d.tbk).replace(',', '.') }}</td>
|
||||
<td class="numeric-cell bg-primary-subtle text-primary fw-bold">${{ "{:,.0f}".format(d.total).replace(',', '.') }}</td>
|
||||
<td class="numeric-cell bg-warning-subtle text-warning fw-bold text-end pe-4">{{ d.porcentaje }}%</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot class="fw-bold fs-6">
|
||||
<tr>
|
||||
<td class="sticky-col text-center py-2 bg-dark text-white">TOTALES</td>
|
||||
<td class="numeric-cell py-2 bg-success text-white">${{ "{:,.0f}".format(totales.efectivo).replace(',', '.') }}</td>
|
||||
<td class="numeric-cell py-2 bg-info text-dark">${{ "{:,.0f}".format(totales.tbk).replace(',', '.') }}</td>
|
||||
<td class="numeric-cell py-2 bg-primary text-white">${{ "{:,.0f}".format(totales.total).replace(',', '.') }}</td>
|
||||
<td class="numeric-cell py-2 bg-warning text-dark text-end pe-4">{{ totales.porcentaje }}%</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -486,7 +486,7 @@
|
||||
<h5 class="card-title mb-0">Detalle de Ventas</h5>
|
||||
</div>
|
||||
<p class="text-muted small flex-grow-1">Análisis detallado de ventas diarias, productos vendidos y consolidado mensual.</p>
|
||||
<a href="{{ url_for('report_modulo_periodo', modulo_id=modulo_id) }}" class="btn btn-primary w-100 mt-3">Generar Reporte</a>
|
||||
<a href="{{ url_for('report_modulo_periodo', modulo_id=modulo_id) }}" class="btn btn-outline-primary w-100 mt-3">Generar Reporte</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -501,7 +501,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>
|
||||
<a href="{{ url_for('report_modulo_comisiones', modulo_id=modulo_id) }}" class="btn btn-success w-100 mt-3">Generar Reporte</a>
|
||||
<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>
|
||||
@@ -516,7 +516,7 @@
|
||||
<h5 class="card-title mb-0">Control de Horarios</h5>
|
||||
</div>
|
||||
<p class="text-muted small flex-grow-1">Registro de horas de entrada y salida de los trabajadores y acompañantes.</p>
|
||||
<a href="{{ url_for('report_modulo_horarios', modulo_id=modulo_id) }}" class="btn btn-warning w-100 mt-3">Generar Reporte</a>
|
||||
<a href="{{ url_for('report_modulo_horarios', modulo_id=modulo_id) }}" class="btn btn-outline-warning w-100 mt-3">Generar Reporte</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -531,7 +531,7 @@
|
||||
<h5 class="card-title mb-0">Centros Comerciales</h5>
|
||||
</div>
|
||||
<p class="text-muted small flex-grow-1">Reporte de datos exigidos por la administración del centro comercial.</p>
|
||||
<button class="btn btn-outline-info w-100 mt-3" onclick="alert('Reporte en construcción')">Generar Reporte</button>
|
||||
<a href="{{ url_for('report_modulo_centros_comerciales', modulo_id=modulo_id) }}" class="btn btn-outline-info w-100 mt-3">Generar Reporte</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -546,7 +546,7 @@
|
||||
<h5 class="card-title mb-0">Cálculo de IVA</h5>
|
||||
</div>
|
||||
<p class="text-muted small flex-grow-1">Proyección de impuestos basados en las ventas declaradas.</p>
|
||||
<button class="btn btn-outline-secondary w-100 mt-3" onclick="alert('Reporte en construcción')">Generar Reporte</button>
|
||||
<a href="{{ url_for('report_modulo_calculo_iva', modulo_id=modulo_id) }}" class="btn btn-outline-secondary w-100 mt-3">Generar Reporte</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user