manuales_desarrollo

Manuales de desarrollo de YeboYebo


Project maintained by yeboyebo Hosted on GitHub Pages — Theme by mattgraham

SpreadSheetReport.qs

La clase SpreadSheetReport.qs permite la creación de hojas de cálculo y la generación de informes tabulados de varios niveles.

Generación de informes tipo Report (con niveles)

Prerrequisitos

Parámetros opcionales

Podemos usar la clase SpreadSheetReport para generar un informe que incluya varios niveles, así como cabeceras y pies de nivel.

Un ejemplo simple de cómo se utilizaría esta clase sería el siguiente.

Función de lanzamiento

function oficial_tbnExcel_clicked() {
  const _i = this.iface;
  const cursor = this.cursor();

  //  Prerrequisitos
  const nombreInforme = "nombre del informe";
  const nombreFichero = "nombre del fichero generado";
  const camposCalculados = _i.dameCamposCalculados();
  const acumulados = _i.dameAcumulados();

  //Para obtener los datos que se imprimirán en el informe tenemos 2 opciones:

  //OPCIÓN 1: Usamos una query que tengamos definida de la que obtener los datos
  const query = _i.estableceConsulta("nombreConsulta", "tabla.id = 1", "");

  //OPCIÓN 2: Podemos guardar los datos con la estructura que necesitemos dentro de un array que hayamos procesado manualmente
  const data = _i.dameDatos();

  /*Llamamos a la función donde definimos la estructura de niveles del informe.
    Esta nos debe devolver un objeto de la clase SpreadSheetReport con los niveles definidos*/
  const spreadSheetReport = _i.dameReport(nombreInforme);

  //Depende de la opción que estemos usando para recoger los datos tendremos que usar los siguientes métodos de la clase:

  //Si los recogemos por Query
  spreadSheetReport.setQuery(query);

  //Si los recogemos manualmente
  spreadSheetReport.setData(data);

  spreadSheetReport.setCamposCalculados(camposCalculados);

  //PARÁMETROS OPCIONALES

  /*ESTILOS:
    Podemos crear un diccionario para guardar cómodamente los estilos de nuestro informe.

    Estilos disponibles:
        - "italic"
        - "tSep"
        - "bSep"
        - "rSep"
        - "lSep"
        - "lAlig"
        - "rAlig"
        - "none"
        - "undeline"
        - "bold"
        - "border" -> aplica los 4 Sep
        - "bx" -> aplica los Sep horizontales
        - "by" -> aplica los Sep verticales
    */

  spreadSheetReport.setEstilos({
    // Cada estilo debe tener asignado un array con los diferentes estilos a aplicar.
    normal: ["lAlig", "border"],
    titulo: ["lAlig", "border", "bold"],
  });

  /*OPCIONES:
    Se le puede asignar cualquier estructura de datos.
    Pero por conveniencia deberíamos asignarle siempre un diccionario.

    Por ejemplo si quisiéramos que el usuario pueda decidir si quiere o no los pies en el informe,
    se añadiría la opción de la siguiente manera.
    */

  var res = formUI.preguntaMsg(
    sys.translate("¿quieres imprimir los pies?"),
    "info",
    this,
    MessageBox.Yes,
    MessageBox.No
  );

  spreadSheetReport.setOpciones({
    imprimirPies: res == Message.Yes,
  });

  /*renderizamos la estructura de niveles.
    Esta funcion devuelve un objeto spreadsheet, que contiene los datos dispuestos en filas.*/
  const spreadsheet = spreadSheetReport.render();

  //Con la funcion toODS transformamos el objeto a una  hoja de cálculo EXCEL. Le pasamos el nombre que queramos asignar al fichero generado
  spreadsheet.toODS(nombreFichero);
}

Función que extrae los campos calculados

function oficial_dameCamposCalculados() {
  const cc = [];
  //para cada campo que queramos añadir, tendremos que asignarle un titulo y una función que devuelve el valor del campo
  cc.push({
    titulo: "nombreDelCampo",
    funcion: function (valores) {
      var valor;
      //código que calcula el valor del campo
      return valor;
    },
  });
  return cc;
}

Función que extrae los campos acumulados

function oficial_dameAcumulados() {
  const acumulados = {};
  //para cada campo acumulado que queramos añadir, tendremos que añadir una clave al diccionario, y luego tenemos 2 opciones:

  //OPCIÓN 1: guardar directamente el tipo de acumulado
  acumulados["clave"] = "SUMA";

  /*
  OPCION 2: guardar un diccionario con el tipo del acumulado y una funcion para extraer el valor del incremento
  
  Esta opción es útil cuando la estructura de los valores del informe es más compleja que un diccionario con únicamente datos primitivos.

  Ejemplo:{
    Producto: x Unidades: x Envases:
    [
        {refEnvase:x Peso: x},
        {refEnvase:x Peso: x}
    ]
  }
  Si quisieramos en este ejemplo acumular el peso de los envases, tendríamos que usar esta forma
  */

  acumulados["clave"] = {
    tipo: "SUMA",
    funcionGet: function (valores, clave) {
      var incremento = 0;
      for (var i = 0; i < valores.envases.length; i++) {
        incremento += valores.envases[i].peso;
      }
      return incremento;
    },
  };
  return acumulados;
}

Función que establece la query

function oficial_estableceConsulta(nombreConsulta, where, orderBy) {
  var query: FLSqlQuery = new FLSqlQuery(nombreConsulta);

  if (where) query.setWhere(where);

  if (orderBy) query.setOrderBy(orderBy);

  return query;
}

Función que establece los datos del informe manualmente

function oficial_dameDatos() {
  /*
    Imaginemos que queremos guardar los datos de las ventas con este formato:

    {producto:x, unidadesVendidas: x, precio:x}

    */
  var q = new FLSqlQuery();

  //Introduciríamos los datos de la query y la ejecutariamos

  var datos = [];
  while (q.next()) {
    datos.push({
      producto: q.value("producto"),
      unidadesVendidad: q.value("unidadesVendidas"),
      precio: q.value("precio"),
    });
  }

  /*En este caso no tiene mucho sentido hacer esto ya que es mas sencillo obtener los datos de una query. 
    Pero si los datos del informe necesitan usar varias querys o una estructura de datos más compleja, 
    esta manera es más flexible.
    */
  return datos;
}

Función con la estructura de niveles del informe

function oficial_dameReport(nombreInforme)
{
    //Importamos la clase de "contexts/shared/infrastructure/SpreadSheetReport.qs" y creamos una instancia de la clase
    const SpreadSheetReport = FormImport.from("contexts/shared/infrastructure/SpreadSheetReport.qs");
    const report = new SpreadSheetReport(nombreInforme)

    const _i = this.iface

     /*Para cada nivel, indicamos el campo de la consulta que marca su rotura
     Esta función crea internamente el array de aNiveles
     (con longitud igual a report.Niveles.length + 1)*/
    report.setNiveles(["almacenes.codalmacen"])

    // report: Indica las funciones de cabecera y pie de informe con setReportPie y setReportCabecera
    // El nivel de report no puede contener acumulados, para ello se utiliza el nivel 0
    report.setReportPie(function (valores, spreadsheet) {
        var fila = spreadsheet.addFila()
        fila.setFormato(estilos.titulo)
        fila.addCelda("TOTAL INFORME")
        fila.addEspacios(1)
        fila.addCelda(valores["total"])
    })

    // Para asignar los acumulados se pueden asignar a cada nivel por separado
    report.setAcumulados(0,{
        // Incluimos una clave por cada campo de la consulta (calculado o no) que queramos
        // usar en el acumulado. La única opción de acumulación por ahora es la suma (SUMA).
        "total": "SUMA"
    })
    report.setAcumulados(1,{
        "total": "SUMA"
    })

    /*En el caso de que todos los niveles tengan los mismos acumulados,
    se pueden añadir a todos los niveles de golpe.*/
    report.setAllAcumulados({
        "total": "SUMA"
    })

    /*Para cada nivel se llama a la funcion setCabecera, setDetalle, setPie del nivel correspondiente,
    aunque no es necesario definir todas las secciones en cada nivel.

    A cada una de estas funciones se les debe pasar por parámetro el nivel al que se está haciendo referencia,
    y la funcion que debe ejecutar para dibujar esa seccion en el informe

    La función de dibujado siempre tendrá 4 parámetros, o 5 si añadimos los acumulados

    Parámetros:

    -valores -> Diccionario con los valores a pintar en este nivel.
    -estilos -> Diccionario con los estilos que nosotros hayamos añadido al report.
    -opciones -> Diccionario con las opciones que le hayamos añadido al report.
    -spreadsheet -> Objeto donde añadiremos las filas de cada sección.
    -acumulados -> Diccionario con los valores de los campos acumulados de este nivel.
    Solo podemos usarlo si previamente hemos hecho un setAcumulados en el nivel que
    corresponda.
    */


    //NIVEL 0

    //Ejemplo sin acumulados
    report.setDetalle(0, function(valores, estilos, opciones, spreadsheet) {
        //añadimos filas al spreadsheet de esta manera
        var fila = spreadsheet.addFila()
        /*podemos darle estilo a todas las celdas que añadamos a esta fila con setFormato,
        pasandole un array con los estilos que deban aplicarse a las celdas.
        */
        fila.setFormato(estilos.titulo) //Debe hacerse antes de añadir las celdas.
        fila.addCelda(valores["almacenes.codalmacen"])
        fila.addCelda(valores["almacences.nombre"])
        fila = spreadsheet.addFila()
        fila.setFormato(estilos.titulo)
        //con addEspacios podemos añadir n celdas vacias en la fila actual
        fila.addEspacios(2)
        fila.addCelda("Referencia")
        fila.addCelda("Descripción")
        fila.addCelda("Cantidad")
        fila.addCelda("Coste")
        /*si quisiéramos que una celda tuviera un estilo distinto al de la fila,
        le podemos pasar su propio estilo al añadir la celda.
        (Estos estilos no se acumulan con los de la fila, sino que los sustituye)*/
        fila.addCelda("Total",estilos.normal)
    })
    /*Ejemplo con acumulados */
    report.setPie(0, function(valores, estilos, opciones, spreadsheet, acumulados) {
        // Podemos establecer una condición para no dibujar la sección, como la que añadimos previamente a la propiedad opciones
        if(!opciones.imprimirPies){
            return
        }
        var fila = spreadsheet.addFila()
        fila.setFormato(estilos.titulo)
        fila.addCelda("TOTAL " + valores["almacenes.codalmacen"])
        fila.addCelda(valores["almacenes.nombre"])
        fila.addCelda(acumulados["total"])
        //Para añadir una fila totalmente vacia basta con hacer un addFila
        spreadsheet.addFila()
    })

    //NIVEL 1

    report.setDetalle(1, function(valores, estilos, opciones, spreadsheet, acumulados, spreadsheetReport) {

        var fila = spreadsheet.addFila()
        fila.setFormato(estilos.normal)
        //Si quisieramos introducir un valor en una celda concreta de la fila, podemos usar el siguiente método de la clase fila
        fila.insertCeldaInPosition(2, valores["stocks.referencia"])
        fila.insertCeldaInPosition(3, valores["articulos.descripcion"])

        /*si el valor que queremos guardar en la celda es un número decimal,
        podemos cambiar la precision de la siguiente manera*/
        var celda = fila.add(valores["cantidad"])
        celda.setPrecision(2)
        celda = fila.add(valores["coste"])
        celda.setPrecision(2)
        celda = fila.add(valores["total"])
        celda.setPrecision(2)
        //Si quisieramos que el una determinada celda ocupara más de una columna, podemos hacerlo utilizando setTamCelda
        celda.setTamCelda(3) //esta celda ocuparia 3 columnas

        /*
        En el caso de que el informe sea muy complejo, podemos hacer uso del ultimo parámetro de la función de dibujado, en el que se guarda el propio objeto report mientras se está ejecutando el render.

        Esto solo deberíamos hacerlo en casos raros, pero un ejemplo es cuando necesitamos acceder a los acumulados de un nivel distinto al que se está pintando.
        */
       const acumuladosInforme = spreadsheetReport.acumulados;
		acumuladosNivelCero = spreadsheetReport.getAcumulados(valores, 0, acumuladosInforme)

        //Esto nos serviría por ejemplo para calcular campos como porcentajes de un total que se esta calculando en el nivel 0

        valorPorcentaje = parseFloat(acumulados["clave"] / parseFloat(acumuladosNivelCero["clave"]) * 100)

        fila.addCelda(valorPorcentaje + " %")

    })
}

### Más

- [Volver al Índice](/manuales_desarrollo/programacion/qsa/)