bank fields, date range filters, add worker modal, fix empty report crash

This commit is contained in:
2026-06-22 01:28:33 -04:00
parent dcad421c27
commit 81251fb869
16 changed files with 346 additions and 225 deletions

View File

@@ -276,6 +276,13 @@ def init_db():
FOREIGN KEY (rendicion_id) REFERENCES rendiciones(id), FOREIGN KEY (rendicion_id) REFERENCES rendiciones(id),
FOREIGN KEY (producto_id) REFERENCES productos(id))''') FOREIGN KEY (producto_id) REFERENCES productos(id))''')
# Migrate: add bank fields if missing
for col in ['nombre_banco', 'numero_cuenta', 'tipo_cuenta', 'rut_banco']:
try:
c.execute(f"ALTER TABLE workers ADD COLUMN {col} TEXT DEFAULT ''")
except sqlite3.OperationalError:
pass # column already exists
c.execute("SELECT id FROM workers WHERE is_admin = 1") c.execute("SELECT id FROM workers WHERE is_admin = 1")
if not c.fetchone(): if not c.fetchone():
admin_pass = generate_password_hash("admin123") admin_pass = generate_password_hash("admin123")

View File

@@ -35,28 +35,38 @@ def generar_historico_definitivo(dias_atras=180):
nombre_falso = f"Trabajador {i+1} ({mod_name})" nombre_falso = f"Trabajador {i+1} ({mod_name})"
phone_falso = f"+56 9 8888 {mod_id:02d}{i:02d}" phone_falso = f"+56 9 8888 {mod_id:02d}{i:02d}"
banco = random.choice(["Banco Estado", "Banco de Chile", "Banco Falabella", "Santander", "BCI", "Scotiabank"])
workers_data.append(( workers_data.append((
rut_falso, nombre_falso, phone_falso, rut_falso, nombre_falso, phone_falso,
default_pass, 0, mod_id, tipos[i] default_pass, 0, mod_id, tipos[i],
banco, f"{random.randint(10000000, 99999999)}", random.choice(["Cuenta Corriente", "Cuenta Vista", "Cuenta Rut"]),
f"{random.randint(10000000, 99999999)}-{random.choice('0123456789K')}",
)) ))
c.executemany('''INSERT OR IGNORE INTO workers c.executemany('''INSERT OR IGNORE INTO workers
(rut, name, phone, password_hash, is_admin, modulo_id, tipo) (rut, name, phone, password_hash, is_admin, modulo_id, tipo,
VALUES (?, ?, ?, ?, ?, ?, ?)''', workers_data) nombre_banco, numero_cuenta, tipo_cuenta, rut_banco)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', workers_data)
conn.commit() conn.commit()
# 3. PREPARACIÓN DE DATOS # 3. PREPARACIÓN DE DATOS
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")
all_workers_data = c.fetchall() all_workers_data = c.fetchall()
todos_los_trabajadores = [w[0] for w in all_workers_data] todos_los_trabajadores = [w[0] for w in all_workers_data]
workers_tipo = {w[0]: w[2] for w in all_workers_data}
trabajadores_por_modulo = {} trabajadores_por_modulo = {}
for w_id, m_id in all_workers_data: for w_id, m_id, _ in all_workers_data:
if m_id not in trabajadores_por_modulo: if m_id not in trabajadores_por_modulo:
trabajadores_por_modulo[m_id] = [] trabajadores_por_modulo[m_id] = []
trabajadores_por_modulo[m_id].append(w_id) trabajadores_por_modulo[m_id].append(w_id)
c.execute("SELECT id, price, commission FROM productos") c.execute("""
SELECT p.id, ph.price, ph.commission
FROM productos p
JOIN precios_historicos ph ON p.id = ph.producto_id
GROUP BY p.id
""")
productos = c.fetchall() productos = c.fetchall()
# 4. VIAJE EN EL TIEMPO # 4. VIAJE EN EL TIEMPO
@@ -104,15 +114,21 @@ def generar_historico_definitivo(dias_atras=180):
else: else:
hora_entrada = f"{random.randint(13, 15):02d}:{random.choice(['00', '30'])}" hora_entrada = f"{random.randint(13, 15):02d}:{random.choice(['00', '30'])}"
hora_salida = f"{random.randint(19, 21):02d}:{random.choice(['00', '30'])}" hora_salida = f"{random.randint(19, 21):02d}:{random.choice(['00', '30'])}"
worker_tipo = workers_tipo.get(worker_id, "Part Time")
worker_comision = 1 if worker_tipo == "Full Time" else random.choice([0, 1])
# Acompañante (70%) # Acompañante (70%)
companion_id = None companion_id = None
comp_in, comp_out = None, None comp_in, comp_out = None, None
companion_comision = 0
if random.random() < 0.70: if random.random() < 0.70:
posibles_comp = [w for w in todos_los_trabajadores if w != worker_id] posibles_comp = [w for w in todos_los_trabajadores if w != worker_id]
if posibles_comp: if posibles_comp:
companion_id = random.choice(posibles_comp) companion_id = random.choice(posibles_comp)
comp_in, comp_out = hora_entrada, hora_salida comp_in, comp_out = hora_entrada, hora_salida
comp_tipo = workers_tipo.get(companion_id, "Part Time")
companion_comision = 1 if comp_tipo == "Full Time" else random.choice([0, 1])
num_prods = random.randint(1, 5) num_prods = random.randint(1, 5)
prods_elegidos = random.sample(productos, min(num_prods, len(productos))) prods_elegidos = random.sample(productos, min(num_prods, len(productos)))
@@ -155,16 +171,19 @@ def generar_historico_definitivo(dias_atras=180):
c.execute(''' c.execute('''
INSERT INTO rendiciones INSERT INTO rendiciones
(worker_id, companion_id, modulo_id, fecha, hora_entrada, hora_salida, (worker_id, worker_comision, companion_id, modulo_id, fecha,
companion_hora_entrada, companion_hora_salida, hora_entrada, hora_salida, companion_hora_entrada, companion_hora_salida,
companion_comision,
venta_debito, venta_credito, venta_mp, venta_efectivo, venta_debito, venta_credito, venta_mp, venta_efectivo,
boletas_debito, boletas_credito, boletas_mp, boletas_efectivo, boletas_debito, boletas_credito, boletas_mp, boletas_efectivo,
gastos, observaciones, worker_comision, companion_comision) gastos, observaciones)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (worker_id, companion_id, modulo_id, fecha_str, hora_entrada, hora_salida, ''', (worker_id, worker_comision, companion_id, modulo_id, fecha_str,
comp_in, comp_out, debito, credito, mp, efectivo, hora_entrada, hora_salida, comp_in, comp_out,
b_debito, b_credito, b_mp, b_efectivo, companion_comision,
gastos, tipo_registro, 1 if companion_id else 0)) debito, credito, mp, efectivo,
b_debito, b_credito, b_mp, b_efectivo,
gastos, tipo_registro))
r_id = c.lastrowid r_id = c.lastrowid

View File

@@ -55,6 +55,10 @@ class Worker(db.Model):
is_admin = db.Column(db.Boolean, default=False) is_admin = db.Column(db.Boolean, default=False)
modulo_id = db.Column(db.Integer, db.ForeignKey('modulos.id')) modulo_id = db.Column(db.Integer, db.ForeignKey('modulos.id'))
tipo = db.Column(db.String, default='Full Time') tipo = db.Column(db.String, default='Full Time')
nombre_banco = db.Column(db.String, default='')
numero_cuenta = db.Column(db.String, default='')
tipo_cuenta = db.Column(db.String, default='')
rut_banco = db.Column(db.String, default='')
class Rendicion(db.Model): class Rendicion(db.Model):

View File

@@ -17,6 +17,20 @@ from services import report_service, rendiciones_service
admin_bp = Blueprint('admin', __name__, url_prefix='/admin') admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
BANCOS = [
"Banco Estado", "Banco de Chile", "Banco Falabella", "BCI Nova",
"Banco Bice", "Banco Consorcio", "Banco Security", "Banefe",
"Coopeuch", "Corpbanca", "Multicaja", "Ahorrocoop",
"BBVA Chile", "Banco Condell", "Banco Do Brasil S.A.",
"Banco Edwards Citi", "Banco Internacional", "Banco Itaú Chile",
"Banco París", "Banco Penta", "Banco Ripley",
"Banco Santander Chile", "Banco de Crédito e Inversiones - BCI",
"Banco de la Nación Argentina", "Caputal", "Coocretal",
"Credichile Atlas", "DNB Bank ASA", "Detacoop",
"Oriencoop", "RABOBANK CHILE", "Scotiabank Chile",
"Servipag Express", "THE BANK OF TOKYO-MITSUBISHI UFJ, LTD.",
]
# ============================================================ # ============================================================
# WORKERS # WORKERS
@@ -47,9 +61,17 @@ def manage_workers():
password = generate_random_password() password = generate_random_password()
p_hash = generate_password_hash(password) p_hash = generate_password_hash(password)
nombre_banco = request.form.get('nombre_banco', '')
if nombre_banco == '__otro__':
nombre_banco = request.form.get('nombre_banco_otro', '')
new_worker = Worker( new_worker = Worker(
rut=rut, name=name, phone=phone, password_hash=p_hash, rut=rut, name=name, phone=phone, password_hash=p_hash,
is_admin=False, modulo_id=int(modulo_id), tipo=tipo, is_admin=False, modulo_id=int(modulo_id), tipo=tipo,
nombre_banco=nombre_banco,
numero_cuenta=request.form.get('numero_cuenta', ''),
tipo_cuenta=request.form.get('tipo_cuenta', ''),
rut_banco=request.form.get('rut_banco', ''),
) )
try: try:
db.session.add(new_worker) db.session.add(new_worker)
@@ -69,7 +91,8 @@ def manage_workers():
.all() .all()
) )
workers = [ workers = [
(w.id, w.rut, w.name, w.phone, m.name if m else None, w.modulo_id, w.tipo) (w.id, w.rut, w.name, w.phone, m.name if m else None, w.modulo_id, w.tipo,
w.nombre_banco or '', w.numero_cuenta or '', w.tipo_cuenta or '', w.rut_banco or '')
for w, m in workers_rows for w, m in workers_rows
] ]
@@ -81,7 +104,7 @@ def manage_workers():
) )
modulos = [(m.id, m.name, z.name) for m, z in modulos_rows] modulos = [(m.id, m.name, z.name) for m, z in modulos_rows]
return render_template('admin_workers.html', workers=workers, form=form_data, modulos=modulos) return render_template('admin_workers.html', workers=workers, form=form_data, modulos=modulos, bancos=BANCOS)
@admin_bp.route('/workers/edit/<int:id>', methods=['GET', 'POST']) @admin_bp.route('/workers/edit/<int:id>', methods=['GET', 'POST'])
@@ -108,6 +131,13 @@ def edit_worker(id):
worker.phone = format_phone(raw_phone) worker.phone = format_phone(raw_phone)
worker.modulo_id = int(modulo_id) worker.modulo_id = int(modulo_id)
worker.tipo = tipo worker.tipo = tipo
nombre_banco = request.form.get('nombre_banco', '')
if nombre_banco == '__otro__':
nombre_banco = request.form.get('nombre_banco_otro', '')
worker.nombre_banco = nombre_banco
worker.numero_cuenta = request.form.get('numero_cuenta', '')
worker.tipo_cuenta = request.form.get('tipo_cuenta', '')
worker.rut_banco = request.form.get('rut_banco', '')
db.session.commit() db.session.commit()
flash("Trabajador actualizado exitosamente.", "success") flash("Trabajador actualizado exitosamente.", "success")
@@ -126,8 +156,10 @@ def edit_worker(id):
if not worker: if not worker:
return redirect(url_for('admin.manage_workers')) return redirect(url_for('admin.manage_workers'))
worker_tuple = (worker.id, worker.rut, worker.name, worker.phone, worker.modulo_id) worker_tuple = (worker.id, worker.rut, worker.name, worker.phone, worker.modulo_id,
return render_template('edit_worker.html', worker=worker_tuple, modulos=modulos) worker.nombre_banco or '', worker.numero_cuenta or '', worker.tipo_cuenta or '',
worker.rut_banco or '')
return render_template('edit_worker.html', worker=worker_tuple, modulos=modulos, bancos=BANCOS)
@admin_bp.route('/workers/delete/<int:id>', methods=['POST']) @admin_bp.route('/workers/delete/<int:id>', methods=['POST'])
@@ -411,40 +443,28 @@ def api_product_history(id):
@admin_bp.route('/rendiciones') @admin_bp.route('/rendiciones')
@admin_required @admin_required
def admin_rendiciones(): def admin_rendiciones():
mes_seleccionado = request.args.get('mes') hoy = date.today()
anio_seleccionado = request.args.get('anio') fecha_inicio = request.args.get('fecha_inicio', f"{hoy.year}-{hoy.month:02d}-01")
dia_seleccionado = request.args.get('dia') fecha_fin = request.args.get('fecha_fin', hoy.strftime('%Y-%m-%d'))
zona_id_seleccionada = request.args.get('zona_id') zona_id_seleccionada = request.args.get('zona_id')
modulo_id_seleccionado = request.args.get('modulo_id') modulo_id_seleccionado = request.args.get('modulo_id')
if request.args.get('mes') is None:
hoy = date.today()
mes_seleccionado = f"{hoy.month:02d}"
anio_seleccionado = str(hoy.year)
dia_seleccionado = f"{hoy.day:02d}"
mes_seleccionado = mes_seleccionado.zfill(2)
rendiciones_completas = rendiciones_service.get_filtered_rendiciones( rendiciones_completas = rendiciones_service.get_filtered_rendiciones(
mes_seleccionado, anio_seleccionado, dia_seleccionado, fecha_inicio, fecha_fin,
zona_id_seleccionada, modulo_id_seleccionado, zona_id_seleccionada, modulo_id_seleccionado,
) )
workers, modulos, zonas, anios_disponibles = rendiciones_service.get_filter_catalogs() workers, modulos, zonas, anios_disponibles = rendiciones_service.get_filter_catalogs()
dias_disponibles = [f"{d:02d}" for d in range(1, 32)]
return render_template('admin_rendiciones.html', return render_template('admin_rendiciones.html',
rendiciones=rendiciones_completas, rendiciones=rendiciones_completas,
workers=workers, workers=workers,
modulos=modulos, modulos=modulos,
zonas=zonas, zonas=zonas,
mes_actual=mes_seleccionado, fecha_inicio=fecha_inicio,
anio_actual=anio_seleccionado, fecha_fin=fecha_fin,
dia_actual=dia_seleccionado,
zona_actual=zona_id_seleccionada, zona_actual=zona_id_seleccionada,
modulo_actual=modulo_id_seleccionado, modulo_actual=modulo_id_seleccionado,
anios_disponibles=anios_disponibles, anios_disponibles=anios_disponibles)
dias_disponibles=dias_disponibles)
@admin_bp.route('/rendiciones/delete/<int:id>', methods=['POST']) @admin_bp.route('/rendiciones/delete/<int:id>', methods=['POST'])
@@ -549,87 +569,87 @@ def admin_reportes_index():
@admin_bp.route('/reportes/modulo/<int:modulo_id>') @admin_bp.route('/reportes/modulo/<int:modulo_id>')
@admin_required @admin_required
def report_modulo_periodo(modulo_id): def report_modulo_periodo(modulo_id):
anio, mes, dia_f, worker_id = get_report_params() fecha_inicio, fecha_fin, worker_id = get_report_params()
mod_name, workers_list, anios_list = report_service.get_modulo_workers_and_anios(modulo_id) mod_name, workers_list, anios_list = report_service.get_modulo_workers_and_anios(modulo_id)
data = report_service.get_modulo_periodo_data(modulo_id, anio, mes, dia_f, worker_id) data = report_service.get_modulo_periodo_data(modulo_id, fecha_inicio, fecha_fin, worker_id)
return render_template('admin_report_modulo.html', return render_template('admin_report_modulo.html',
modulo_name=mod_name, modulo_id=modulo_id, modulo_name=mod_name, modulo_id=modulo_id,
mes_nombre=f"{mes}/{anio}", mes_nombre=f"{fecha_inicio} a {fecha_fin}",
dias_en_periodo=data['dias_en_periodo'], dias_en_periodo=data['dias_en_periodo'],
data_por_dia=data['data_por_dia'], data_por_dia=data['data_por_dia'],
totales_mes=data['totales_mes'], totales_mes=data['totales_mes'],
dias_activos=data['dias_activos'], dias_activos=data['dias_activos'],
workers_list=workers_list, worker_actual=worker_id, workers_list=workers_list, worker_actual=worker_id,
dia_actual=dia_f, mes_actual=mes, anio_actual=anio, fecha_inicio=fecha_inicio, fecha_fin=fecha_fin,
anios_disponibles=anios_list) anios_disponibles=anios_list)
@admin_bp.route('/reportes/modulo/<int:modulo_id>/comisiones') @admin_bp.route('/reportes/modulo/<int:modulo_id>/comisiones')
@admin_required @admin_required
def report_modulo_comisiones(modulo_id): def report_modulo_comisiones(modulo_id):
anio, mes, dia_f, worker_id = get_report_params() fecha_inicio, fecha_fin, worker_id = get_report_params()
mod_name, workers_list, anios_list = report_service.get_modulo_workers_and_anios(modulo_id) mod_name, workers_list, anios_list = report_service.get_modulo_workers_and_anios(modulo_id)
data = report_service.get_comisiones_data(modulo_id, anio, mes, dia_f, worker_id) data = report_service.get_comisiones_data(modulo_id, fecha_inicio, fecha_fin, worker_id)
return render_template('admin_report_comisiones.html', return render_template('admin_report_comisiones.html',
modulo_name=mod_name, modulo_id=modulo_id, modulo_name=mod_name, modulo_id=modulo_id,
mes_nombre=f"{mes}/{anio}", mes_nombre=f"{fecha_inicio} a {fecha_fin}",
workers_data=data['workers_data'], workers_data=data['workers_data'],
dias_en_periodo=data['dias_en_periodo'], dias_en_periodo=data['dias_en_periodo'],
workers_list=workers_list, worker_actual=worker_id, workers_list=workers_list, worker_actual=worker_id,
dia_actual=dia_f, mes_actual=mes, anio_actual=anio, fecha_inicio=fecha_inicio, fecha_fin=fecha_fin,
anios_disponibles=anios_list) anios_disponibles=anios_list)
@admin_bp.route('/reportes/modulo/<int:modulo_id>/horarios') @admin_bp.route('/reportes/modulo/<int:modulo_id>/horarios')
@admin_required @admin_required
def report_modulo_horarios(modulo_id): def report_modulo_horarios(modulo_id):
anio, mes, dia_f, worker_id = get_report_params() fecha_inicio, fecha_fin, worker_id = get_report_params()
mod_name, workers_list, anios_list = report_service.get_modulo_workers_and_anios(modulo_id) mod_name, workers_list, anios_list = report_service.get_modulo_workers_and_anios(modulo_id)
data = report_service.get_horarios_data(modulo_id, anio, mes, dia_f, worker_id) data = report_service.get_horarios_data(modulo_id, fecha_inicio, fecha_fin, worker_id)
return render_template('admin_report_horarios.html', return render_template('admin_report_horarios.html',
modulo_name=mod_name, modulo_id=modulo_id, modulo_name=mod_name, modulo_id=modulo_id,
mes_nombre=f"{mes}/{anio}", mes_nombre=f"{fecha_inicio} a {fecha_fin}",
workers_data=data['workers_data'], workers_data=data['workers_data'],
dias_en_periodo=data['dias_en_periodo'], dias_en_periodo=data['dias_en_periodo'],
workers_list=workers_list, worker_actual=worker_id, workers_list=workers_list, worker_actual=worker_id,
dia_actual=dia_f, mes_actual=mes, anio_actual=anio, fecha_inicio=fecha_inicio, fecha_fin=fecha_fin,
anios_disponibles=anios_list) anios_disponibles=anios_list)
@admin_bp.route('/reportes/modulo/<int:modulo_id>/centros_comerciales') @admin_bp.route('/reportes/modulo/<int:modulo_id>/centros_comerciales')
@admin_required @admin_required
def report_modulo_centros_comerciales(modulo_id): def report_modulo_centros_comerciales(modulo_id):
anio, mes, dia_f, worker_id = get_report_params() fecha_inicio, fecha_fin, worker_id = get_report_params()
mod_name, workers_list, anios_list = report_service.get_modulo_workers_and_anios(modulo_id) mod_name, workers_list, anios_list = report_service.get_modulo_workers_and_anios(modulo_id)
data = report_service.get_cc_data(modulo_id, anio, mes, dia_f, worker_id) data = report_service.get_cc_data(modulo_id, fecha_inicio, fecha_fin, worker_id)
return render_template('admin_report_cc.html', return render_template('admin_report_cc.html',
modulo_name=mod_name, modulo_id=modulo_id, modulo_name=mod_name, modulo_id=modulo_id,
mes_nombre=f"{mes}/{anio}", mes_nombre=f"{fecha_inicio} a {fecha_fin}",
dias_en_periodo=data['dias_en_periodo'], dias_en_periodo=data['dias_en_periodo'],
data_por_dia=data['data_por_dia'], data_por_dia=data['data_por_dia'],
totales=data['totales'], totales=data['totales'],
workers_list=workers_list, worker_actual=worker_id, workers_list=workers_list, worker_actual=worker_id,
dia_actual=dia_f, mes_actual=mes, anio_actual=anio, fecha_inicio=fecha_inicio, fecha_fin=fecha_fin,
anios_disponibles=anios_list) anios_disponibles=anios_list)
@admin_bp.route('/reportes/modulo/<int:modulo_id>/calculo_iva') @admin_bp.route('/reportes/modulo/<int:modulo_id>/calculo_iva')
@admin_required @admin_required
def report_modulo_calculo_iva(modulo_id): def report_modulo_calculo_iva(modulo_id):
anio, mes, dia_f, worker_id = get_report_params() fecha_inicio, fecha_fin, worker_id = get_report_params()
mod_name, workers_list, anios_list = report_service.get_modulo_workers_and_anios(modulo_id) mod_name, workers_list, anios_list = report_service.get_modulo_workers_and_anios(modulo_id)
data = report_service.get_iva_data(modulo_id, anio, mes, dia_f, worker_id) data = report_service.get_iva_data(modulo_id, fecha_inicio, fecha_fin, worker_id)
return render_template('admin_report_iva.html', return render_template('admin_report_iva.html',
modulo_name=mod_name, modulo_id=modulo_id, modulo_name=mod_name, modulo_id=modulo_id,
mes_nombre=f"{mes}/{anio}", mes_nombre=f"{fecha_inicio} a {fecha_fin}",
dias_en_periodo=data['dias_en_periodo'], dias_en_periodo=data['dias_en_periodo'],
data_por_dia=data['data_por_dia'], data_por_dia=data['data_por_dia'],
totales=data['totales'], totales=data['totales'],
workers_list=workers_list, worker_actual=worker_id, workers_list=workers_list, worker_actual=worker_id,
dia_actual=dia_f, mes_actual=mes, anio_actual=anio, fecha_inicio=fecha_inicio, fecha_fin=fecha_fin,
anios_disponibles=anios_list) anios_disponibles=anios_list)

View File

@@ -25,15 +25,17 @@ def get_filter_catalogs():
return workers, modulos, zonas, anios_disponibles return workers, modulos, zonas, anios_disponibles
def get_filtered_rendiciones(mes, anio, dia, zona_id, modulo_id): def get_filtered_rendiciones(fecha_inicio, fecha_fin, zona_id, modulo_id):
from datetime import datetime
inicio = datetime.strptime(fecha_inicio, '%Y-%m-%d').date()
fin = datetime.strptime(fecha_fin, '%Y-%m-%d').date()
Companion = aliased(Worker) Companion = aliased(Worker)
filters = [ filters = [
func.strftime('%m', Rendicion.fecha) == mes, Rendicion.fecha >= inicio,
func.strftime('%Y', Rendicion.fecha) == anio, Rendicion.fecha <= fin,
] ]
if dia:
filters.append(func.strftime('%d', Rendicion.fecha) == dia.zfill(2))
if zona_id: if zona_id:
filters.append(Modulo.zona_id == zona_id) filters.append(Modulo.zona_id == zona_id)
if modulo_id: if modulo_id:

View File

@@ -1,5 +1,4 @@
import calendar from datetime import date, datetime, timedelta
from datetime import date, datetime
from sqlalchemy import func from sqlalchemy import func
from sqlalchemy.orm import aliased from sqlalchemy.orm import aliased
from models.models import db, Modulo, Worker, Rendicion, RendicionItem from models.models import db, Modulo, Worker, Rendicion, RendicionItem
@@ -9,26 +8,33 @@ Companion = aliased(Worker, name='companion')
WEEKDAY_SHORT = ['L', 'M', 'M', 'J', 'V', 'S', 'D'] WEEKDAY_SHORT = ['L', 'M', 'M', 'J', 'V', 'S', 'D']
def _fecha_filters(anio, mes, dia_f): def _parse_date(date_str):
return datetime.strptime(date_str, '%Y-%m-%d').date()
def _fecha_filters(fecha_inicio, fecha_fin):
filters = [ filters = [
func.strftime('%m', Rendicion.fecha) == mes, Rendicion.fecha >= _parse_date(fecha_inicio),
func.strftime('%Y', Rendicion.fecha) == anio, Rendicion.fecha <= _parse_date(fecha_fin),
] ]
if dia_f:
filters.append(func.strftime('%d', Rendicion.fecha) == dia_f.zfill(2))
return filters return filters
def _dias_en_periodo(anio, mes): def _dias_en_periodo(fecha_inicio, fecha_fin):
_, num_dias = calendar.monthrange(int(anio), int(mes)) inicio = _parse_date(fecha_inicio)
return [f'{d:02d}' for d in range(1, num_dias + 1)] fin = _parse_date(fecha_fin)
if inicio > fin:
inicio, fin = fin, inicio
return [f'{(inicio + timedelta(days=i)).strftime("%d")}' for i in range((fin - inicio).days + 1)]
def _dias_con_nombre(anio, mes): def _dias_con_nombre(fecha_inicio, fecha_fin):
_, num_dias = calendar.monthrange(int(anio), int(mes)) inicio = _parse_date(fecha_inicio)
fin = _parse_date(fecha_fin)
return [ return [
{'num': f'{d:02d}', 'name': WEEKDAY_SHORT[date(int(anio), int(mes), d).weekday()]} {'num': (inicio + timedelta(days=i)).strftime('%d'),
for d in range(1, num_dias + 1) 'name': WEEKDAY_SHORT[(inicio + timedelta(days=i)).weekday()]}
for i in range((fin - inicio).days + 1)
] ]
@@ -64,10 +70,10 @@ def _calcular_horas(hora_in, hora_out):
return 0, "0:00" return 0, "0:00"
def get_modulo_periodo_data(modulo_id, anio, mes, dia_f, worker_id): def get_modulo_periodo_data(modulo_id, fecha_inicio, fecha_fin, worker_id):
filters = [ filters = [
Rendicion.modulo_id == modulo_id, Rendicion.modulo_id == modulo_id,
*_fecha_filters(anio, mes, dia_f), *_fecha_filters(fecha_inicio, fecha_fin),
] ]
if worker_id: if worker_id:
filters.append(Rendicion.worker_id == worker_id) filters.append(Rendicion.worker_id == worker_id)
@@ -92,7 +98,7 @@ def get_modulo_periodo_data(modulo_id, anio, mes, dia_f, worker_id):
).group_by('dia').all() ).group_by('dia').all()
comisiones = {row[0]: row[1] for row in comisiones_rows} comisiones = {row[0]: row[1] for row in comisiones_rows}
dias_en_periodo = _dias_en_periodo(anio, mes) dias_en_periodo = _dias_en_periodo(fecha_inicio, fecha_fin)
data_por_dia = {d: { data_por_dia = {d: {
'debito': 0, 'credito': 0, 'mp': 0, 'efectivo': 0, 'debito': 0, 'credito': 0, 'mp': 0, 'efectivo': 0,
'gastos': 0, 'comision': 0, 'venta_total': 0, 'gastos': 0, 'comision': 0, 'venta_total': 0,
@@ -109,7 +115,7 @@ def get_modulo_periodo_data(modulo_id, anio, mes, dia_f, worker_id):
'gastos': gastos, 'venta_total': vt, 'comision': comisiones.get(d, 0), 'gastos': gastos, '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']} totales_mes = {k: sum(d[k] for d in data_por_dia.values()) for k in (next(iter(data_por_dia.values()), {})).keys()}
dias_activos = sum(1 for d in data_por_dia.values() if d['venta_total'] > 0) dias_activos = sum(1 for d in data_por_dia.values() if d['venta_total'] > 0)
return { return {
@@ -120,10 +126,10 @@ def get_modulo_periodo_data(modulo_id, anio, mes, dia_f, worker_id):
} }
def get_comisiones_data(modulo_id, anio, mes, dia_f, worker_id): def get_comisiones_data(modulo_id, fecha_inicio, fecha_fin, worker_id):
filters = [ filters = [
Rendicion.modulo_id == modulo_id, Rendicion.modulo_id == modulo_id,
*_fecha_filters(anio, mes, dia_f), *_fecha_filters(fecha_inicio, fecha_fin),
] ]
if worker_id: if worker_id:
filters.append(db.or_(Rendicion.worker_id == worker_id, Rendicion.companion_id == worker_id)) filters.append(db.or_(Rendicion.worker_id == worker_id, Rendicion.companion_id == worker_id))
@@ -166,14 +172,14 @@ def get_comisiones_data(modulo_id, anio, mes, dia_f, worker_id):
return { return {
'workers_data': dict(sorted(workers_data.items(), key=lambda x: x[1]['name'])), 'workers_data': dict(sorted(workers_data.items(), key=lambda x: x[1]['name'])),
'dias_en_periodo': _dias_en_periodo(anio, mes), 'dias_en_periodo': _dias_en_periodo(fecha_inicio, fecha_fin),
} }
def get_horarios_data(modulo_id, anio, mes, dia_f, worker_id): def get_horarios_data(modulo_id, fecha_inicio, fecha_fin, worker_id):
filters = [ filters = [
Rendicion.modulo_id == modulo_id, Rendicion.modulo_id == modulo_id,
*_fecha_filters(anio, mes, dia_f), *_fecha_filters(fecha_inicio, fecha_fin),
] ]
if worker_id: if worker_id:
filters.append(db.or_(Rendicion.worker_id == worker_id, Rendicion.companion_id == worker_id)) filters.append(db.or_(Rendicion.worker_id == worker_id, Rendicion.companion_id == worker_id))
@@ -212,14 +218,14 @@ def get_horarios_data(modulo_id, anio, mes, dia_f, worker_id):
return { return {
'workers_data': workers_data, 'workers_data': workers_data,
'dias_en_periodo': _dias_con_nombre(anio, mes), 'dias_en_periodo': _dias_con_nombre(fecha_inicio, fecha_fin),
} }
def get_cc_data(modulo_id, anio, mes, dia_f, worker_id): def get_cc_data(modulo_id, fecha_inicio, fecha_fin, worker_id):
filters = [ filters = [
Rendicion.modulo_id == modulo_id, Rendicion.modulo_id == modulo_id,
*_fecha_filters(anio, mes, dia_f), *_fecha_filters(fecha_inicio, fecha_fin),
] ]
if worker_id: if worker_id:
filters.append(Rendicion.worker_id == worker_id) filters.append(Rendicion.worker_id == worker_id)
@@ -231,7 +237,7 @@ def get_cc_data(modulo_id, anio, mes, dia_f, worker_id):
func.sum(Rendicion.venta_debito + Rendicion.venta_credito + Rendicion.venta_mp + Rendicion.venta_efectivo), func.sum(Rendicion.venta_debito + Rendicion.venta_credito + Rendicion.venta_mp + Rendicion.venta_efectivo),
).filter(*filters).group_by('dia').all() ).filter(*filters).group_by('dia').all()
dias_en_periodo = _dias_en_periodo(anio, mes) dias_en_periodo = _dias_en_periodo(fecha_inicio, fecha_fin)
data_por_dia = {d: {'red_compra': 0, 'efectivo': 0, 'total_trans': 0, 'venta_neta': 0} for d in dias_en_periodo} data_por_dia = {d: {'red_compra': 0, 'efectivo': 0, 'total_trans': 0, 'venta_neta': 0} for d in dias_en_periodo}
totales = {'red_compra': 0, 'efectivo': 0, 'total_trans': 0, 'venta_neta': 0} totales = {'red_compra': 0, 'efectivo': 0, 'total_trans': 0, 'venta_neta': 0}
@@ -246,16 +252,16 @@ def get_cc_data(modulo_id, anio, mes, dia_f, worker_id):
totales[k] += data_por_dia[dia][k] totales[k] += data_por_dia[dia][k]
return { return {
'dias_en_periodo': _dias_con_nombre(anio, mes), 'dias_en_periodo': _dias_con_nombre(fecha_inicio, fecha_fin),
'data_por_dia': data_por_dia, 'data_por_dia': data_por_dia,
'totales': totales, 'totales': totales,
} }
def get_iva_data(modulo_id, anio, mes, dia_f, worker_id): def get_iva_data(modulo_id, fecha_inicio, fecha_fin, worker_id):
filters = [ filters = [
Rendicion.modulo_id == modulo_id, Rendicion.modulo_id == modulo_id,
*_fecha_filters(anio, mes, dia_f), *_fecha_filters(fecha_inicio, fecha_fin),
] ]
if worker_id: if worker_id:
filters.append(Rendicion.worker_id == worker_id) filters.append(Rendicion.worker_id == worker_id)
@@ -266,7 +272,7 @@ def get_iva_data(modulo_id, anio, mes, dia_f, worker_id):
func.sum(Rendicion.venta_debito + Rendicion.venta_credito + Rendicion.venta_mp), func.sum(Rendicion.venta_debito + Rendicion.venta_credito + Rendicion.venta_mp),
).filter(*filters).group_by('dia').all() ).filter(*filters).group_by('dia').all()
dias_en_periodo = _dias_en_periodo(anio, mes) dias_en_periodo = _dias_en_periodo(fecha_inicio, fecha_fin)
data_por_dia = {d: {'efectivo': 0, 'tbk': 0, 'total': 0, 'porcentaje': 0} for d in dias_en_periodo} data_por_dia = {d: {'efectivo': 0, 'tbk': 0, 'total': 0, 'porcentaje': 0} for d in dias_en_periodo}
totales = {'efectivo': 0, 'tbk': 0, 'total': 0, 'porcentaje': 0} totales = {'efectivo': 0, 'tbk': 0, 'total': 0, 'porcentaje': 0}
@@ -287,7 +293,7 @@ def get_iva_data(modulo_id, anio, mes, dia_f, worker_id):
totales['porcentaje'] = round((totales['efectivo'] / totales['total']) * 100) totales['porcentaje'] = round((totales['efectivo'] / totales['total']) * 100)
return { return {
'dias_en_periodo': _dias_con_nombre(anio, mes), 'dias_en_periodo': _dias_con_nombre(fecha_inicio, fecha_fin),
'data_por_dia': data_por_dia, 'data_por_dia': data_por_dia,
'totales': totales, 'totales': totales,
} }

View File

@@ -1,3 +1,9 @@
function toggleOtroBanco(selectEl, wrapperId) {
const wrapper = document.getElementById(wrapperId);
if (!wrapper) return;
wrapper.style.display = selectEl.value === '__otro__' ? '' : 'none';
}
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
const editWorkerModal = document.getElementById('editWorkerModal'); const editWorkerModal = document.getElementById('editWorkerModal');
const confirmResetModal = document.getElementById('confirmResetPass'); const confirmResetModal = document.getElementById('confirmResetPass');
@@ -26,6 +32,24 @@ document.addEventListener("DOMContentLoaded", function () {
editWorkerModal.querySelector('#edit_worker_phone').value = button.getAttribute('data-phone'); editWorkerModal.querySelector('#edit_worker_phone').value = button.getAttribute('data-phone');
editWorkerModal.querySelector('#edit_worker_modulo').value = button.getAttribute('data-modulo'); editWorkerModal.querySelector('#edit_worker_modulo').value = button.getAttribute('data-modulo');
editWorkerModal.querySelector('#edit_worker_tipo').value = button.getAttribute('data-tipo'); editWorkerModal.querySelector('#edit_worker_tipo').value = button.getAttribute('data-tipo');
const bancoVal = button.getAttribute('data-nombre-banco');
const bancoSelect = editWorkerModal.querySelector('#edit_worker_nombre_banco');
const otroWrapper = editWorkerModal.querySelector('#edit_otro_banco_wrapper');
const otroInput = editWorkerModal.querySelector('#edit_worker_nombre_banco_otro');
const isCustom = bancoVal && !Array.from(bancoSelect.options).some(o => o.value === bancoVal);
if (isCustom) {
bancoSelect.value = '__otro__';
otroWrapper.style.display = '';
otroInput.value = bancoVal;
} else {
bancoSelect.value = bancoVal;
otroWrapper.style.display = 'none';
otroInput.value = '';
}
editWorkerModal.querySelector('#edit_worker_numero_cuenta').value = button.getAttribute('data-numero-cuenta');
editWorkerModal.querySelector('#edit_worker_rut_banco').value = button.getAttribute('data-rut-banco');
editWorkerModal.querySelector('#edit_worker_tipo_cuenta').value = button.getAttribute('data-tipo-cuenta');
}); });
} }
@@ -46,8 +70,12 @@ document.addEventListener("DOMContentLoaded", function () {
if (rutInput) { if (rutInput) {
window.formatHelpers.bindRutInput('#rutInput'); window.formatHelpers.bindRutInput('#rutInput');
} }
const addRutInput = document.getElementById('addRutInput');
if (addRutInput) {
window.formatHelpers.bindRutInput('#addRutInput');
}
window.formatHelpers.bindPhoneInput('.phone-input, #phoneInput'); window.formatHelpers.bindPhoneInput('.phone-input, #phoneInput, #addPhoneInput');
const searchInputWorker = document.getElementById('searchWorker'); const searchInputWorker = document.getElementById('searchWorker');
const moduleSelectFilter = document.getElementById('filterModule'); const moduleSelectFilter = document.getElementById('filterModule');

View File

@@ -14,40 +14,13 @@
<form method="GET" action="{{ url_for('admin.admin_rendiciones') }}" id="filterForm"> <form method="GET" action="{{ url_for('admin.admin_rendiciones') }}" id="filterForm">
<div class="row g-2 align-items-end"> <div class="row g-2 align-items-end">
<div class="col-md-2"> <div class="col-md-2">
<label class="form-label small text-muted mb-1">Año</label> <label class="form-label small text-muted mb-1">Desde</label>
<select name="anio" class="form-select form-select-sm"> <input type="date" name="fecha_inicio" class="form-control form-control-sm" value="{{ fecha_inicio }}">
{% for anio in anios_disponibles %}
<option value="{{ anio }}" {% if anio_actual == anio %}selected{% endif %}>{{ anio }}</option>
{% endfor %}
</select>
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<label class="form-label small text-muted mb-1">Mes</label> <label class="form-label small text-muted mb-1">Hasta</label>
<select name="mes" class="form-select form-select-sm"> <input type="date" name="fecha_fin" class="form-control form-control-sm" value="{{ fecha_fin }}">
<option value="01" {% if mes_actual == '01' %}selected{% endif %}>Enero</option>
<option value="02" {% if mes_actual == '02' %}selected{% endif %}>Febrero</option>
<option value="03" {% if mes_actual == '03' %}selected{% endif %}>Marzo</option>
<option value="04" {% if mes_actual == '04' %}selected{% endif %}>Abril</option>
<option value="05" {% if mes_actual == '05' %}selected{% endif %}>Mayo</option>
<option value="06" {% if mes_actual == '06' %}selected{% endif %}>Junio</option>
<option value="07" {% if mes_actual == '07' %}selected{% endif %}>Julio</option>
<option value="08" {% if mes_actual == '08' %}selected{% endif %}>Agosto</option>
<option value="09" {% if mes_actual == '09' %}selected{% endif %}>Septiembre</option>
<option value="10" {% if mes_actual == '10' %}selected{% endif %}>Octubre</option>
<option value="11" {% if mes_actual == '11' %}selected{% endif %}>Noviembre</option>
<option value="12" {% if mes_actual == '12' %}selected{% endif %}>Diciembre</option>
</select>
</div> </div>
<div class="col-md-1">
<label class="form-label small text-muted mb-1">Día</label>
<select name="dia" class="form-select form-select-sm">
<option value="">Todos</option>
{% for d in dias_disponibles %}
<option value="{{ d }}" {% if dia_actual == d %}selected{% endif %}>{{ d }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-3"> <div class="col-md-3">
<label class="form-label small text-muted mb-1">Zona</label> <label class="form-label small text-muted mb-1">Zona</label>
<select name="zona_id" id="zonaSelect" class="form-select form-select-sm"> <select name="zona_id" id="zonaSelect" class="form-select form-select-sm">
@@ -66,8 +39,7 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<div class="col-md-2">
<div class="col-md-1">
<button type="submit" class="btn btn-primary btn-sm w-100"><i class="bi bi-search"></i> Filtrar</button> <button type="submit" class="btn btn-primary btn-sm w-100"><i class="bi bi-search"></i> Filtrar</button>
</div> </div>
</div> </div>

View File

@@ -16,14 +16,11 @@
</div> </div>
</div> </div>
{{ report_filters( {{ report_filters(
url_for('admin.report_modulo_periodo', modulo_id=modulo_id), url_for('admin.report_modulo_centros_comerciales', modulo_id=modulo_id),
workers_list, workers_list,
worker_actual, worker_actual,
dia_actual, fecha_inicio,
[('01','Ene'),('02','Feb'),('03','Mar'),('04','Abr'),('05','May'),('06','Jun'),('07','Jul'),('08','Ago'),('09','Sep'),('10','Oct'),('11','Nov'),('12','Dic')], fecha_fin
mes_actual,
anios_disponibles,
anio_actual
) }} ) }}
<div class="card shadow-sm border-0"> <div class="card shadow-sm border-0">
<div class="card-body p-0"> <div class="card-body p-0">

View File

@@ -16,14 +16,11 @@
</div> </div>
</div> </div>
{{ report_filters( {{ report_filters(
url_for('admin.report_modulo_periodo', modulo_id=modulo_id), url_for('admin.report_modulo_comisiones', modulo_id=modulo_id),
workers_list, workers_list,
worker_actual, worker_actual,
dia_actual, fecha_inicio,
[('01','Ene'),('02','Feb'),('03','Mar'),('04','Abr'),('05','May'),('06','Jun'),('07','Jul'),('08','Ago'),('09','Sep'),('10','Oct'),('11','Nov'),('12','Dic')], fecha_fin
mes_actual,
anios_disponibles,
anio_actual
) }} ) }}
<div class="card shadow-sm border-0"> <div class="card shadow-sm border-0">
<div class="card-body"> <div class="card-body">

View File

@@ -16,14 +16,11 @@
</div> </div>
</div> </div>
{{ report_filters( {{ report_filters(
url_for('admin.report_modulo_periodo', modulo_id=modulo_id), url_for('admin.report_modulo_horarios', modulo_id=modulo_id),
workers_list, workers_list,
worker_actual, worker_actual,
dia_actual, fecha_inicio,
[('01','Ene'),('02','Feb'),('03','Mar'),('04','Abr'),('05','May'),('06','Jun'),('07','Jul'),('08','Ago'),('09','Sep'),('10','Oct'),('11','Nov'),('12','Dic')], fecha_fin
mes_actual,
anios_disponibles,
anio_actual
) }} ) }}
{% if workers_data %} {% if workers_data %}
<div class="card shadow-sm border-0"> <div class="card shadow-sm border-0">

View File

@@ -16,14 +16,11 @@
</div> </div>
</div> </div>
{{ report_filters( {{ report_filters(
url_for('admin.report_modulo_periodo', modulo_id=modulo_id), url_for('admin.report_modulo_calculo_iva', modulo_id=modulo_id),
workers_list, workers_list,
worker_actual, worker_actual,
dia_actual, fecha_inicio,
[('01','Ene'),('02','Feb'),('03','Mar'),('04','Abr'),('05','May'),('06','Jun'),('07','Jul'),('08','Ago'),('09','Sep'),('10','Oct'),('11','Nov'),('12','Dic')], fecha_fin
mes_actual,
anios_disponibles,
anio_actual
) }} ) }}
<div class="card shadow-sm border-0"> <div class="card shadow-sm border-0">
<div class="card-body p-0"> <div class="card-body p-0">

View File

@@ -55,11 +55,8 @@
url_for('admin.report_modulo_periodo', modulo_id=modulo_id), url_for('admin.report_modulo_periodo', modulo_id=modulo_id),
workers_list, workers_list,
worker_actual, worker_actual,
dia_actual, fecha_inicio,
[('01','Ene'),('02','Feb'),('03','Mar'),('04','Abr'),('05','May'),('06','Jun'),('07','Jul'),('08','Ago'),('09','Sep'),('10','Oct'),('11','Nov'),('12','Dic')], fecha_fin
mes_actual,
anios_disponibles,
anio_actual
) }} ) }}
<div class="card shadow-sm border-0"> <div class="card shadow-sm border-0">

View File

@@ -1,53 +1,20 @@
{% extends "macros/base.html" %} {% extends "macros/base.html" %}
{% from 'macros/modals.html' import confirm_modal, edit_worker_modal %} {% from 'macros/modals.html' import confirm_modal, edit_worker_modal, add_worker_modal %}
{% from "macros/ui.html" import flashed_messages %} {% from "macros/ui.html" import flashed_messages %}
{% block title %}Gestión de Trabajadores{% endblock %} {% block title %}Gestión de Trabajadores{% endblock %}
{% block content %} {% block content %}
<h2 class="mb-4">Gestión de Trabajadores</h2>
{{ flashed_messages() }} {{ flashed_messages() }}
{{ edit_worker_modal(modulos) }} {{ edit_worker_modal(modulos, bancos) }}
{{ add_worker_modal(modulos, bancos) }}
<div class="card mb-4"> <div class="d-flex justify-content-between align-items-center mb-3">
<div class="card-header bg-primary text-white">Agregar Nuevo Trabajador</div> <h2 class="mb-0">Gestión de Trabajadores</h2>
<div class="card-body"> <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addWorkerModal">
<form method="POST" action="{{ url_for('admin.manage_workers') }}"> <i class="bi bi-plus-lg"></i> Agregar Trabajador
<div class="row g-3"> </button>
<div class="col-md-2">
<label class="form-label">RUT</label>
<input type="text" class="form-control" name="rut" id="rutInput" placeholder="12.345.678-9" value="{{ form.get('rut', '') }}" maxlength="12" required>
</div>
<div class="col-md-3">
<label class="form-label">Nombre Completo</label>
<input type="text" class="form-control" name="name" value="{{ form.get('name', '') }}" required>
</div>
<div class="col-md-2">
<label class="form-label">Teléfono</label>
<input type="text" class="form-control" name="phone" id="phoneInput" placeholder="9 1234 5678" value="{{ form.get('phone', '') }}" required>
</div>
<div class="col-md-3">
<label class="form-label">Módulo Asignado</label>
<select class="form-select" name="modulo_id" required>
<option value="" selected disabled>Seleccionar Módulo...</option>
{% for mod in modulos %}
<option value="{{ mod[0] }}">{{ mod[2] }} - {{ mod[1] }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-2">
<label class="form-label">Jornada</label>
<select class="form-select" name="tipo" required>
<option value="Full Time" {% if form.get('tipo') == 'Full Time' %}selected{% endif %}>Full Time</option>
<option value="Part Time" {% if form.get('tipo') == 'Part Time' %}selected{% endif %}>Part Time</option>
</select>
</div>
</div>
<button type="submit" class="btn btn-primary mt-3">Guardar Trabajador</button>
</form>
</div>
</div> </div>
<div class="card mb-4 shadow-sm border-0"> <div class="card mb-4 shadow-sm border-0">
@@ -88,6 +55,10 @@
<th>Teléfono</th> <th>Teléfono</th>
<th>Módulo</th> <th>Módulo</th>
<th>Tipo</th> <th>Tipo</th>
<th>Banco</th>
<th>N° Cuenta</th>
<th>RUT Cuenta</th>
<th>Tipo Cuenta</th>
<th class="text-end">Acciones</th> <th class="text-end">Acciones</th>
</tr> </tr>
</thead> </thead>
@@ -103,6 +74,10 @@
{{ worker[6] }} {{ worker[6] }}
</span> </span>
</td> </td>
<td class="align-middle">{{ worker[7] or '-' }}</td>
<td class="align-middle">{{ worker[8] or '-' }}</td>
<td class="align-middle">{{ worker[10] or '-' }}</td>
<td class="align-middle">{{ worker[9] or '-' }}</td>
<td class="text-end"> <td class="text-end">
<button type="button" <button type="button"
class="btn btn-primary btn-sm btn-edit-sm" class="btn btn-primary btn-sm btn-edit-sm"
@@ -113,7 +88,11 @@
data-name="{{ worker[2] }}" data-name="{{ worker[2] }}"
data-phone="{{ worker[3] }}" data-phone="{{ worker[3] }}"
data-modulo="{{ worker[5] }}" data-modulo="{{ worker[5] }}"
data-tipo="{{ worker[6] }}"> data-tipo="{{ worker[6] }}"
data-nombre-banco="{{ worker[7] }}"
data-numero-cuenta="{{ worker[8] }}"
data-tipo-cuenta="{{ worker[9] }}"
data-rut-banco="{{ worker[10] }}">
<i class="bi bi-pencil"></i> <i class="bi bi-pencil"></i>
</button> </button>
@@ -132,7 +111,7 @@
</tr> </tr>
{% else %} {% else %}
<tr> <tr>
<td colspan="6" class="text-center py-3 text-muted">No hay trabajadores registrados.</td> <td colspan="10" class="text-center py-3 text-muted">No hay trabajadores registrados.</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@@ -82,7 +82,7 @@
</div> </div>
{% endmacro %} {% endmacro %}
{% macro edit_worker_modal(modulos) %} {% macro edit_worker_modal(modulos, bancos) %}
<div class="modal fade" id="editWorkerModal" tabindex="-1" aria-hidden="true"> <div class="modal fade" id="editWorkerModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
@@ -120,6 +120,37 @@
<option value="Part Time">Part Time</option> <option value="Part Time">Part Time</option>
</select> </select>
</div> </div>
<div class="mb-3">
<label class="form-label">Banco</label>
<select class="form-select" name="nombre_banco" id="edit_worker_nombre_banco" onchange="toggleOtroBanco(this, 'edit_otro_banco_wrapper')">
<option value="">Seleccionar...</option>
{% for b in bancos %}
<option value="{{ b }}">{{ b }}</option>
{% endfor %}
<option value="__otro__">Otro...</option>
</select>
<div id="edit_otro_banco_wrapper" class="mt-1" style="display:none">
<input type="text" class="form-control form-control-sm" name="nombre_banco_otro" id="edit_worker_nombre_banco_otro" placeholder="Especificar banco...">
</div>
</div>
<div class="mb-3">
<label class="form-label">N° Cuenta</label>
<input type="text" class="form-control" name="numero_cuenta" id="edit_worker_numero_cuenta" placeholder="Ej: 12345678">
</div>
<div class="mb-3">
<label class="form-label">RUT Cuenta</label>
<input type="text" class="form-control" name="rut_banco" id="edit_worker_rut_banco" placeholder="Ej: 12.345.678-9">
</div>
<div class="mb-3">
<label class="form-label">Tipo Cuenta</label>
<select class="form-select" name="tipo_cuenta" id="edit_worker_tipo_cuenta">
<option value="">Seleccionar...</option>
<option value="Cuenta Corriente">Cuenta Corriente</option>
<option value="Cuenta Vista">Cuenta Vista</option>
<option value="Cuenta Rut">Cuenta Rut</option>
<option value="Cuenta de Ahorro">Cuenta de Ahorro</option>
</select>
</div>
</form> </form>
<hr> <hr>
@@ -144,6 +175,88 @@
</div> </div>
{% endmacro %} {% endmacro %}
{% macro add_worker_modal(modulos, bancos) %}
<div class="modal fade" id="addWorkerModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Agregar Nuevo Trabajador</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="POST" action="{{ url_for('admin.manage_workers') }}">
<div class="modal-body">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">RUT</label>
<input type="text" class="form-control" name="rut" id="addRutInput" placeholder="12.345.678-9" maxlength="12" required>
</div>
<div class="col-md-8">
<label class="form-label">Nombre Completo</label>
<input type="text" class="form-control" name="name" required>
</div>
<div class="col-md-4">
<label class="form-label">Teléfono</label>
<input type="text" class="form-control" name="phone" id="addPhoneInput" placeholder="9 1234 5678" required>
</div>
<div class="col-md-4">
<label class="form-label">Módulo Asignado</label>
<select class="form-select" name="modulo_id" required>
<option value="" selected disabled>Seleccionar Módulo...</option>
{% for mod in modulos %}
<option value="{{ mod[0] }}">{{ mod[2] }} - {{ mod[1] }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-4">
<label class="form-label">Jornada</label>
<select class="form-select" name="tipo" required>
<option value="Full Time">Full Time</option>
<option value="Part Time">Part Time</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Banco</label>
<select class="form-select" name="nombre_banco" onchange="toggleOtroBanco(this, 'add_otro_banco_wrapper')">
<option value="">Seleccionar...</option>
{% for b in bancos %}
<option value="{{ b }}">{{ b }}</option>
{% endfor %}
<option value="__otro__">Otro...</option>
</select>
<div id="add_otro_banco_wrapper" class="mt-1" style="display:none">
<input type="text" class="form-control form-control-sm" name="nombre_banco_otro" placeholder="Especificar banco...">
</div>
</div>
<div class="col-md-4">
<label class="form-label">N° Cuenta</label>
<input type="text" class="form-control" name="numero_cuenta" placeholder="Ej: 12345678">
</div>
<div class="col-md-4">
<label class="form-label">RUT Cuenta</label>
<input type="text" class="form-control" name="rut_banco" placeholder="Ej: 12.345.678-9">
</div>
<div class="col-md-4">
<label class="form-label">Tipo Cuenta</label>
<select class="form-select" name="tipo_cuenta">
<option value="">Seleccionar...</option>
<option value="Cuenta Corriente">Cuenta Corriente</option>
<option value="Cuenta Vista">Cuenta Vista</option>
<option value="Cuenta Rut">Cuenta Rut</option>
<option value="Cuenta de Ahorro">Cuenta de Ahorro</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-primary">Guardar Trabajador</button>
</div>
</form>
</div>
</div>
</div>
{% endmacro %}
{% macro rendicion_detail_modal(rendicion, items, total_calculado, comision_total) %} {% macro rendicion_detail_modal(rendicion, items, total_calculado, comision_total) %}
<div class="modal fade" id="viewRendicion{{ rendicion[0] }}" tabindex="-1" aria-hidden="true"> <div class="modal fade" id="viewRendicion{{ rendicion[0] }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable"> <div class="modal-dialog modal-xl modal-dialog-scrollable">
@@ -575,30 +688,17 @@
</div> </div>
{% endmacro %} {% endmacro %}
{% macro report_filters(action_url, workers, selected_worker, selected_dia, meses, mes_act, anios, anio_act) %} {% macro report_filters(action_url, workers, selected_worker, fecha_inicio, fecha_fin) %}
<div class="card shadow-sm mb-4 border-0 bg-dark-subtle"> <div class="card shadow-sm mb-4 border-0 bg-dark-subtle">
<div class="card-body p-3"> <div class="card-body p-3">
<form method="GET" action="{{ action_url }}" class="row g-2 align-items-end"> <form method="GET" action="{{ action_url }}" class="row g-2 align-items-end">
<div class="col-md-2"> <div class="col-md-2">
<label class="form-label small text-muted mb-1">Año/Mes</label> <label class="form-label small text-muted mb-1">Desde</label>
<div class="input-group input-group-sm"> <input type="date" name="fecha_inicio" class="form-control form-control-sm" value="{{ fecha_inicio }}">
<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>
<div class="col-md-2"> <div class="col-md-2">
<label class="form-label small text-muted mb-1">Día (Opcional)</label> <label class="form-label small text-muted mb-1">Hasta</label>
<select name="dia" class="form-select form-select-sm"> <input type="date" name="fecha_fin" class="form-control form-control-sm" value="{{ fecha_fin }}">
<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>
<div class="col-md-3"> <div class="col-md-3">
<label class="form-label small text-muted mb-1">Trabajador</label> <label class="form-label small text-muted mb-1">Trabajador</label>

View File

@@ -59,8 +59,7 @@ def admin_required(f):
def get_report_params(): def get_report_params():
"""Captura filtros de la URL o establece valores por defecto.""" """Captura filtros de la URL o establece valores por defecto."""
hoy = date.today() hoy = date.today()
anio = request.args.get('anio', str(hoy.year)) fecha_inicio = request.args.get('fecha_inicio', f"{hoy.year}-{hoy.month:02d}-01")
mes = request.args.get('mes', f"{hoy.month:02d}") fecha_fin = request.args.get('fecha_fin', hoy.strftime('%Y-%m-%d'))
dia = request.args.get('dia')
worker_id = request.args.get('worker_id') worker_id = request.args.get('worker_id')
return anio, mes, dia, worker_id return fecha_inicio, fecha_fin, worker_id