Documentación interna
Comparativo

📊 Comparativo por Renglón (comparativo)


⚠️ IMPORTANTE

Para utilizar este endpoint en modo comparativo es obligatorio enviar:

include_comparativo=1

Parámetro de búsqueda:

find_eq_numero

🧠 Comportamiento del endpoint

🔎 Búsqueda vacía

find_eq_numero=

➡ Devuelve lista de licitaciones


🔎 Búsqueda con valor

Se busca usando coincidencia parcial (LIKE).

Resultado:

  • 0 resultados → lista vacía\
  • Más de 1 resultado → lista\
  • 1 resultado pero NO coincide exactamente → lista\
  • 1 resultado y coincide EXACTAMENTE → comparativo

✔ Condición exacta:

licitacion.numero === find_eq_numero

🧠 Concepto general

El bloque comparativo representa el análisis de cada renglón de la licitación desde el punto de vista del cliente logueado.

👉 Cada elemento del array corresponde a un renglón\ 👉 Dentro de cada renglón ya vienen calculadas todas las métricas necesarias para comparar contra la competencia


🧱 Estructura de cada renglón

{
  "idRenglon": 15,
  "numeroRenglon": 1,
  "descripcion": "Producto X",
  "miOferta": {
    "ofertaTotal": 10000,
    "mejorOferta": false,
    "posicion": 2,
    "diferenciaConMejor": 500,
    "diferenciaPctConMejor": 5
  },
  "mejorOfertaGeneral": 9500,
  "cantidadOferentes": 4,
  "ofertas": [
    {
      "oferente": "Proveedor A",
      "ofertaTotal": 9500,
      "posicion": 1,
      "mejorOferta": true,
      "esCliente": false
    }
  ]
}

🔑 Campos principales

miOferta

  • Puede ser null
  • Ya viene calculado

mejorOfertaGeneral

Menor precio del renglón


cantidadOferentes

Cantidad de oferentes


ofertas

  • Ordenado por precio ASC
  • Incluye ranking

🖥️ Uso en la interfaz

Vista

  • Tabla por renglón
  • Detalle expandible

🚫 Importante

El frontend NO debe recalcular nada.


📘 Tipos TypeScript

export interface LicitacionComparativoItem {
  id: number;
  numero: string;
  nombre: string;
  tipo: string;
  fechaApertura: string | null;
  unidadEjecutora: string | null;
  servicioAdmFinanciero: string | null;
  tipoOrden: "ABIERTA" | "SIN_MODALIDAD" | null;
  rubro: string | null;
  fechaPublicacion: string | null;
}

export interface OferenteResumen {
  nombre: string | null;
  cuit: string | null;
  renglonesOfertados: number;
  renglonesMejorOferta: number;
  importeMejorOferta: number;
  tasaEfectividad: number;
}

export interface ClienteResumen {
  cuit: string;
  nombre: string | null;
  renglonesOfertados: number;
  renglonesMejorOferta: number;
  importeTotalOfertado: number;
  importeMejorOferta: number;
  tasaEfectividad: number;
  posicionPromedio: number;
}

export interface OfertaRenglon {
  oferente: string;
  cuit: string;
  ofertaTotal: number;
  mejorOferta: boolean;
  posicion: number;
  esCliente: boolean;
}

export interface MiOferta extends OfertaRenglon {
  diferenciaConMejor: number;
  diferenciaPctConMejor: number;
}

export interface RenglonComparativo {
  idRenglon: number;
  numeroRenglon: number;
  descripcion: string;
  cantidad: number;
  unidad: string | null;
  miOferta: MiOferta | null;
  mejorOfertaGeneral: number;
  cantidadOferentes: number;
  ofertas: OfertaRenglon[];
}

export interface ComparativoClienteResponse {
  cliente: ClienteResumen | null;
  comparativoCliente: RenglonComparativo[];
}

export interface ComparativoListaResponse {
  modo: "lista";
  find_eq_numero: string;
  total: number;
  items: LicitacionComparativoItem[];
}

export interface ComparativoDetalleResponse {
  modo: "comparativo";
  find_eq_numero: string;
  licitacion: unknown; // si después querés, esto lo tipamos fino con la entidad serializada real
  oferentes: OferenteResumen[];
  comparativo: ComparativoClienteResponse;
}

export type ComparativoEndpointResponse =
  | ComparativoListaResponse
  | ComparativoDetalleResponse;


export function isComparativoDetalle(
  data: ComparativoEndpointResponse
): data is ComparativoDetalleResponse {
  return data.modo === "comparativo";
}

export function isComparativoLista(
  data: ComparativoEndpointResponse
): data is ComparativoListaResponse {
  return data.modo === "lista";