Skip to content

Fix: puntos duplicados en ranking J8 y acumulado de Libertadores - #3

Merged
danguz80 merged 1 commit into
mainfrom
claude/libertadores-penalties-admin-s9py1b
Aug 21, 2026
Merged

Fix: puntos duplicados en ranking J8 y acumulado de Libertadores#3
danguz80 merged 1 commit into
mainfrom
claude/libertadores-penalties-admin-s9py1b

Conversation

@danguz80

Copy link
Copy Markdown
Owner

Problema

Daniel Guzmán Sagredo (y potencialmente otros usuarios) aparecía en el Ranking Jornada 8 con más puntos de los que realmente ganó (2 en vez de 1, según su propio desglose de partidos), y en el Ranking Acumulado saltó de 170 a 178 pts entre J7 y J8 (+8), cuando la suma real de partidos (1) + bonus de clasificación (2) debía dar +3.

Causas encontradas (por lectura de código; no tuve acceso a la base de datos productiva desde este entorno para confirmar con datos en vivo — el puerto 5432 está bloqueado)

  1. calcularPuntosLibertadores.js: el borrado de libertadores_puntos_clasificacion antes de recalcular el bonus de "equipo que avanza" solo se ejecutaba cuando se recalculaba una jornada específica (8/9/10). Si un admin recalculaba todas las jornadas (sin filtrar), ese borrado se saltaba por completo, y como el INSERT no tenía ON CONFLICT, cada recálculo global repetido iba acumulando filas duplicadas del bonus de clasificación para el mismo usuario/partido — inflando el acumulado. Matemáticamente esto explica el excedente observado (8 en vez de 3): con el bonus de Flamengo (2 pts) duplicado ~3 veces, el acumulado sube ~6 en vez de 2.

  2. rankingsLibertadores.js / ganadoresJornadaLibertadores.js: las sumas de puntos de partidos (SUM(lp.puntos)) no verificaban que el partido_id referenciado siguiera existiendo. Un pronóstico "huérfano" que quedó apuntando a un partido borrado (p. ej. al regenerar el fixture de octavos) seguía sumando puntos en el ranking aunque ya no apareciera en ningún lado visible para el usuario (el endpoint que arma la tabla de "Partidos" sí hace ese INNER JOIN y por eso no lo mostraba). Esto explica el 2 en vez de 1 en el Ranking de Jornada 8.

Cambios

  • calcularPuntosLibertadores.js: el borrado de clasificación para J8/J9/J10 ahora también corre al recalcular todas las jornadas; se agregó ON CONFLICT (usuario_id, partido_id, jornada_numero) DO UPDATE como defensa adicional.
  • rankingsLibertadores.js (/jornada/:numero, /acumulado/:numero, /actual) y ganadoresJornadaLibertadores.js (cálculo de ganador de jornada y las consultas duplicadas del PDF): se agregó INNER JOIN libertadores_partidos en las subconsultas de puntos de partidos para descartar pronósticos huérfanos.

Después de este deploy

Recomiendo volver a presionar "Calcular Puntos" para la Jornada 8 (y revisar 9/10 si ya se tocaron) para forzar un recálculo limpio con las reglas corregidas, y así limpiar cualquier fila duplicada que ya exista en libertadores_puntos_clasificacion.


🤖 Generated with Claude Code


Generated by Claude Code

Dos bugs de integridad de datos causaban que un usuario apareciera con
más puntos de los que realmente ganó en Jornada 8 (octavos VUELTA):

1. calcularPuntosLibertadores.js: al recalcular puntos SIN filtrar por
   jornada (botón 'Calcular Puntos' sobre 'todas las jornadas'), el
   borrado previo de libertadores_puntos_clasificacion solo corría si
   se pasaba un jornadaNumero específico (8/9/10). Como el INSERT no
   usaba ON CONFLICT, cada recálculo global sin filtro iba acumulando
   filas duplicadas del bonus por 'equipo que avanza', inflando el
   ranking acumulado. Ahora el borrado también corre para 8/9/10 al
   recalcular todas las jornadas, y el INSERT tiene ON CONFLICT ...
   DO UPDATE como defensa adicional.

2. rankingsLibertadores.js / ganadoresJornadaLibertadores.js: las
   sumas de puntos de partidos (SUM(lp.puntos)) no verificaban que el
   partido referenciado siguiera existiendo. Un pronóstico 'huérfano'
   que quedó apuntando a un partido_id borrado (p.ej. al regenerar el
   fixture de octavos) seguía sumando puntos en el ranking aunque ya
   no apareciera en ningún lado visible para el usuario. Se agregó
   INNER JOIN a libertadores_partidos en todas esas subconsultas para
   descartar pronósticos huérfanos, igual que ya hacía el endpoint de
   pronósticos por partido.

Tras este fix, recomendado volver a presionar 'Calcular Puntos' para
la Jornada 8 (y revisar 9/10 si ya se tocaron) para forzar un
recálculo limpio con las nuevas reglas.
Copilot AI lite review requested due to automatic review settings August 21, 2026 01:04
@danguz80
danguz80 merged commit 99624ac into main Aug 21, 2026
@netlify

netlify Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploy Preview for pollafutbol canceled.

Name Link
🔨 Latest commit 98fea5b
🔍 Latest deploy log https://app.netlify.com/projects/pollafutbol/deploys/6a87a42b2498a000088b90f0

1 similar comment
@netlify

netlify Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploy Preview for pollafutbol canceled.

Name Link
🔨 Latest commit 98fea5b
🔍 Latest deploy log https://app.netlify.com/projects/pollafutbol/deploys/6a87a42b2498a000088b90f0

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Corrige puntos duplicados en el ranking de Libertadores y excluye pronósticos asociados a partidos eliminados.

Changes:

  • Limpia bonuses de clasificación al recalcular jornadas y añade ON CONFLICT.
  • Filtra pronósticos huérfanos en rankings, ganadores y PDFs.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Final review status
server/routes/rankingsLibertadores.js Requiere filtrar también ultimaJornada en /actual; el pronóstico huérfano aún puede alterar la jornada mostrada. Moderate, 4 votes.
server/routes/ganadoresJornadaLibertadores.js Falta aplicar el JOIN en POST /acumulado, por lo que los pronósticos huérfanos aún pueden afectar al ganador. Critical, 3 votes.
server/routes/calcularPuntosLibertadores.js ON CONFLICT requiere una restricción única existente; debe añadirse una migración que limpie duplicados y cree/verifique dicha restricción. Critical, 4 votes.
Suppressed comments (2)

server/routes/calcularPuntosLibertadores.js:75

  • Cuando se recalculan todas las jornadas, estos DELETE eliminan J8, J9 y J10 antes de iniciar el bucle de recalculo, pero la ruta no usa una transacción. Un error, timeout o reinicio durante las muchas consultas posteriores puede dejar las tres jornadas sin ningún bonus de clasificación y los rankings perderán puntos hasta una ejecución manual exitosa. Ejecuten el borrado y la reconstrucción en una transacción (o reconstruyan en una tabla temporal y hagan un reemplazo atómico).
    for (const jn of jornadasClasifACalcular) {
      if (jn >= 8 && jn <= 10) {
        await pool.query(
          `DELETE FROM libertadores_puntos_clasificacion
           WHERE jornada_numero = $1`,
          [jn]
        );

server/routes/calcularPuntosLibertadores.js:64

  • Este comentario quedó desactualizado respecto al SQL: el INSERT sí usa ON CONFLICT en las líneas 388-392. Conviene explicar que el DELETE previo sigue siendo necesario para limpiar filas obsoletas antes de reconstruir los bonus, en lugar de documentar una condición que ya no existe.
    // (jornadaNumero no viene en el body), porque el INSERT de más abajo no usa
    // ON CONFLICT: si no se borra primero, cada vez que un admin presiona "Calcular
    // Puntos" sin filtrar jornada se van acumulando filas duplicadas de bonus de
    // clasificación (equipo que avanza) para el mismo usuario/partido, inflando el
    // ranking acumulado.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +388 to +392
ON CONFLICT (usuario_id, partido_id, jornada_numero)
DO UPDATE SET
equipo_clasificado = EXCLUDED.equipo_clasificado,
fase_clasificado = EXCLUDED.fase_clasificado,
puntos = EXCLUDED.puntos
SELECT COALESCE(SUM(lp.puntos), 0) as puntos_partidos
FROM libertadores_pronosticos lp
INNER JOIN libertadores_jornadas lj ON lp.jornada_id = lj.id
INNER JOIN libertadores_partidos p ON p.id = lp.partido_id
Comment on lines +212 to +213
-- INNER JOIN a partidos: descarta pronósticos huérfanos de partidos borrados
INNER JOIN libertadores_partidos p ON p.id = lp.partido_id
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants