universal filters v1 + more sample data
This commit is contained in:
124
database.py
124
database.py
@@ -1,4 +1,6 @@
|
||||
import sqlite3
|
||||
import datetime
|
||||
import random
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
DB_NAME = "db/rendiciones.db"
|
||||
@@ -7,13 +9,19 @@ def get_db_connection():
|
||||
return sqlite3.connect(DB_NAME)
|
||||
|
||||
def populateDefaults():
|
||||
random.seed(42)
|
||||
conn = get_db_connection()
|
||||
c = conn.cursor()
|
||||
|
||||
c.execute("SELECT COUNT(*) FROM zonas")
|
||||
if c.fetchone()[0] == 0:
|
||||
zonas = ['Norte', 'Quinta', 'RM', 'Sur']
|
||||
for zona in zonas:
|
||||
c.execute("INSERT INTO zonas (name) VALUES (?)", (zona,))
|
||||
conn.commit()
|
||||
|
||||
c.execute("SELECT COUNT(*) FROM modulos")
|
||||
if c.fetchone()[0] == 0:
|
||||
c.execute("SELECT id, name FROM zonas")
|
||||
zona_map = {name: id for id, name in c.fetchall()}
|
||||
modulos_data = [
|
||||
@@ -30,7 +38,7 @@ def populateDefaults():
|
||||
|
||||
c.execute("SELECT COUNT(*) FROM productos")
|
||||
if c.fetchone()[0] == 0:
|
||||
productos_data = [
|
||||
productos_base = [
|
||||
('PACK LENTES DE SOL 1 x', 12990, 200),
|
||||
('PACK LENTES DE PANTALLA', 12990, 200),
|
||||
('PACK LENTES DE SOL 2 x', 19990, 400),
|
||||
@@ -64,59 +72,115 @@ def populateDefaults():
|
||||
c.execute("SELECT id FROM zonas")
|
||||
zonas_ids = [row[0] for row in c.fetchall()]
|
||||
for zona_id in zonas_ids:
|
||||
for name, price, commission in productos_data:
|
||||
for name, price, commission in productos_base:
|
||||
c.execute("INSERT INTO productos (zona_id, name, price, commission) VALUES (?, ?, ?, ?)",
|
||||
(zona_id, name, price, commission))
|
||||
conn.commit()
|
||||
|
||||
c.execute("SELECT COUNT(*) FROM workers WHERE is_admin = 0")
|
||||
if c.fetchone()[0] == 0:
|
||||
c.execute("SELECT id FROM modulos LIMIT 2")
|
||||
modulos_ids = c.fetchall()
|
||||
if len(modulos_ids) >= 2:
|
||||
mod_1 = modulos_ids[0][0]
|
||||
mod_2 = modulos_ids[1][0]
|
||||
c.execute("SELECT id FROM modulos")
|
||||
mod_ids = [row[0] for row in c.fetchall()]
|
||||
default_pass = generate_password_hash("123456")
|
||||
|
||||
workers_data = [
|
||||
("11.111.111-1", "Juan Perez", "+56 9 1111 1111", default_pass, 0, mod_1, "Full Time"),
|
||||
("22.222.222-2", "Maria Gonzalez", "+56 9 2222 2222", default_pass, 0, mod_1, "Part Time"),
|
||||
("33.333.333-3", "Pedro Soto", "+56 9 3333 3333", default_pass, 0, mod_2, "Full Time"),
|
||||
("44.444.444-4", "Ana Silva", "+56 9 4444 4444", default_pass, 0, mod_2, "Part Time")
|
||||
("11.111.111-1", "Juan Perez", "+56 9 1111 1111", default_pass, 0, mod_ids[0], "Full Time"),
|
||||
("22.222.222-2", "Maria Gonzalez", "+56 9 2222 2222", default_pass, 0, mod_ids[0], "Part Time"),
|
||||
("33.333.333-3", "Pedro Soto", "+56 9 3333 3333", default_pass, 0, mod_ids[1], "Full Time"),
|
||||
("44.444.444-4", "Ana Silva", "+56 9 4444 4444", default_pass, 0, mod_ids[1], "Part Time"),
|
||||
("55.555.555-5", "Diego Lopez", "+56 9 5555 5555", default_pass, 0, mod_ids[2], "Full Time"),
|
||||
("66.666.666-6", "Claudia Jara", "+56 9 6666 6666", default_pass, 0, mod_ids[2], "Full Time"),
|
||||
("77.777.777-7", "Roberto Munoz", "+56 9 7777 7777", default_pass, 0, mod_ids[3], "Part Time"),
|
||||
("88.888.888-8", "Elena Espinoza", "+56 9 8888 8888", default_pass, 0, mod_ids[3], "Part Time"),
|
||||
("99.999.999-9", "Mauricio Rivas", "+56 9 9999 9999", default_pass, 0, mod_ids[4], "Full Time"),
|
||||
("10.101.101-0", "Sofia Castro", "+56 9 1010 1010", default_pass, 0, mod_ids[4], "Part Time")
|
||||
]
|
||||
for w in workers_data:
|
||||
c.execute("INSERT OR IGNORE INTO workers (rut, name, phone, password_hash, is_admin, modulo_id, tipo) VALUES (?, ?, ?, ?, ?, ?, ?)", w)
|
||||
conn.commit()
|
||||
# Generar Rendiciones Placeholder
|
||||
|
||||
c.execute("SELECT COUNT(*) FROM rendiciones")
|
||||
if c.fetchone()[0] == 0:
|
||||
import datetime
|
||||
# 1. Obtener datos necesarios para las llaves foráneas
|
||||
c.execute("SELECT id, modulo_id FROM workers WHERE is_admin = 0")
|
||||
c.execute("SELECT id, modulo_id, tipo FROM workers WHERE is_admin = 0")
|
||||
workers_list = c.fetchall()
|
||||
|
||||
# 2. Insertar una rendición por trabajador
|
||||
for w_id, m_id in workers_list:
|
||||
fecha_ejemplo = (datetime.date.today() - datetime.timedelta(days=1)).isoformat()
|
||||
hoy = datetime.date.today()
|
||||
dias_totales = 365 * 2 + hoy.timetuple().tm_yday
|
||||
|
||||
for i in range(dias_totales):
|
||||
fecha_actual = (hoy - datetime.timedelta(days=i))
|
||||
fecha_str = fecha_actual.isoformat()
|
||||
|
||||
num_modulos_hoy = random.randint(3, 6)
|
||||
mods_activos = random.sample(range(len(modulos_data)), k=num_modulos_hoy)
|
||||
|
||||
for mod_idx in mods_activos:
|
||||
target_mod_id = mod_idx + 1
|
||||
workers_in_mod = [w for w in workers_list if w[1] == target_mod_id]
|
||||
|
||||
if not workers_in_mod:
|
||||
continue
|
||||
|
||||
main_worker = random.choice(workers_in_mod)
|
||||
w_id, m_id, w_tipo = main_worker
|
||||
|
||||
w_comision = 1
|
||||
if w_tipo == "Part Time":
|
||||
w_comision = random.choice([0, 1])
|
||||
|
||||
companion_id = None
|
||||
comp_hora_ent = None
|
||||
comp_hora_sal = None
|
||||
comp_comision = 0
|
||||
|
||||
other_workers_in_mod = [w for w in workers_in_mod if w[0] != w_id]
|
||||
if other_workers_in_mod and random.random() < 0.3:
|
||||
companion = random.choice(other_workers_in_mod)
|
||||
companion_id = companion[0]
|
||||
comp_hora_ent = "11:00"
|
||||
comp_hora_sal = "19:00"
|
||||
comp_comision = random.choice([0, 1])
|
||||
|
||||
v_debito = random.randint(15000, 80000)
|
||||
v_credito = random.randint(10000, 120000)
|
||||
v_efectivo = random.randint(5000, 50000)
|
||||
|
||||
b_debito = max(1, v_debito // 15000)
|
||||
b_credito = max(1, v_credito // 15000)
|
||||
b_efectivo = max(1, v_efectivo // 15000)
|
||||
|
||||
gastos = random.choice([0, 0, 0, 2000, 5000])
|
||||
|
||||
c.execute('''INSERT INTO rendiciones
|
||||
(worker_id, worker_comision, modulo_id, fecha, hora_entrada, hora_salida,
|
||||
venta_debito, venta_credito, venta_mp, venta_efectivo, gastos, observaciones)
|
||||
VALUES (?, 1, ?, ?, '09:00', '21:00', ?, ?, ?, ?, 0, 'Rendición de prueba')''',
|
||||
(w_id, m_id, fecha_ejemplo, 10000, 20000, 5000, 15000))
|
||||
(worker_id, worker_comision, companion_id, companion_hora_entrada, companion_hora_salida,
|
||||
companion_comision, modulo_id, fecha, hora_entrada, hora_salida,
|
||||
venta_debito, venta_credito, venta_efectivo,
|
||||
boletas_debito, boletas_credito, boletas_efectivo,
|
||||
gastos, observaciones)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, '09:00', '21:00', ?, ?, ?, ?, ?, ?, ?, ?)''',
|
||||
(w_id, w_comision, companion_id, comp_hora_ent, comp_hora_sal,
|
||||
comp_comision, m_id, fecha_str, v_debito, v_credito, v_efectivo,
|
||||
b_debito, b_credito, b_efectivo,
|
||||
gastos, f"Carga histórica {fecha_str}"))
|
||||
|
||||
rendicion_id = c.lastrowid
|
||||
rend_id = c.lastrowid
|
||||
|
||||
# 3. Añadir productos a esa rendición (Rendicion_items)
|
||||
# Obtenemos productos de cualquier zona para el ejemplo
|
||||
c.execute("SELECT id, price, commission FROM productos LIMIT 3")
|
||||
prods = c.fetchall()
|
||||
c.execute("SELECT id, price, commission FROM productos WHERE zona_id = (SELECT zona_id FROM modulos WHERE id = ?)", (m_id,))
|
||||
prods_zona = c.fetchall()
|
||||
|
||||
for p_id, p_price, p_comm in prods:
|
||||
if prods_zona:
|
||||
items_hoy = random.sample(prods_zona, k=min(len(prods_zona), random.randint(2, 6)))
|
||||
for p_id, p_price, p_comm in items_hoy:
|
||||
c.execute('''INSERT INTO rendicion_items
|
||||
(rendicion_id, producto_id, cantidad, precio_historico, comision_historica)
|
||||
VALUES (?, ?, ?, ?, ?)''',
|
||||
(rendicion_id, p_id, 2, p_price, p_comm))
|
||||
(rend_id, p_id, random.randint(1, 4), p_price, p_comm))
|
||||
|
||||
if i % 100 == 0:
|
||||
conn.commit()
|
||||
|
||||
conn.commit()
|
||||
|
||||
conn.close()
|
||||
|
||||
def init_db():
|
||||
@@ -181,11 +245,13 @@ def init_db():
|
||||
comision_historica INTEGER NOT NULL,
|
||||
FOREIGN KEY (rendicion_id) REFERENCES rendiciones(id),
|
||||
FOREIGN KEY (producto_id) REFERENCES productos(id))''')
|
||||
|
||||
c.execute("SELECT id FROM workers WHERE is_admin = 1")
|
||||
if not c.fetchone():
|
||||
admin_pass = generate_password_hash("admin123")
|
||||
c.execute("INSERT INTO workers (rut, name, phone, password_hash, is_admin) VALUES (?, ?, ?, ?, ?)",
|
||||
("1-9", "System Admin", "+56 9 0000 0000", admin_pass, 1))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
populateDefaults()
|
||||
481
routes_admin.py
481
routes_admin.py
@@ -3,7 +3,7 @@ from flask import app, render_template, request, redirect, url_for, flash, sessi
|
||||
from werkzeug.security import generate_password_hash
|
||||
from datetime import date, datetime
|
||||
from database import get_db_connection
|
||||
from utils import admin_required, validate_rut, format_rut, validate_phone, format_phone, generate_random_password
|
||||
from utils import admin_required, validate_rut, format_rut, validate_phone, format_phone, generate_random_password, get_report_params, get_common_report_data
|
||||
import calendar
|
||||
|
||||
def register_admin_routes(app):
|
||||
@@ -508,413 +508,146 @@ def register_admin_routes(app):
|
||||
@app.route('/admin/reportes/modulo/<int:modulo_id>')
|
||||
@admin_required
|
||||
def report_modulo_periodo(modulo_id):
|
||||
mes_actual = date.today().month
|
||||
anio_actual = date.today().year
|
||||
dias_en_periodo = [f'{d:02}' for d in range(1, 32)]
|
||||
|
||||
anio, mes, dia_f, worker_id = get_report_params()
|
||||
conn = get_db_connection()
|
||||
c = conn.cursor()
|
||||
mod_name, workers_list, anios_list = get_common_report_data(c, modulo_id)
|
||||
|
||||
c.execute("SELECT name FROM modulos WHERE id = ?", (modulo_id,))
|
||||
modulo_info = c.fetchone()
|
||||
if not modulo_info:
|
||||
where_clause = "WHERE r.modulo_id = ? AND strftime('%m', r.fecha) = ? AND strftime('%Y', r.fecha) = ?"
|
||||
params = [modulo_id, mes, anio]
|
||||
if dia_f: where_clause += " AND strftime('%d', r.fecha) = ?"; params.append(dia_f.zfill(2))
|
||||
if worker_id: where_clause += " AND r.worker_id = ?"; params.append(worker_id)
|
||||
|
||||
c.execute(f"SELECT strftime('%d', r.fecha) as dia, SUM(r.venta_debito), SUM(r.venta_credito), SUM(r.venta_mp), SUM(r.venta_efectivo), SUM(r.gastos) FROM rendiciones r {where_clause} GROUP BY dia", tuple(params))
|
||||
finanzas = c.fetchall()
|
||||
c.execute(f"SELECT strftime('%d', r.fecha) as dia, SUM(ri.cantidad * ri.comision_historica) FROM rendicion_items ri JOIN rendiciones r ON ri.rendicion_id = r.id {where_clause} AND (r.worker_comision = 1 OR r.companion_comision = 1) GROUP BY dia", tuple(params))
|
||||
comisiones = {row[0]: row[1] for row in c.fetchall()}
|
||||
|
||||
_, num_dias = calendar.monthrange(int(anio), int(mes))
|
||||
dias_en_periodo = [f'{d:02d}' for d in range(1, num_dias + 1)]
|
||||
data_por_dia = {d: {'debito':0,'credito':0,'mp':0,'efectivo':0,'gastos':0,'comision':0,'venta_total':0} for d in dias_en_periodo}
|
||||
for r in finanzas:
|
||||
d = r[0]
|
||||
vt = sum(filter(None, r[1:5]))
|
||||
data_por_dia[d].update({'debito':r[1] or 0,'credito':r[2] or 0,'mp':r[3] or 0,'efectivo':r[4] or 0,'gastos':r[5] or 0,'venta_total':vt,'comision':comisiones.get(d, 0)})
|
||||
|
||||
totales_mes = {k: sum(d[k] for d in data_por_dia.values()) for k in data_por_dia['01'].keys()}
|
||||
dias_activos = sum(1 for d in data_por_dia.values() if d['venta_total'] > 0)
|
||||
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_debito) as debito,
|
||||
SUM(r.venta_credito) as credito,
|
||||
SUM(r.venta_mp) as mp,
|
||||
SUM(r.venta_efectivo) as efectivo,
|
||||
SUM(r.gastos) as gastos
|
||||
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)))
|
||||
finanzas_db = c.fetchall()
|
||||
|
||||
c.execute('''
|
||||
SELECT strftime('%d', r.fecha) as dia,
|
||||
SUM(ri.cantidad * ri.comision_historica * CASE WHEN r.worker_comision = 1 OR r.companion_comision = 1 THEN 1 ELSE 0 END) as comision_total
|
||||
FROM rendicion_items ri
|
||||
JOIN rendiciones r ON ri.rendicion_id = r.id
|
||||
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)))
|
||||
comisiones_db = c.fetchall()
|
||||
|
||||
conn.close()
|
||||
|
||||
data_por_dia = {dia: {'debito': 0, 'credito': 0, 'mp': 0, 'efectivo': 0, 'gastos': 0, 'comision': 0, 'venta_total': 0} for dia in dias_en_periodo}
|
||||
|
||||
for row in finanzas_db:
|
||||
dia, debito, credito, mp, efectivo, gastos = row
|
||||
venta_total = (debito or 0) + (credito or 0) + (mp or 0) + (efectivo or 0)
|
||||
data_por_dia[dia].update({
|
||||
'debito': debito or 0,
|
||||
'credito': credito or 0,
|
||||
'mp': mp or 0,
|
||||
'efectivo': efectivo or 0,
|
||||
'gastos': gastos or 0,
|
||||
'venta_total': venta_total
|
||||
})
|
||||
|
||||
for row in comisiones_db:
|
||||
dia, comision = row
|
||||
data_por_dia[dia]['comision'] = comision or 0
|
||||
|
||||
totales_mes = {'debito': 0, 'credito': 0, 'mp': 0, 'efectivo': 0, 'gastos': 0, 'comision': 0, 'venta_total': 0}
|
||||
dias_activos = 0
|
||||
|
||||
for dia, datos in data_por_dia.items():
|
||||
if datos['venta_total'] > 0 or datos['gastos'] > 0:
|
||||
dias_activos += 1
|
||||
for k in totales_mes.keys():
|
||||
totales_mes[k] += datos[k]
|
||||
|
||||
return render_template('admin_report_modulo.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_mes=totales_mes,
|
||||
dias_activos=dias_activos)
|
||||
return render_template('admin_report_modulo.html', modulo_name=mod_name, modulo_id=modulo_id, mes_nombre=f"{mes}/{anio}", dias_en_periodo=dias_en_periodo, data_por_dia=data_por_dia, totales_mes=totales_mes, dias_activos=dias_activos, workers_list=workers_list, worker_actual=worker_id, dia_actual=dia_f, mes_actual=mes, anio_actual=anio, anios_disponibles=anios_list)
|
||||
|
||||
@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
|
||||
|
||||
anio, mes, dia_f, worker_id = get_report_params()
|
||||
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)))
|
||||
|
||||
mod_name, workers_list, anios_list = get_common_report_data(c, modulo_id)
|
||||
where_clause = "WHERE r.modulo_id = ? AND strftime('%m', r.fecha) = ? AND strftime('%Y', r.fecha) = ?"
|
||||
params = [modulo_id, mes, anio]
|
||||
if dia_f: where_clause += " AND strftime('%d', r.fecha) = ?"; params.append(dia_f.zfill(2))
|
||||
if worker_id: where_clause += " AND (r.worker_id = ? OR r.companion_id = ?)"; params.extend([worker_id, worker_id])
|
||||
c.execute(f"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, (SELECT SUM(cantidad * comision_historica) FROM rendicion_items WHERE rendicion_id = r.id) FROM rendiciones r JOIN workers w ON r.worker_id = w.id LEFT JOIN workers cw ON r.companion_id = cw.id {where_clause}", tuple(params))
|
||||
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)
|
||||
for r in rendiciones:
|
||||
total_com = r[10] or 0
|
||||
# Lógica simplificada: reparte si ambos comisionan
|
||||
for idx, wid, wname, wtipo, wcom in [(0, r[2], r[3], r[4], r[5]), (1, r[6], r[7], r[8], r[9])]:
|
||||
if wid and wcom:
|
||||
if wid not in workers_data: workers_data[wid] = {'name':wname,'tipo':wtipo,'dias':{},'total':0,'enabled':True}
|
||||
val = total_com / 2 if (r[5] and r[9]) else total_com
|
||||
workers_data[wid]['dias'][r[1]] = workers_data[wid]['dias'].get(r[1], 0) + val
|
||||
workers_data[wid]['total'] += val
|
||||
_, num_dias = calendar.monthrange(int(anio), int(mes))
|
||||
dias_en_periodo = [f'{d:02d}' for d in range(1, num_dias + 1)]
|
||||
conn.close()
|
||||
return render_template('admin_report_comisiones.html', modulo_name=mod_name, modulo_id=modulo_id, mes_nombre=f"{mes}/{anio}", workers_data=dict(sorted(workers_data.items(), key=lambda x:x[1]['name'])), dias_en_periodo=dias_en_periodo, workers_list=workers_list, worker_actual=worker_id, dia_actual=dia_f, mes_actual=mes, anio_actual=anio, anios_disponibles=anios_list)
|
||||
|
||||
@app.route('/admin/reportes/modulo/<int:modulo_id>/horarios')
|
||||
@admin_required
|
||||
def report_modulo_horarios(modulo_id):
|
||||
import calendar
|
||||
from datetime import date, datetime
|
||||
|
||||
mes_actual = date.today().month
|
||||
anio_actual = date.today().year
|
||||
|
||||
anio, mes, dia_f, worker_id = get_report_params()
|
||||
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]
|
||||
|
||||
# 1. Pre-cargar a los trabajadores oficiales del módulo (aunque no hayan trabajado aún)
|
||||
c.execute("SELECT id, name FROM workers WHERE modulo_id = ? AND is_admin = 0", (modulo_id,))
|
||||
assigned_workers = c.fetchall()
|
||||
|
||||
workers_data = {}
|
||||
for w_id, w_name in assigned_workers:
|
||||
workers_data[w_id] = {'name': w_name, 'dias': {}, 'total_horas': 0.0}
|
||||
|
||||
# 2. Extraer rendiciones del mes/módulo
|
||||
c.execute('''
|
||||
SELECT
|
||||
r.fecha,
|
||||
w.id, w.name, r.hora_entrada, r.hora_salida,
|
||||
cw.id, cw.name, r.companion_hora_entrada, r.companion_hora_salida
|
||||
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)))
|
||||
|
||||
mod_name, workers_list, anios_list = get_common_report_data(c, modulo_id)
|
||||
where_clause = "WHERE r.modulo_id = ? AND strftime('%m', r.fecha) = ? AND strftime('%Y', r.fecha) = ?"
|
||||
params = [modulo_id, mes, anio]
|
||||
if dia_f: where_clause += " AND strftime('%d', r.fecha) = ?"; params.append(dia_f.zfill(2))
|
||||
if worker_id: where_clause += " AND (r.worker_id = ? OR r.companion_id = ?)"; params.extend([worker_id, worker_id])
|
||||
c.execute(f"SELECT r.fecha, w.id, w.name, r.hora_entrada, r.hora_salida, cw.id, cw.name, r.companion_hora_entrada, r.companion_hora_salida FROM rendiciones r JOIN workers w ON r.worker_id = w.id LEFT JOIN workers cw ON r.companion_id = cw.id {where_clause}", tuple(params))
|
||||
rendiciones = c.fetchall()
|
||||
conn.close()
|
||||
|
||||
def calc_horas(in_str, out_str):
|
||||
if not in_str or not out_str:
|
||||
return 0.0, "0:00"
|
||||
workers_data = {}
|
||||
def calc_h(i, o):
|
||||
if not i or not o: return 0, "0:00"
|
||||
try:
|
||||
t1 = datetime.strptime(in_str, '%H:%M')
|
||||
t2 = datetime.strptime(out_str, '%H:%M')
|
||||
delta = t2 - t1
|
||||
return delta.seconds / 3600, f"{delta.seconds // 3600}:{(delta.seconds % 3600) // 60:02d}"
|
||||
except ValueError:
|
||||
return 0.0, "0:00"
|
||||
|
||||
t1, t2 = datetime.strptime(i, '%H:%M'), datetime.strptime(o, '%H:%M')
|
||||
d = t2 - t1
|
||||
return d.seconds/3600, f"{d.seconds//3600}:{(d.seconds%3600)//60:02d}"
|
||||
except: return 0, "0:00"
|
||||
for r in rendiciones:
|
||||
fecha, w_id, w_name, w_in, w_out, c_id, c_name, c_in, c_out = r
|
||||
dia = fecha[-2:]
|
||||
|
||||
# Titular (Si no es del módulo, lo metemos con etiqueta de Apoyo)
|
||||
if w_id not in workers_data:
|
||||
workers_data[w_id] = {'name': f"{w_name} (Apoyo)", 'dias': {}, 'total_horas': 0.0}
|
||||
|
||||
h_dec, h_str = calc_horas(w_in, w_out)
|
||||
workers_data[w_id]['dias'][dia] = {'in': w_in, 'out': w_out, 'hrs': h_str}
|
||||
workers_data[w_id]['total_horas'] += h_dec
|
||||
|
||||
# Acompañante
|
||||
if c_id and c_in and c_out:
|
||||
if c_id not in workers_data:
|
||||
workers_data[c_id] = {'name': f"{c_name} (Apoyo)", 'dias': {}, 'total_horas': 0.0}
|
||||
|
||||
h_dec, h_str = calc_horas(c_in, c_out)
|
||||
workers_data[c_id]['dias'][dia] = {'in': c_in, 'out': c_out, 'hrs': h_str}
|
||||
workers_data[c_id]['total_horas'] += h_dec
|
||||
|
||||
for w_id in workers_data:
|
||||
th = workers_data[w_id]['total_horas']
|
||||
workers_data[w_id]['total_hrs_str'] = f"{int(th)}:{int(round((th - int(th)) * 60)):02d}"
|
||||
|
||||
# Ordenar alfabéticamente (Los de apoyo quedarán entremezclados por orden alfabético)
|
||||
workers_data = dict(sorted(workers_data.items(), key=lambda x: x[1]['name']))
|
||||
|
||||
_, num_dias = calendar.monthrange(anio_actual, mes_actual)
|
||||
nombres_dias = ['D', 'L', 'M', 'M', 'J', 'V', 'S'] # Ajustado para que el 0 de Python(Lunes) sea coherente si usas isoweekday
|
||||
|
||||
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] # weekday(): Lunes es 0, Domingo es 6
|
||||
})
|
||||
|
||||
return render_template('admin_report_horarios.html',
|
||||
modulo_name=modulo_name,
|
||||
mes_nombre=f'{mes_actual:02}/{anio_actual}',
|
||||
workers_data=workers_data,
|
||||
dias_en_periodo=dias_en_periodo)
|
||||
|
||||
d = r[0][-2:]
|
||||
for wid, wname, win, wout in [(r[1], r[2], r[3], r[4]), (r[5], r[6], r[7], r[8])]:
|
||||
if wid:
|
||||
if wid not in workers_data: workers_data[wid] = {'name':wname,'dias':{},'total_horas':0}
|
||||
h_dec, h_str = calc_h(win, wout)
|
||||
workers_data[wid]['dias'][d] = {'in':win,'out':wout,'hrs':h_str}
|
||||
workers_data[wid]['total_horas'] += h_dec
|
||||
for w in workers_data.values():
|
||||
th = w['total_horas']
|
||||
w['total_hrs_str'] = f"{int(th)}:{int((th-int(th))*60):02d}"
|
||||
_, num_dias = calendar.monthrange(int(anio), int(mes))
|
||||
dias_en_periodo = [{'num':f'{d:02d}','name':['L','M','M','J','V','S','D'][date(int(anio),int(mes),d).weekday()]} for d in range(1, num_dias+1)]
|
||||
conn.close()
|
||||
return render_template('admin_report_horarios.html', modulo_name=mod_name, modulo_id=modulo_id, mes_nombre=f"{mes}/{anio}", workers_data=workers_data, dias_en_periodo=dias_en_periodo, workers_list=workers_list, worker_actual=worker_id, dia_actual=dia_f, mes_actual=mes, anio_actual=anio, anios_disponibles=anios_list)
|
||||
|
||||
@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]
|
||||
})
|
||||
|
||||
anio, mes, dia_f, worker_id = get_report_params()
|
||||
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)))
|
||||
|
||||
mod_name, workers_list, anios_list = get_common_report_data(c, modulo_id)
|
||||
where_clause = "WHERE r.modulo_id = ? AND strftime('%m', r.fecha) = ? AND strftime('%Y', r.fecha) = ?"
|
||||
params = [modulo_id, mes, anio]
|
||||
if dia_f: where_clause += " AND strftime('%d', r.fecha) = ?"; params.append(dia_f.zfill(2))
|
||||
if worker_id: where_clause += " AND r.worker_id = ?"; params.append(worker_id)
|
||||
c.execute(f"SELECT strftime('%d', r.fecha) as dia, SUM(r.boletas_debito + r.boletas_credito + r.boletas_mp), SUM(r.boletas_efectivo), SUM(r.venta_debito + r.venta_credito + r.venta_mp + r.venta_efectivo) FROM rendiciones r {where_clause} GROUP BY dia", tuple(params))
|
||||
resultados = c.fetchall()
|
||||
_, num_dias = calendar.monthrange(int(anio), int(mes))
|
||||
data_por_dia = {f'{d:02d}': {'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 r in resultados:
|
||||
dia, rc, ef, vt = r[0], r[1] or 0, r[2] or 0, r[3] or 0
|
||||
vn = round(vt/1.19)
|
||||
data_por_dia[dia] = {'red_compra':rc,'efectivo':ef,'total_trans':rc+ef,'venta_neta':vn}
|
||||
for k in totales: totales[k] += data_por_dia[dia][k]
|
||||
dias_en_periodo = [{'num':f'{d:02d}','name':['L','M','M','J','V','S','D'][date(int(anio),int(mes),d).weekday()]} for d in range(1, num_dias+1)]
|
||||
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)
|
||||
return render_template('admin_report_cc.html', modulo_name=mod_name, modulo_id=modulo_id, mes_nombre=f"{mes}/{anio}", dias_en_periodo=dias_en_periodo, data_por_dia=data_por_dia, totales=totales, workers_list=workers_list, worker_actual=worker_id, dia_actual=dia_f, mes_actual=mes, anio_actual=anio, anios_disponibles=anios_list)
|
||||
|
||||
@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]
|
||||
})
|
||||
|
||||
anio, mes, dia_f, worker_id = get_report_params()
|
||||
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)))
|
||||
|
||||
mod_name, workers_list, anios_list = get_common_report_data(c, modulo_id)
|
||||
where_clause = "WHERE r.modulo_id = ? AND strftime('%m', r.fecha) = ? AND strftime('%Y', r.fecha) = ?"
|
||||
params = [modulo_id, mes, anio]
|
||||
if dia_f: where_clause += " AND strftime('%d', r.fecha) = ?"; params.append(dia_f.zfill(2))
|
||||
if worker_id: where_clause += " AND r.worker_id = ?"; params.append(worker_id)
|
||||
c.execute(f"SELECT strftime('%d', r.fecha) as dia, SUM(r.venta_efectivo), SUM(r.venta_debito + r.venta_credito + r.venta_mp) FROM rendiciones r {where_clause} GROUP BY dia", tuple(params))
|
||||
resultados = c.fetchall()
|
||||
_, num_dias = calendar.monthrange(int(anio), int(mes))
|
||||
data_por_dia = {f'{d:02d}': {'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 r in resultados:
|
||||
dia, ef, tbk = r[0], r[1] or 0, r[2] or 0
|
||||
tt = ef + tbk
|
||||
data_por_dia[dia] = {'efectivo':ef,'tbk':tbk,'total':tt,'porcentaje':round((ef/tt)*100) if tt>0 else 0}
|
||||
totales['efectivo'] += ef; totales['tbk'] += tbk; totales['total'] += tt
|
||||
if totales['total'] > 0: totales['porcentaje'] = round((totales['efectivo']/totales['total'])*100)
|
||||
dias_en_periodo = [{'num':f'{d:02d}','name':['L','M','M','J','V','S','D'][date(int(anio),int(mes),d).weekday()]} for d in range(1, num_dias+1)]
|
||||
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)
|
||||
return render_template('admin_report_iva.html', modulo_name=mod_name, modulo_id=modulo_id, mes_nombre=f"{mes}/{anio}", dias_en_periodo=dias_en_periodo, data_por_dia=data_por_dia, totales=totales, workers_list=workers_list, worker_actual=worker_id, dia_actual=dia_f, mes_actual=mes, anio_actual=anio, anios_disponibles=anios_list)
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "macros/base.html" %}
|
||||
{% from "macros/modals.html" import report_filters %}
|
||||
|
||||
{% block title %}Reporte: Centros Comerciales - {{ modulo_name }}{% endblock %}
|
||||
|
||||
@@ -49,7 +50,16 @@
|
||||
<div class="text-muted"><small>Período: {{ mes_nombre }}</small></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ report_filters(
|
||||
url_for('report_modulo_periodo', modulo_id=modulo_id),
|
||||
workers_list,
|
||||
worker_actual,
|
||||
dia_actual,
|
||||
[('01','Ene'),('02','Feb'),('03','Mar'),('04','Abr'),('05','May'),('06','Jun'),('07','Jul'),('08','Ago'),('09','Sep'),('10','Oct'),('11','Nov'),('12','Dic')],
|
||||
mes_actual,
|
||||
anios_disponibles,
|
||||
anio_actual
|
||||
) }}
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive table-container custom-scrollbar">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "macros/base.html" %}
|
||||
{% from "macros/modals.html" import report_filters %}
|
||||
|
||||
{% block title %}Reporte: Comisiones - {{ modulo_name }}{% endblock %}
|
||||
|
||||
@@ -31,7 +32,16 @@
|
||||
<div class="text-muted"><small>Período: {{ mes_nombre }}</small></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ report_filters(
|
||||
url_for('report_modulo_periodo', modulo_id=modulo_id),
|
||||
workers_list,
|
||||
worker_actual,
|
||||
dia_actual,
|
||||
[('01','Ene'),('02','Feb'),('03','Mar'),('04','Abr'),('05','May'),('06','Jun'),('07','Jul'),('08','Ago'),('09','Sep'),('10','Oct'),('11','Nov'),('12','Dic')],
|
||||
mes_actual,
|
||||
anios_disponibles,
|
||||
anio_actual
|
||||
) }}
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
{% if workers_data %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "macros/base.html" %}
|
||||
{% from "macros/modals.html" import report_filters %}
|
||||
|
||||
{% block title %}Reporte: Horarios - {{ modulo_name }}{% endblock %}
|
||||
|
||||
@@ -45,7 +46,16 @@
|
||||
<div class="text-muted"><small>Período: {{ mes_nombre }}</small></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ report_filters(
|
||||
url_for('report_modulo_periodo', modulo_id=modulo_id),
|
||||
workers_list,
|
||||
worker_actual,
|
||||
dia_actual,
|
||||
[('01','Ene'),('02','Feb'),('03','Mar'),('04','Abr'),('05','May'),('06','Jun'),('07','Jul'),('08','Ago'),('09','Sep'),('10','Oct'),('11','Nov'),('12','Dic')],
|
||||
mes_actual,
|
||||
anios_disponibles,
|
||||
anio_actual
|
||||
) }}
|
||||
{% if workers_data %}
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body p-0">
|
||||
@@ -60,8 +70,8 @@
|
||||
</tr>
|
||||
<tr>
|
||||
{% for w_id, data in workers_data.items() %}
|
||||
<th class="bg-body-tertiary">Ent</th>
|
||||
<th class="bg-body-tertiary">Sal</th>
|
||||
<th class="bg-body-tertiary">Entrada</th>
|
||||
<th class="bg-body-tertiary">Salida</th>
|
||||
<th class="bg-success-subtle text-success">Hrs</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "macros/base.html" %}
|
||||
{% from "macros/modals.html" import report_filters %}
|
||||
|
||||
{% block title %}Reporte: Cálculo de IVA - {{ modulo_name }}{% endblock %}
|
||||
|
||||
@@ -49,7 +50,16 @@
|
||||
<div class="text-muted"><small>Período: {{ mes_nombre }}</small></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ report_filters(
|
||||
url_for('report_modulo_periodo', modulo_id=modulo_id),
|
||||
workers_list,
|
||||
worker_actual,
|
||||
dia_actual,
|
||||
[('01','Ene'),('02','Feb'),('03','Mar'),('04','Abr'),('05','May'),('06','Jun'),('07','Jul'),('08','Ago'),('09','Sep'),('10','Oct'),('11','Nov'),('12','Dic')],
|
||||
mes_actual,
|
||||
anios_disponibles,
|
||||
anio_actual
|
||||
) }}
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive table-container custom-scrollbar">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "macros/base.html" %}
|
||||
{% from "macros/modals.html" import report_filters %}
|
||||
|
||||
{% block title %}Reporte: Finanzas - {{ modulo_name }}{% endblock %}
|
||||
|
||||
@@ -72,6 +73,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ report_filters(
|
||||
url_for('report_modulo_periodo', modulo_id=modulo_id),
|
||||
workers_list,
|
||||
worker_actual,
|
||||
dia_actual,
|
||||
[('01','Ene'),('02','Feb'),('03','Mar'),('04','Abr'),('05','May'),('06','Jun'),('07','Jul'),('08','Ago'),('09','Sep'),('10','Oct'),('11','Nov'),('12','Dic')],
|
||||
mes_actual,
|
||||
anios_disponibles,
|
||||
anio_actual
|
||||
) }}
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header border-0 bg-transparent d-flex justify-content-between align-items-center pb-0">
|
||||
<span class="fw-bold text-muted text-uppercase"><i class="bi bi-calendar3 me-1"></i> Desglose Diario</span>
|
||||
|
||||
@@ -574,3 +574,45 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro report_filters(action_url, workers, selected_worker, selected_dia, meses, mes_act, anios, anio_act) %}
|
||||
<div class="card shadow-sm mb-4 border-0 bg-dark-subtle">
|
||||
<div class="card-body p-3">
|
||||
<form method="GET" action="{{ action_url }}" class="row g-2 align-items-end">
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small text-muted mb-1">Año/Mes</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<select name="anio" class="form-select">
|
||||
{% for a in anios %}<option value="{{ a }}" {{ 'selected' if a|string == anio_act|string }}>{{ a }}</option>{% endfor %}
|
||||
</select>
|
||||
<select name="mes" class="form-select">
|
||||
{% for m_num, m_name in meses %}<option value="{{ m_num }}" {{ 'selected' if m_num == mes_act }}>{{ m_name }}</option>{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small text-muted mb-1">Día (Opcional)</label>
|
||||
<select name="dia" class="form-select form-select-sm">
|
||||
<option value="">Todos los días</option>
|
||||
{% for d in range(1, 32) %}
|
||||
{% set d_str = "%02d"|format(d) %}
|
||||
<option value="{{ d_str }}" {{ 'selected' if d_str == selected_dia }}>{{ d_str }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small text-muted mb-1">Trabajador</label>
|
||||
<select name="worker_id" class="form-select form-select-sm">
|
||||
<option value="">Todos los trabajadores</option>
|
||||
{% for w in workers %}
|
||||
<option value="{{ w[0] }}" {{ 'selected' if w[0]|string == selected_worker|string }}>{{ w[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm w-100"><i class="bi bi-filter"></i> Aplicar Filtros</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
32
utils.py
32
utils.py
@@ -2,7 +2,8 @@ import re
|
||||
import random
|
||||
import string
|
||||
from functools import wraps
|
||||
from flask import session, redirect, url_for, flash
|
||||
from flask import session, redirect, url_for, flash, request
|
||||
from datetime import date
|
||||
|
||||
def generate_random_password(length=6):
|
||||
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
|
||||
@@ -54,3 +55,32 @@ def admin_required(f):
|
||||
return redirect(url_for('index'))
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
def get_report_params():
|
||||
"""Captura filtros de la URL o establece valores por defecto."""
|
||||
hoy = date.today()
|
||||
anio = request.args.get('anio', str(hoy.year))
|
||||
mes = request.args.get('mes', f"{hoy.month:02d}")
|
||||
dia = request.args.get('dia')
|
||||
worker_id = request.args.get('worker_id')
|
||||
return anio, mes, dia, worker_id
|
||||
|
||||
def get_common_report_data(c, modulo_id):
|
||||
"""Obtiene nombre del módulo y lista de trabajadores para los filtros."""
|
||||
c.execute("SELECT name FROM modulos WHERE id = ?", (modulo_id,))
|
||||
modulo_info = c.fetchone()
|
||||
|
||||
c.execute('''
|
||||
SELECT DISTINCT w.id, w.name
|
||||
FROM workers w
|
||||
WHERE w.modulo_id = ? AND w.is_admin = 0
|
||||
ORDER BY w.name
|
||||
''', (modulo_id,))
|
||||
workers = c.fetchall()
|
||||
|
||||
c.execute("SELECT DISTINCT strftime('%Y', fecha) as anio FROM rendiciones ORDER BY anio DESC")
|
||||
anios = [row[0] for row in c.fetchall()]
|
||||
if str(date.today().year) not in anios:
|
||||
anios.insert(0, str(date.today().year))
|
||||
|
||||
return modulo_info[0] if modulo_info else "Módulo", workers, anios
|
||||
Reference in New Issue
Block a user