← back to herto2007-prog__fairpadel-backend

Function bodies 439 total

All specs Real LLM only Function bodies
generarJugadores function · typescript · L37-L77 (41 LOC)
prisma/seed-test-players.ts
function generarJugadores() {
  const jugadores: {
    documento: string;
    nombre: string;
    apellido: string;
    genero: 'MASCULINO' | 'FEMENINO';
    email: string;
    telefono: string;
    ciudad: string;
  }[] = [];

  // 48 hombres (docs 2000001-2000048)
  for (let i = 0; i < 48; i++) {
    const doc = `${2000001 + i}`;
    jugadores.push({
      documento: doc,
      nombre: nombresM[i],
      apellido: apellidos[i],
      genero: 'MASCULINO',
      email: `jugador.m${i + 1}@test.com`,
      telefono: `+5959820${String(i + 1).padStart(5, '0')}`,
      ciudad: ciudades[i % ciudades.length],
    });
  }

  // 48 mujeres (docs 3000001-3000048)
  for (let i = 0; i < 48; i++) {
    const doc = `${3000001 + i}`;
    jugadores.push({
      documento: doc,
      nombre: nombresF[i],
      apellido: apellidos[i],
      genero: 'FEMENINO',
      email: `jugadora.f${i + 1}@test.com`,
      telefono: `+5959830${String(i + 1).padStart(5, '0')}`,
      ciudad: ciudades[i % ciudades.leng
inscribirParejas function · typescript · L218-L286 (69 LOC)
prisma/seed-test-players.ts
  async function inscribirParejas(
    players: any[],
    startIdx: number,
    categoryId: string,
    targetPairs: number,
  ) {
    let created = 0;

    for (let i = startIdx; i < players.length - 1 && created < targetPairs; i += 2) {
      const j1 = players[i];
      const j2 = players[i + 1];

      // Verificar si ya existe pareja inscrita en esta categoría del torneo
      const existingInscripcion = await prisma.inscripcion.findFirst({
        where: {
          tournamentId: torneo.id,
          categoryId,
          pareja: {
            OR: [
              { jugador1Id: j1.id, jugador2Id: j2.id },
              { jugador1Id: j2.id, jugador2Id: j1.id },
            ],
          },
        },
      });

      if (existingInscripcion) {
        created++;
        continue;
      }

      const pareja = await prisma.pareja.create({
        data: {
          jugador1Id: j1.id,
          jugador2Id: j2.id,
          jugador2Documento: j2.documento,
        },
      });

      c
main function · typescript · L124-L284 (161 LOC)
prisma/seed-torneo-masivo.ts
async function main() {
  const tournamentId = process.argv[2];
  if (!tournamentId) {
    console.error('❌ Uso: npx ts-node prisma/seed-torneo-masivo.ts <tournamentId>');
    process.exit(1);
  }

  // ── 1. Fetch tournament + categories ──
  const torneo = await prisma.tournament.findUnique({
    where: { id: tournamentId },
    include: {
      categorias: { include: { category: true } },
      modalidades: true,
    },
  });

  if (!torneo) {
    console.error(`❌ Torneo "${tournamentId}" no encontrado`);
    process.exit(1);
  }

  console.log(`\n🏆 Torneo: "${torneo.nombre}" (${torneo.estado})`);
  console.log(`   Categorías: ${torneo.categorias.length}`);

  // Map category name patterns to TournamentCategory IDs
  const catMap: Record<string, { tcId: string; catId: string; catName: string }> = {};
  for (const tc of torneo.categorias) {
    const name = tc.category.nombre.toLowerCase();
    catMap[`${tc.category.tipo}_${name}`] = {
      tcId: tc.id,
      catId: tc.categoryId,
 
findCat function · typescript · L160-L169 (10 LOC)
prisma/seed-torneo-masivo.ts
  function findCat(genero: 'MASCULINO' | 'FEMENINO', shortName: string) {
    // shortName = "8va", "7ma", "6ta", "5ta", "4ta", "3ra", "2da", "1ra"
    const entries = Object.entries(catMap);
    for (const [key, val] of entries) {
      if (key.startsWith(genero) && val.catName.toLowerCase().includes(shortName.toLowerCase())) {
        return val;
      }
    }
    return null;
  }
createPlayers function · typescript · L288-L295 (8 LOC)
prisma/seed-torneo-masivo.ts
async function createPlayers(
  count: number,
  genero: 'MASCULINO' | 'FEMENINO',
  docStart: number,
  namePool: string[],
  passwordHash: string,
  roleId: string,
): Promise<{ id: string; documento: string }[]> {
createPairsAndInscribe function · typescript · L335-L414 (80 LOC)
prisma/seed-torneo-masivo.ts
async function createPairsAndInscribe(
  players: { id: string; documento: string }[],
  startIdx: number,
  targetPairs: number,
  tournamentId: string,
  categoryId: string,
  modalidad: string,
  monto: number,
  comision: number,
): Promise<number> {
  let created = 0;

  for (let i = 0; i < targetPairs; i++) {
    const p1 = players[startIdx + i * 2];
    const p2 = players[startIdx + i * 2 + 1];

    if (!p1 || !p2) {
      console.error(`   ⚠️  No hay suficientes jugadores (necesitaba índice ${startIdx + i * 2 + 1})`);
      break;
    }

    // Check duplicate
    const existing = await prisma.inscripcion.findFirst({
      where: {
        tournamentId,
        categoryId,
        pareja: {
          OR: [
            { jugador1Id: p1.id, jugador2Id: p2.id },
            { jugador1Id: p2.id, jugador2Id: p1.id },
          ],
        },
      },
    });

    if (existing) {
      created++;
      continue;
    }

    const pareja = await prisma.pareja.create({
      data: {
    
AdminController.obtenerUsuarios method · typescript · L88-L93 (6 LOC)
src/admin/admin.controller.ts
  obtenerUsuarios(
    @Query('search') search?: string,
    @Query('estado') estado?: string,
  ) {
    return this.adminService.obtenerUsuarios(search, estado);
  }
Repobility · severity-and-effort ranking · https://repobility.com
AdminController.obtenerUsuariosPremium method · typescript · L216-L221 (6 LOC)
src/admin/admin.controller.ts
  obtenerUsuariosPremium(
    @Query('search') search?: string,
    @Query('estado') estado?: string,
  ) {
    return this.adminService.obtenerUsuariosPremium(search, estado);
  }
AdminController.seedTestData method · typescript · L277-L282 (6 LOC)
src/admin/admin.controller.ts
  seedTestData(
    @Param('id') id: string,
    @Body() dto: SeedTestDataDto,
  ) {
    return this.adminService.seedTestData(id, dto.parejasPorCategoria);
  }
AdminService.obtenerTorneosPendientes method · typescript · L21-L49 (29 LOC)
src/admin/admin.service.ts
  async obtenerTorneosPendientes() {
    const torneos = await this.prisma.tournament.findMany({
      where: {
        estado: 'PENDIENTE_APROBACION',
      },
      include: {
        organizador: {
          select: {
            id: true,
            nombre: true,
            apellido: true,
            email: true,
            telefono: true,
          },
        },
        categorias: {
          include: {
            category: true,
          },
        },
        modalidades: true,
      },
      orderBy: {
        createdAt: 'desc',
      },
    });

    return torneos;
  }
AdminService.aprobarTorneo method · typescript · L51-L70 (20 LOC)
src/admin/admin.service.ts
  async aprobarTorneo(id: string) {
    const torneo = await this.prisma.tournament.findUnique({
      where: { id },
    });

    if (!torneo) {
      throw new NotFoundException('Torneo no encontrado');
    }

    await this.prisma.tournament.update({
      where: { id },
      data: {
        estado: 'PUBLICADO',
      },
    });

    // TODO: Notificar al organizador

    return { message: 'Torneo aprobado' };
  }
AdminService.rechazarTorneo method · typescript · L72-L91 (20 LOC)
src/admin/admin.service.ts
  async rechazarTorneo(id: string, motivo: string) {
    const torneo = await this.prisma.tournament.findUnique({
      where: { id },
    });

    if (!torneo) {
      throw new NotFoundException('Torneo no encontrado');
    }

    await this.prisma.tournament.update({
      where: { id },
      data: {
        estado: 'RECHAZADO',
      },
    });

    // TODO: Notificar al organizador con el motivo

    return { message: 'Torneo rechazado' };
  }
AdminService.obtenerSolicitudesOrganizador method · typescript · L95-L121 (27 LOC)
src/admin/admin.service.ts
  async obtenerSolicitudesOrganizador(estado?: string) {
    const where: any = {};
    if (estado) {
      where.estado = estado;
    }

    const solicitudes = await this.prisma.solicitudOrganizador.findMany({
      where,
      include: {
        user: {
          select: {
            id: true,
            nombre: true,
            apellido: true,
            email: true,
            telefono: true,
            ciudad: true,
          },
        },
      },
      orderBy: {
        createdAt: 'desc',
      },
    });

    return solicitudes;
  }
AdminService.aprobarSolicitudOrganizador method · typescript · L123-L159 (37 LOC)
src/admin/admin.service.ts
  async aprobarSolicitudOrganizador(id: string) {
    const solicitud = await this.prisma.solicitudOrganizador.findUnique({
      where: { id },
      include: { user: true },
    });

    if (!solicitud) {
      throw new NotFoundException('Solicitud no encontrada');
    }

    // Buscar rol de organizador
    const rolOrganizador = await this.prisma.role.findUnique({
      where: { nombre: 'organizador' },
    });

    if (!rolOrganizador) {
      throw new NotFoundException('Rol organizador no encontrado');
    }

    // Asignar rol
    await this.prisma.userRole.create({
      data: {
        userId: solicitud.userId,
        roleId: rolOrganizador.id,
      },
    });

    // Actualizar solicitud
    await this.prisma.solicitudOrganizador.update({
      where: { id },
      data: { estado: 'APROBADA' },
    });

    // TODO: Notificar al usuario

    return { message: 'Solicitud aprobada' };
  }
AdminService.rechazarSolicitudOrganizador method · typescript · L161-L181 (21 LOC)
src/admin/admin.service.ts
  async rechazarSolicitudOrganizador(id: string, motivo: string) {
    const solicitud = await this.prisma.solicitudOrganizador.findUnique({
      where: { id },
    });

    if (!solicitud) {
      throw new NotFoundException('Solicitud no encontrada');
    }

    await this.prisma.solicitudOrganizador.update({
      where: { id },
      data: {
        estado: 'RECHAZADA',
        motivo,
      },
    });

    // TODO: Notificar al usuario con el motivo

    return { message: 'Solicitud rechazada' };
  }
If a scraper extracted this row, it came from Repobility (https://repobility.com)
AdminService.obtenerFotosModeracion method · typescript · L185-L212 (28 LOC)
src/admin/admin.service.ts
  async obtenerFotosModeracion() {
    const fotos = await this.prisma.foto.findMany({
      where: {
        estadoModeracion: 'PENDIENTE',
      },
      include: {
        user: {
          select: {
            id: true,
            nombre: true,
            apellido: true,
            email: true,
          },
        },
        tournament: {
          select: {
            id: true,
            nombre: true,
          },
        },
      },
      orderBy: {
        createdAt: 'desc',
      },
    });

    return fotos;
  }
AdminService.aprobarFoto method · typescript · L214-L231 (18 LOC)
src/admin/admin.service.ts
  async aprobarFoto(id: string) {
    const foto = await this.prisma.foto.findUnique({
      where: { id },
    });

    if (!foto) {
      throw new NotFoundException('Foto no encontrada');
    }

    await this.prisma.foto.update({
      where: { id },
      data: {
        estadoModeracion: 'APROBADA',
      },
    });

    return { message: 'Foto aprobada' };
  }
AdminService.eliminarFotoInapropiada method · typescript · L233-L262 (30 LOC)
src/admin/admin.service.ts
  async eliminarFotoInapropiada(id: string, motivo: string) {
    const foto = await this.prisma.foto.findUnique({
      where: { id },
    });

    if (!foto) {
      throw new NotFoundException('Foto no encontrada');
    }

    await this.prisma.foto.update({
      where: { id },
      data: {
        estadoModeracion: 'RECHAZADA',
      },
    });

    // Crear registro en moderación
    await this.prisma.fotoPerfilModeracion.create({
      data: {
        userId: foto.userId,
        fotoUrl: foto.urlImagen,
        estado: 'RECHAZADA',
        motivoRechazo: motivo,
      },
    });

    // TODO: Notificar al usuario

    return { message: 'Foto eliminada' };
  }
AdminService.obtenerUsuarios method · typescript · L266-L303 (38 LOC)
src/admin/admin.service.ts
  async obtenerUsuarios(search?: string, estado?: string) {
    const where: any = {};

    if (search) {
      where.OR = [
        { nombre: { contains: search, mode: 'insensitive' } },
        { apellido: { contains: search, mode: 'insensitive' } },
        { email: { contains: search, mode: 'insensitive' } },
        { documento: { contains: search } },
      ];
    }

    if (estado) {
      where.estado = estado;
    }

    const usuarios = await this.prisma.user.findMany({
      where,
      select: {
        id: true,
        nombre: true,
        apellido: true,
        documento: true,
        email: true,
        telefono: true,
        ciudad: true,
        estado: true,
        esPremium: true,
        createdAt: true,
      },
      orderBy: {
        createdAt: 'desc',
      },
      take: 100,
    });

    return usuarios;
  }
AdminService.suspenderUsuario method · typescript · L305-L322 (18 LOC)
src/admin/admin.service.ts
  async suspenderUsuario(id: string, motivo: string) {
    const usuario = await this.prisma.user.findUnique({
      where: { id },
    });

    if (!usuario) {
      throw new NotFoundException('Usuario no encontrado');
    }

    await this.prisma.user.update({
      where: { id },
      data: { estado: 'SUSPENDIDO' },
    });

    // TODO: Notificar al usuario con el motivo

    return { message: 'Usuario suspendido' };
  }
AdminService.activarUsuario method · typescript · L324-L339 (16 LOC)
src/admin/admin.service.ts
  async activarUsuario(id: string) {
    const usuario = await this.prisma.user.findUnique({
      where: { id },
    });

    if (!usuario) {
      throw new NotFoundException('Usuario no encontrado');
    }

    await this.prisma.user.update({
      where: { id },
      data: { estado: 'ACTIVO' },
    });

    return { message: 'Usuario activado' };
  }
AdminService.obtenerReportesFotos method · typescript · L343-L377 (35 LOC)
src/admin/admin.service.ts
  async obtenerReportesFotos(estado?: string) {
    const where: any = {};
    if (estado) {
      where.estado = estado;
    }

    const reportes = await this.prisma.reporteFoto.findMany({
      where,
      include: {
        foto: {
          include: {
            user: {
              select: {
                id: true,
                nombre: true,
                apellido: true,
              },
            },
          },
        },
        user: {
          select: {
            id: true,
            nombre: true,
            apellido: true,
          },
        },
      },
      orderBy: {
        createdAt: 'desc',
      },
    });

    return reportes;
  }
AdminService.obtenerReportesUsuarios method · typescript · L379-L409 (31 LOC)
src/admin/admin.service.ts
  async obtenerReportesUsuarios(estado?: string) {
    const where: any = {};
    if (estado) {
      where.estado = estado;
    }

    const reportes = await this.prisma.reporte.findMany({
      where,
      include: {
        reportador: {
          select: {
            id: true,
            nombre: true,
            apellido: true,
          },
        },
        reportado: {
          select: {
            id: true,
            nombre: true,
            apellido: true,
          },
        },
      },
      orderBy: {
        createdAt: 'desc',
      },
    });

    return reportes;
  }
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
AdminService.resolverReporteFoto method · typescript · L411-L431 (21 LOC)
src/admin/admin.service.ts
  async resolverReporteFoto(id: string, accion: string) {
    const reporte = await this.prisma.reporteFoto.findUnique({
      where: { id },
      include: { foto: true },
    });

    if (!reporte) {
      throw new NotFoundException('Reporte no encontrado');
    }

    if (accion === 'ELIMINAR_FOTO') {
      await this.eliminarFotoInapropiada(reporte.fotoId, 'Reportada como inapropiada');
    }

    await this.prisma.reporteFoto.update({
      where: { id },
      data: { estado: 'APROBADA' },
    });

    return { message: 'Reporte resuelto' };
  }
AdminService.resolverReporteUsuario method · typescript · L433-L452 (20 LOC)
src/admin/admin.service.ts
  async resolverReporteUsuario(id: string, accion: string) {
    const reporte = await this.prisma.reporte.findUnique({
      where: { id },
    });

    if (!reporte) {
      throw new NotFoundException('Reporte no encontrado');
    }

    if (accion === 'SUSPENDER') {
      await this.suspenderUsuario(reporte.reportadoId, 'Reportado por múltiples usuarios');
    }

    await this.prisma.reporte.update({
      where: { id },
      data: { estado: 'APROBADA' },
    });

    return { message: 'Reporte resuelto' };
  }
AdminService.obtenerSuscripciones method · typescript · L456-L481 (26 LOC)
src/admin/admin.service.ts
  async obtenerSuscripciones(estado?: string) {
    const where: any = {};
    if (estado) {
      where.estado = estado;
    }

    const suscripciones = await this.prisma.suscripcion.findMany({
      where,
      include: {
        user: {
          select: {
            id: true,
            nombre: true,
            apellido: true,
            email: true,
          },
        },
        plan: true,
      },
      orderBy: {
        createdAt: 'desc',
      },
    });

    return suscripciones;
  }
AdminService.extenderSuscripcion method · typescript · L483-L516 (34 LOC)
src/admin/admin.service.ts
  async extenderSuscripcion(id: string, dias: number) {
    const suscripcion = await this.prisma.suscripcion.findUnique({
      where: { id },
    });

    if (!suscripcion) {
      throw new NotFoundException('Suscripción no encontrada');
    }

    const nuevaFechaFin = new Date(suscripcion.fechaFin);
    nuevaFechaFin.setDate(nuevaFechaFin.getDate() + dias);

    // If subscription was VENCIDA, reactivate it and set user as premium
    const updateData: any = {
      fechaFin: nuevaFechaFin,
      fechaRenovacion: nuevaFechaFin,
    };
    if (suscripcion.estado === 'VENCIDA') {
      updateData.estado = 'ACTIVA';
    }

    await this.prisma.suscripcion.update({
      where: { id },
      data: updateData,
    });

    // Ensure user is premium (especially if reactivated from VENCIDA)
    await this.prisma.user.update({
      where: { id: suscripcion.userId },
      data: { esPremium: true },
    });

    return { message: `Suscripción extendida por ${dias} días` };
  }
AdminService.obtenerConfiguracionPuntos method · typescript · L520-L528 (9 LOC)
src/admin/admin.service.ts
  async obtenerConfiguracionPuntos() {
    const configuracion = await this.prisma.configuracionPuntos.findMany({
      orderBy: {
        puntosBase: 'desc',
      },
    });

    return configuracion;
  }
AdminService.actualizarConfiguracionPuntos method · typescript · L530-L548 (19 LOC)
src/admin/admin.service.ts
  async actualizarConfiguracionPuntos(id: string, data: any) {
    const config = await this.prisma.configuracionPuntos.findUnique({
      where: { id },
    });

    if (!config) {
      throw new NotFoundException('Configuración no encontrada');
    }

    await this.prisma.configuracionPuntos.update({
      where: { id },
      data: {
        puntosBase: data.puntosBase,
        multiplicador: data.multiplicador,
      },
    });

    return { message: 'Configuración actualizada' };
  }
AdminService.crearCupon method · typescript · L552-L565 (14 LOC)
src/admin/admin.service.ts
  async crearCupon(data: any) {
    const cupon = await this.prisma.cupon.create({
      data: {
        codigo: data.codigo,
        tipo: data.tipo,
        valor: data.valor,
        fechaInicio: new Date(data.fechaInicio),
        fechaExpiracion: new Date(data.fechaExpiracion),
        limiteUsos: data.limiteUsos,
      },
    });

    return cupon;
  }
AdminService.obtenerCupones method · typescript · L567-L575 (9 LOC)
src/admin/admin.service.ts
  async obtenerCupones() {
    const cupones = await this.prisma.cupon.findMany({
      orderBy: {
        createdAt: 'desc',
      },
    });

    return cupones;
  }
Repobility analyzer · published findings · https://repobility.com
AdminService.desactivarCupon method · typescript · L577-L584 (8 LOC)
src/admin/admin.service.ts
  async desactivarCupon(id: string) {
    await this.prisma.cupon.update({
      where: { id },
      data: { estado: 'INACTIVO' },
    });

    return { message: 'Cupón desactivado' };
  }
AdminService.obtenerMetricasDashboard method · typescript · L588-L604 (17 LOC)
src/admin/admin.service.ts
  async obtenerMetricasDashboard() {
    const totalUsuarios = await this.prisma.user.count();
    const usuariosPremium = await this.prisma.user.count({
      where: { esPremium: true },
    });
    const totalTorneos = await this.prisma.tournament.count();
    const torneosPendientes = await this.prisma.tournament.count({
      where: { estado: 'PENDIENTE_APROBACION' },
    });

    return {
      totalUsuarios,
      usuariosPremium,
      totalTorneos,
      torneosPendientes,
    };
  }
AdminService.obtenerMetricasUsuarios method · typescript · L606-L621 (16 LOC)
src/admin/admin.service.ts
  async obtenerMetricasUsuarios() {
    const porEstado = await this.prisma.user.groupBy({
      by: ['estado'],
      _count: true,
    });

    const porGenero = await this.prisma.user.groupBy({
      by: ['genero'],
      _count: true,
    });

    return {
      porEstado,
      porGenero,
    };
  }
AdminService.obtenerMetricasTorneos method · typescript · L623-L630 (8 LOC)
src/admin/admin.service.ts
  async obtenerMetricasTorneos() {
    const porEstado = await this.prisma.tournament.groupBy({
      by: ['estado'],
      _count: true,
    });

    return { porEstado };
  }
AdminService.obtenerMetricasIngresos method · typescript · L632-L659 (28 LOC)
src/admin/admin.service.ts
  async obtenerMetricasIngresos() {
    // Calcular ingresos totales de suscripciones
    const suscripciones = await this.prisma.suscripcion.findMany({
      where: { estado: 'ACTIVA' },
    });

    const mrrSuscripciones = suscripciones.reduce((acc, sub) => {
      const precioMensual = sub.periodo === 'MENSUAL'
        ? sub.precio.toNumber()
        : sub.precio.toNumber() / 12;
      return acc + precioMensual;
    }, 0);

    // Calcular ingresos de comisiones
    const pagos = await this.prisma.pago.findMany({
      where: { estado: 'CONFIRMADO' },
    });

    const totalComisiones = pagos.reduce((acc, pago) => {
      return acc + pago.comision.toNumber();
    }, 0);

    return {
      mrr: mrrSuscripciones,
      totalComisiones,
      suscripcionesActivas: suscripciones.length,
    };
  }
AdminService.obtenerUsuariosPremium method · typescript · L666-L718 (53 LOC)
src/admin/admin.service.ts
  async obtenerUsuariosPremium(search?: string, estado?: string) {
    const where: any = { esPremium: true };

    if (search) {
      where.AND = [
        { esPremium: true },
        {
          OR: [
            { nombre: { contains: search, mode: 'insensitive' } },
            { apellido: { contains: search, mode: 'insensitive' } },
            { email: { contains: search, mode: 'insensitive' } },
            { documento: { contains: search } },
          ],
        },
      ];
      delete where.esPremium;
    }

    const usuarios = await this.prisma.user.findMany({
      where,
      select: {
        id: true,
        nombre: true,
        apellido: true,
        documento: true,
        email: true,
        telefono: true,
        ciudad: true,
        genero: true,
        fotoUrl: true,
        esPremium: true,
        createdAt: true,
        suscripciones: {
          orderBy: { createdAt: 'desc' },
          take: 1,
          include: {
            plan: true,
        
AdminService.obtenerMetricasPremium method · typescript · L723-L812 (90 LOC)
src/admin/admin.service.ts
  async obtenerMetricasPremium() {
    const now = new Date();
    const hace30Dias = new Date(now);
    hace30Dias.setDate(hace30Dias.getDate() - 30);
    const hace60Dias = new Date(now);
    hace60Dias.setDate(hace60Dias.getDate() - 60);

    // Conteos básicos
    const totalPremium = await this.prisma.user.count({ where: { esPremium: true } });
    const totalUsuarios = await this.prisma.user.count();
    const suscripcionesActivas = await this.prisma.suscripcion.count({ where: { estado: 'ACTIVA' } });
    const suscripcionesPendientes = await this.prisma.suscripcion.count({ where: { estado: 'PENDIENTE_PAGO' } });

    // Nuevos premium últimos 30 días
    const nuevosPremium30d = await this.prisma.suscripcion.count({
      where: {
        estado: 'ACTIVA',
        createdAt: { gte: hace30Dias },
      },
    });

    // Cancelaciones últimos 30 días
    const cancelaciones30d = await this.prisma.suscripcion.count({
      where: {
        estado: { in: ['CANCELADA', 'VENCIDA'] },
AdminService.obtenerTendenciasSuscripciones method · typescript · L817-L863 (47 LOC)
src/admin/admin.service.ts
  async obtenerTendenciasSuscripciones() {
    const meses: {
      mes: string;
      nuevas: number;
      canceladas: number;
      ingresos: number;
    }[] = [];

    const now = new Date();

    for (let i = 11; i >= 0; i--) {
      const inicio = new Date(now.getFullYear(), now.getMonth() - i, 1);
      const fin = new Date(now.getFullYear(), now.getMonth() - i + 1, 0, 23, 59, 59);

      const nuevas = await this.prisma.suscripcion.count({
        where: {
          createdAt: { gte: inicio, lte: fin },
          estado: { not: 'PENDIENTE_PAGO' },
        },
      });

      const canceladas = await this.prisma.suscripcion.count({
        where: {
          estado: { in: ['CANCELADA', 'VENCIDA'] },
          updatedAt: { gte: inicio, lte: fin },
        },
      });

      // Revenue del mes (suscripciones creadas ese mes)
      const susMes = await this.prisma.suscripcion.findMany({
        where: {
          createdAt: { gte: inicio, lte: fin },
          estado: { in: ['ACTI
Repobility · severity-and-effort ranking · https://repobility.com
AdminService.obtenerActividadPremium method · typescript · L868-L903 (36 LOC)
src/admin/admin.service.ts
  async obtenerActividadPremium() {
    const suscripciones = await this.prisma.suscripcion.findMany({
      orderBy: { updatedAt: 'desc' },
      take: 20,
      include: {
        user: {
          select: {
            id: true,
            nombre: true,
            apellido: true,
            email: true,
            fotoUrl: true,
          },
        },
        plan: {
          select: {
            nombre: true,
          },
        },
      },
    });

    return suscripciones.map((s) => ({
      id: s.id,
      usuario: s.user,
      plan: s.plan?.nombre,
      estado: s.estado,
      precio: s.precio.toNumber(),
      fechaInicio: s.fechaInicio,
      fechaFin: s.fechaFin,
      autoRenovar: s.autoRenovar,
      cuponAplicado: s.cuponAplicado,
      createdAt: s.createdAt,
      updatedAt: s.updatedAt,
    }));
  }
AdminService.otorgarPremiumManual method · typescript · L908-L961 (54 LOC)
src/admin/admin.service.ts
  async otorgarPremiumManual(userId: string, dias: number, motivo: string) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException('Usuario no encontrado');
    }

    // Buscar plan premium
    const plan = await this.prisma.planPremium.findFirst({
      where: { activo: true },
    });

    if (!plan) {
      throw new NotFoundException('No hay plan premium activo');
    }

    // Cancelar suscripciones activas/pendientes existentes para evitar duplicados
    await this.prisma.suscripcion.updateMany({
      where: { userId, estado: { in: ['ACTIVA', 'PENDIENTE_PAGO'] } },
      data: { estado: 'CANCELADA' },
    });

    const now = new Date();
    const fechaFin = new Date(now);
    fechaFin.setDate(fechaFin.getDate() + dias);

    // Crear suscripción cortesía
    const suscripcion = await this.prisma.suscripcion.create({
      data: {
        userId,
        planId: plan.id,
        periodo: 'ME
AdminService.revocarPremium method · typescript · L966-L990 (25 LOC)
src/admin/admin.service.ts
  async revocarPremium(userId: string) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException('Usuario no encontrado');
    }

    // Cancelar suscripciones activas y pendientes
    await this.prisma.suscripcion.updateMany({
      where: { userId, estado: { in: ['ACTIVA', 'PENDIENTE_PAGO'] } },
      data: { estado: 'CANCELADA' },
    });

    // Quitar premium
    await this.prisma.user.update({
      where: { id: userId },
      data: { esPremium: false },
    });

    return {
      message: `Premium revocado para ${user.nombre} ${user.apellido}`,
    };
  }
AdminService.obtenerEstadisticasCupones method · typescript · L995-L1028 (34 LOC)
src/admin/admin.service.ts
  async obtenerEstadisticasCupones() {
    const cupones = await this.prisma.cupon.findMany({
      orderBy: { createdAt: 'desc' },
    });

    const totalCupones = cupones.length;
    const cuponesActivos = cupones.filter((c) => c.estado === 'ACTIVO').length;
    const totalUsos = cupones.reduce((acc, c) => acc + c.usosActuales, 0);
    const descuentoTotal = cupones.reduce((acc, c) => {
      if (c.tipo === 'PORCENTAJE') return acc; // Can't calculate exact amount for percentage
      return acc + c.valor.toNumber() * c.usosActuales;
    }, 0);

    // Top cupones por uso
    const topCupones = [...cupones]
      .sort((a, b) => b.usosActuales - a.usosActuales)
      .slice(0, 5)
      .map((c) => ({
        codigo: c.codigo,
        tipo: c.tipo,
        valor: c.valor.toNumber(),
        usos: c.usosActuales,
        limite: c.limiteUsos,
        estado: c.estado,
      }));

    return {
      totalCupones,
      cuponesActivos,
      totalUsos,
      descuentoTotal,
      topCup
AdminService.promoverOrganizadorPorDocumento method · typescript · L1032-L1086 (55 LOC)
src/admin/admin.service.ts
  async promoverOrganizadorPorDocumento(documento: string) {
    // Buscar usuario por documento
    const user = await this.prisma.user.findUnique({
      where: { documento },
      include: {
        roles: {
          include: { role: true },
        },
      },
    });

    if (!user) {
      throw new NotFoundException(
        `No se encontró usuario con documento: ${documento}`,
      );
    }

    // Verificar si ya es organizador
    const yaEsOrganizador = user.roles.some(
      (ur) => ur.role.nombre === 'organizador',
    );
    if (yaEsOrganizador) {
      throw new ConflictException(
        `${user.nombre} ${user.apellido} ya tiene rol de organizador`,
      );
    }

    // Buscar rol de organizador
    const rolOrganizador = await this.prisma.role.findUnique({
      where: { nombre: 'organizador' },
    });

    if (!rolOrganizador) {
      throw new NotFoundException('Rol organizador no encontrado');
    }

    // Asignar rol
    await this.prisma.userRole.create({
 
AdminService.actualizarConfiguracionSistema method · typescript · L1096-L1123 (28 LOC)
src/admin/admin.service.ts
  async actualizarConfiguracionSistema(clave: string, valor: string) {
    const config = await this.prisma.configuracionSistema.findUnique({
      where: { clave },
    });

    if (!config) {
      throw new NotFoundException(
        `Configuración '${clave}' no encontrada`,
      );
    }

    // Validar que el valor sea numérico para comisiones
    if (clave === 'COMISION_INSCRIPCION') {
      const numVal = parseFloat(valor);
      if (isNaN(numVal) || numVal < 0 || numVal > 100) {
        throw new BadRequestException(
          'El porcentaje de comisión debe ser un número entre 0 y 100',
        );
      }
    }

    await this.prisma.configuracionSistema.update({
      where: { clave },
      data: { valor },
    });

    return { message: `Configuración '${clave}' actualizada a: ${valor}` };
  }
AdminService.obtenerValorConfiguracion method · typescript · L1125-L1130 (6 LOC)
src/admin/admin.service.ts
  async obtenerValorConfiguracion(clave: string): Promise<string | null> {
    const config = await this.prisma.configuracionSistema.findUnique({
      where: { clave },
    });
    return config?.valor ?? null;
  }
AdminService.setComisionTorneo method · typescript · L1136-L1159 (24 LOC)
src/admin/admin.service.ts
  async setComisionTorneo(tournamentId: string, comisionPorcentaje: number | null) {
    const tournament = await this.prisma.tournament.findUnique({
      where: { id: tournamentId },
    });
    if (!tournament) throw new NotFoundException('Torneo no encontrado');

    if (comisionPorcentaje !== null) {
      if (comisionPorcentaje < 0 || comisionPorcentaje > 100) {
        throw new BadRequestException('La comisión debe estar entre 0 y 100');
      }
    }

    await this.prisma.tournament.update({
      where: { id: tournamentId },
      data: { comisionPorcentaje },
    });

    return {
      message: comisionPorcentaje !== null
        ? `Comisión del torneo configurada a ${comisionPorcentaje}%`
        : 'Comisión del torneo eliminada (usará la configuración global)',
      comisionPorcentaje,
    };
  }
If a scraper extracted this row, it came from Repobility (https://repobility.com)
AdminService.getComisionTorneo method · typescript · L1161-L1179 (19 LOC)
src/admin/admin.service.ts
  async getComisionTorneo(tournamentId: string) {
    const tournament = await this.prisma.tournament.findUnique({
      where: { id: tournamentId },
      select: { id: true, nombre: true, comisionPorcentaje: true },
    });
    if (!tournament) throw new NotFoundException('Torneo no encontrado');

    const globalConfig = await this.prisma.configuracionSistema.findUnique({
      where: { clave: 'COMISION_INSCRIPCION' },
    });

    return {
      tournamentId: tournament.id,
      nombre: tournament.nombre,
      comisionPorcentaje: tournament.comisionPorcentaje,
      comisionGlobal: globalConfig ? parseFloat(globalConfig.valor) : 5,
      usandoGlobal: tournament.comisionPorcentaje === null,
    };
  }
AdminService.obtenerFinanzasDashboard method · typescript · L1185-L1250 (66 LOC)
src/admin/admin.service.ts
  async obtenerFinanzasDashboard() {
    // Comisión fija actual
    const configComision = await this.prisma.configuracionSistema.findUnique({
      where: { clave: 'COMISION_FIJA_POR_JUGADOR' },
    });
    const comisionFijaPorJugador = configComision ? parseFloat(configComision.valor) : 5000;

    // Total comisiones de pagos confirmados
    const pagosConfirmados = await this.prisma.pago.findMany({
      where: { estado: 'CONFIRMADO' },
      select: { comision: true, inscripcionId: true },
    });

    const totalIngresosComisiones = pagosConfirmados.reduce(
      (acc, p) => acc + p.comision.toNumber(), 0,
    );

    // Pagos confirmados del mes actual
    const now = new Date();
    const inicioMes = new Date(now.getFullYear(), now.getMonth(), 1);
    const pagosMes = await this.prisma.pago.findMany({
      where: {
        estado: 'CONFIRMADO',
        fechaConfirm: { gte: inicioMes },
      },
      select: { comision: true },
    });
    const comisionesMes = pagosMes.reduc
AdminService.obtenerFinanzasTorneos method · typescript · L1252-L1310 (59 LOC)
src/admin/admin.service.ts
  async obtenerFinanzasTorneos(estado?: string) {
    const where: any = {};
    if (estado) {
      where.estado = estado;
    }

    const torneos = await this.prisma.tournament.findMany({
      where,
      select: {
        id: true,
        nombre: true,
        flyerUrl: true,
        estado: true,
        fechaInicio: true,
        fechaFin: true,
        costoInscripcion: true,
        createdAt: true,
        inscripciones: {
          select: {
            id: true,
            estado: true,
            pagos: {
              where: { estado: 'CONFIRMADO' },
              select: { comision: true },
            },
          },
        },
      },
      orderBy: { createdAt: 'desc' },
    });

    return torneos.map((t) => {
      let inscripcionesConfirmadas = 0;
      let comisionesGeneradas = 0;

      for (const insc of t.inscripciones) {
        if (insc.estado === 'CONFIRMADA') {
          inscripcionesConfirmadas++;
        }
        for (const pago of insc.pagos) {
   
page 1 / 9next ›