public static void tablatablaCEconomicos(Long idEvaluacion) {

    TipoEvaluacion tipoEvaluacion = TipoEvaluacion.all().first();

    Evaluacion evaluacion = Evaluacion.findById(idEvaluacion);

    List<CEconomico> rowsFiltered = filtroConceptosEconomicos(evaluacion);

    List<Map<String, String>> columnasCEconomicos = new ArrayList<Map<String, String>>();
    List<Double> totalesSolicitadoAnio = new ArrayList<Double>();
    List<Double> totalesEstimadoAnio = new ArrayList<Double>();
    List<Double> totalesPropuestoAnio = new ArrayList<Double>();
    for (int i = 0; i < tipoEvaluacion.duracion; i++) {
      totalesSolicitadoAnio.add(0.0);
      totalesEstimadoAnio.add(0.0);
      totalesPropuestoAnio.add(0.0);
    }
    for (CEconomico cEconomico : rowsFiltered) {
      Map<String, String> columna = new HashMap<String, String>();
      columna.put("id", cEconomico.id.toString());
      Double totalesSolicitado = 0.0;
      Double totalesEstimado = 0.0;
      Double totalesPropuesto = 0.0;

      Pattern pattern = Pattern.compile("^[a-zA-Z]$");

      for (int i = 0; i < tipoEvaluacion.duracion; i++) {
        Matcher matcher = pattern.matcher(cEconomico.tipo.jerarquia);
        if (matcher.find()) {
          totalesEstimadoAnio.set(
              i, totalesEstimadoAnio.get(i) + cEconomico.valores.get(i).valorEstimado);
          totalesSolicitadoAnio.set(
              i, totalesSolicitadoAnio.get(i) + cEconomico.valores.get(i).valorSolicitado);
          totalesPropuestoAnio.set(
              i, totalesPropuestoAnio.get(i) + cEconomico.valores.get(i).valorPropuesto);
        }

        totalesSolicitado += cEconomico.valores.get(i).valorSolicitado;
        columna.put(
            "valorSolicitado" + i,
            FapFormat.formatMoneda(cEconomico.valores.get(i).valorSolicitado));
        totalesEstimado += cEconomico.valores.get(i).valorEstimado;
        columna.put(
            "valorEstimado" + i, FapFormat.formatMoneda(cEconomico.valores.get(i).valorEstimado));
        totalesPropuesto += cEconomico.valores.get(i).valorPropuesto;
        columna.put(
            "valorPropuesto" + i, FapFormat.formatMoneda(cEconomico.valores.get(i).valorPropuesto));
      }

      columna.put("nombre", cEconomico.tipo.nombre);
      columna.put("jerarquia", cEconomico.tipo.jerarquia);
      if (cEconomico.tipo.clase.equals("auto")) {
        columna.put("permiso", "false");
      } else {
        columna.put("permiso", "true");
      }
      columna.put("totalSolicitado", FapFormat.formatMoneda(totalesSolicitado));
      columna.put("totalEstimado", FapFormat.formatMoneda(totalesEstimado));
      columna.put("totalPropuesto", FapFormat.formatMoneda(totalesPropuesto));
      columnasCEconomicos.add(columna);
    }

    Map<String, String> columna = new HashMap<String, String>();
    columna.put("id", "0");
    Double totalesSolicitado = 0.0;
    Double totalesEstimado = 0.0;
    Double totalesPropuesto = 0.0;
    for (int i = 0; i < tipoEvaluacion.duracion; i++) {
      columna.put("valorSolicitado" + i, FapFormat.formatMoneda(totalesSolicitadoAnio.get(i)));
      columna.put("valorEstimado" + i, FapFormat.formatMoneda(totalesEstimadoAnio.get(i)));
      columna.put("valorPropuesto" + i, FapFormat.formatMoneda(totalesEstimadoAnio.get(i)));
      totalesSolicitado += totalesSolicitadoAnio.get(i);
      totalesEstimado += totalesEstimadoAnio.get(i);
      totalesPropuesto += totalesPropuestoAnio.get(i);
    }
    columna.put("jerarquia", "TOTALES");
    columna.put("nombre", "POR AÑOS");
    columna.put("permiso", "false");
    columna.put("totalSolicitado", FapFormat.formatMoneda(totalesSolicitado));
    columna.put("totalEstimado", FapFormat.formatMoneda(totalesEstimado));
    columna.put("totalPropuesto", FapFormat.formatMoneda(totalesPropuesto));
    columnasCEconomicos.add(columna);
    renderJSON(columnasCEconomicos);
  }
  @Util
  public static void save(Long idEvaluacion) {
    Map<String, Long> ids = (Map<String, Long>) tags.TagMapStack.top("idParams");
    if (secure.checkGrafico("guardarEvaluacion", "editable", "editar", ids, null)) {
      boolean actionSave = params.get("save") != null;
      boolean actionPdf = params.get("pdf") != null;
      boolean actionEnd = params.get("end") != null;
      if (!(actionSave || actionPdf || actionEnd)) {
        // No se específico una acción
        notFound("Acción no especificada");
      }
      Evaluacion evaluacion = Evaluacion.findById(params.get("evaluacion.id", Long.class));
      if (evaluacion == null) {
        notFound("Fallo al recuperar la evaluación");
      }
      if (!evaluacion.estado.equals(EstadosEvaluacionEnum.enTramite.name())) {
        Messages.error("No se puede guardar porque esta evaluación ya ha sido finalizada");
        Messages.keep();
        index(evaluacion.id, "leer");
      }
      // Comentarios
      if (evaluacion.tipo.comentariosAdministracion) {
        evaluacion.comentariosAdministracion = params.get("evaluacion.comentariosAdministracion");
      }

      if (evaluacion.tipo.comentariosSolicitante) {
        evaluacion.comentariosSolicitante = params.get("evaluacion.comentariosSolicitante");
      }
      boolean guardarMaximo = false;
      // Criterios de evaluacion
      for (Criterio criterio : evaluacion.criterios) {
        String param = "criterio[" + criterio.id + "]";
        String key = param + ".valor";
        Double valor = params.get(key, Double.class);

        if (criterio.tipo.claseCriterio.equals("manual")
            || criterio.tipo.claseCriterio.equals("automod")) {

          // Únicamente valida cuando se va a finalizar
          // la verificación
          if (actionEnd || actionSave) {
            if (actionEnd) validation.required(key, valor);
            // TODO validaciones de tamaño máximo
            if (criterio.tipo.valorMaximo != null
                && valor != null
                && criterio.tipo.valorMaximo.compareTo(valor) < 0) {
              // validation.addError(key, "El valor "+valor+" es superior al valor máximo permitido:
              // "+criterio.tipo.valorMaximo);
              Messages.warning(
                  "El valor del criterio manual '"
                      + criterio.tipo.jerarquia
                      + " - "
                      + criterio.tipo.nombre
                      + "' ("
                      + FapFormat.formatMoneda(valor)
                      + ") es superior al permitido en ese tipo de criterio: "
                      + FapFormat.formatMoneda(criterio.tipo.valorMaximo));
              if (actionEnd) {
                criterio.valor = criterio.tipo.valorMaximo;
                guardarMaximo = true;
              }
            }
            if (criterio.tipo.valorMinimo != null
                && valor != null
                && criterio.tipo.valorMinimo.compareTo(valor) > 0) {
              Messages.warning(
                  "El criterio manual/automod "
                      + criterio.tipo.jerarquia
                      + " ("
                      + FapFormat.formatMoneda(valor)
                      + ") no llega al mínimo valor permitido: "
                      + criterio.tipo.valorMinimo
                      + ". Se ha establecido como valor a 0,00");
              if (actionEnd) criterio.valor = 0.0;
            }
          }
          if (!validation.hasErrors()) {
            if (guardarMaximo) guardarMaximo = false;
            else criterio.valor = valor;
          }
        }

        if (!validation.hasErrors()) {
          // Comentarios
          if (criterio.tipo.comentariosAdministracion) {
            criterio.comentariosAdministracion = params.get(param + ".comentariosAdministracion");
          }

          if (criterio.tipo.comentariosSolicitante) {
            criterio.comentariosSolicitante = params.get(param + ".comentariosSolicitante");
          }
        }
      }
      if (!validation.hasErrors()) {
        boolean admin = "administradorgestor".contains(AgenteController.getAgente().rolActivo);
        BaremacionService.calcularTotales(evaluacion, admin);
        for (Criterio criterio : evaluacion.criterios) {
          if (criterio.tipo.claseCriterio.equals("auto")) {
            if (actionEnd || actionSave) {
              if (criterio.tipo.valorMaximo != null
                  && criterio.valor != null
                  && criterio.tipo.valorMaximo.compareTo(criterio.valor) < 0) {
                if (actionSave)
                  Messages.warning(
                      "El criterio automático '"
                          + criterio.tipo.jerarquia
                          + " - "
                          + criterio.tipo.nombre
                          + "' ("
                          + FapFormat.formatMoneda(criterio.valor)
                          + ") sobrepasaba el máximo valor permitido. Se ha establecido como valor, su valor máximo posible: "
                          + FapFormat.formatMoneda(criterio.tipo.valorMaximo));
                criterio.valor = criterio.tipo.valorMaximo;
              }
              if (criterio.tipo.valorMinimo != null
                  && criterio.valor != null
                  && criterio.tipo.valorMinimo.compareTo(criterio.valor) > 0) {
                Messages.warning(
                    "El criterio automático "
                        + criterio.tipo.jerarquia
                        + " ("
                        + criterio.valor
                        + ") no llega al mínimo valor permitido: "
                        + FapFormat.formatMoneda(criterio.tipo.valorMinimo)
                        + ". Se ha establecido como valor a 0,00");
                criterio.valor = 0.0;
              }
            }
          }
        }
        if (!Messages.hasErrors()) {
          BaremacionService.calcularTotales(evaluacion, true, true);
        }
        evaluacion.save();
      } else {
        flash(evaluacion);
      }

      if (actionSave || actionEnd) {
        if (actionEnd && !validation.hasErrors()) {
          // Si no hubo errores anteriores, se comprueba si existen validaciones propias de la
          // aplicacion
          botonFinalizar();
          if (!Messages.hasErrors()) {
            evaluacion.estado = EstadosEvaluacionEnum.evaluada.name();
            evaluacion.save();
            Messages.ok(
                "La evaluación del expediente "
                    + evaluacion.solicitud.expedienteAed.idAed
                    + " finalizó correctamente");
            ConsultarEvaluacionesController.index();
          }
        }

        if (actionSave && !validation.hasErrors()) {
          Messages.ok(
              "La evaluación del expediente "
                  + evaluacion.solicitud.expedienteAed.idAed
                  + " se guardó correctamente");
        }

        Messages.keep();
        redirect("fap.FichaEvaluadorController.index", evaluacion.id, "editar");
      }
    } else {
      play.Logger.error("No se cumple el permiso \"guardarEvaluacion\" con ids: " + ids);
      forbidden();
    }
  }