27 abril 2010

zvcExcel 0.9beta

archivado en: ABAP/IV Inventos

Hola sufridos programadores de ABAP:

Es difícil tener la ocasión de escribir “SAP” y “glamour” en la misma frase, a pesar de esta misma.

Esto es especialmente cierto a la hora de exportar ALVs como excel, sobretodo desde versiones antiguas de R/3, pues los resultados son horrorosos a la vista. Y aunque tampoco es la única manera que existe de generar hojas de cálculo desde R/3, ya que tenemos clases ABAP dedicadas, en este caso tenemos que hablar de fealdad a la hora de programarlas.

Si además queremos construir excels un tanto complejos con fórmulas, diversidad de titulares, disposiciones no estrictamente tabulares, etc…, etc… entonces estamos en un auténtico aprieto.

ejemplo elemental de uso de la librería
Para resolver todo esto presento la librería zvcExcel que, aunque no está todo lo acabada que me gustaría, en su forma actual ofrece muchas posibilidades, siendo su mejor baza el enfoque de programación que soporta, muy cercano, en el fondo, al del usuario de Office.

Un humano, a la hora de generar hojas de cálculo, se dedica a mover el cursor e ir rellenando allí donde le apetece o le parece más adecuado, gozando de mucha libertad a la hora de hacer el diseño. Un programa, sin embargo, por lo general sólo se limita a rellenar filas y columnas de datos con una línea de cabecera y gracias.

zvcExcel utiliza una especie de cursor que podemos “mover” libremente (utilizando un lenguaje de macros) rellenando las casillas que nos interesan, dotándolas así de diferente estética, contenidos… y ¡hasta de fórmulas!

Mientras que lo habitual a la hora de totalizar o realizar cálculos en la exportación de éxceles es incrustarlos a piñon en el resultado, zvcExcel permite que introduzcas auténticas fórmulas de excel para que el usuario final, si lo desea, pueda modificar valores en la hoja resultante sin que las cifras se descuadren.

Lo mejor para entender cómo funciona la librería es leer este código de ejemplo, que es el que genera el gráfico de arriba y está ampliamente comentado. Lo único que precisa para funcionar es el include que al final de este artículo adjunto, junto con este mismo programa, en un zip:

REPORT zvcexcel_ejemplo.

INCLUDE zvcexcel.

DATA it_excel TYPE TABLE OF vcxt_excel.
DATA it_ekpo TYPE TABLE OF ekpo WITH HEADER LINE.

START-OF-SELECTION.

  SELECT *
  FROM ekpo
  INTO TABLE it_ekpo
  UP TO 10 ROWS
  WHERE netpr NE 0
    AND menge NE 0
    AND txz01 NE space.

* COMIENZA LA GENERACIÓN DEL EXCEL
* refrescamos nuestra tabla it_excel, que hemos definido nosotros.
  REFRESH it_excel.

* este macro borra las definiciones anteriores y establece un estilo
* de texto por defecto para toda la hoja, y sitúa el cursor en A1
  vcx_initialize.

* este macro hace que cada vez que ejecutemos VCX_CELLVAL, el cursor
* avance una casilla en horizontal. con VCX_AUTOXINC_OFF desactivamos.
  vcx_autoxinc_on.

* empezamos a definir la estética. es necesario saber CSS
* en algunos casos, como ciertas fórmulas, deberemos investigar
* los CSS propios de microsoft, que comienzan por mso-
  vcx_classcss 'titulo' 'font-family:impact;font-size:24px'.
  vcx_classcss 'cabeza' 'font-weight:bolder;font-size:14px;' &
  'background-color:#660000;color:#ffffff'.
  vcx_classcss 'numero' 'color:#ff9900;font-size:12px;' &
  'font-weight:bolder'.
  vcx_classcss 'total' 'font-size:14px;border-top:1px #000000 solid'.
* definimos la clase fecha, para campos tipo datum, utilizando
* un valor por defecto de la librería
  vcx_classcss 'fecha' vcxdefaultdateformat.

* vamos a cargar la primera celda de título.
* le asignamos una estética ANTES de dar valor
  vcx_cellclass 'titulo'.
* fusionamos 6 celdas en horizontal para que quepa bien el título
  vcx_cellspan 6.
* y al final le damos valor. debido a VCX_AUTOXINC_ON avanza el cursor
  vcx_cellval 'ejemplo de excel generado con zvcExcel'.
* salto de línea
  vcx_crlf.
* la celda de abajo la usaremos de comentario y tomará estética defecto
  vcx_cellval 'un tanto elemental'.
  vcx_crlf.

* esta instrucción hace que TODAS las celdas definidas a partir de ahora
* tengan estética tipo 'cabeza'.
  vcx_autoclass 'cabeza'.
  vcx_cellval 'Pedido'.
  vcx_cellval 'Pos.'.
  vcx_cellval 'Concepto'.
  vcx_cellval 'Fecha'.
  vcx_cellval 'Cant.'.
  vcx_cellval 'Un.'.
  vcx_cellval 'Precio'.
* con space desactivamos la clase 'cabeza'.
  vcx_autoclass space.
  vcx_crlf.

* internamente no se trabaja en formato A1 típico excel, sino con
* coordenadas x,y type i. vcx_x y vcx_y recogen en todo momento las
* coordenadas actuales del cursor.

* filainicio y filafin nos servirá para delimitar el rango de una suma
  DATA filainicio TYPE i.
  DATA filafin TYPE i.

  filainicio = vcx_y.

* un vulgar loop nos permite rellenar la hoja rápidamente
  LOOP AT it_ekpo.
    vcx_cellclass 'numero'.
    vcx_cellval it_ekpo-ebeln.
    vcx_cellval it_ekpo-ebelp.
    vcx_cellval it_ekpo-txz01.
    vcx_cellclass 'fecha'.
    vcx_cellval it_ekpo-aedat.
    vcx_cellval it_ekpo-menge.
    vcx_cellval it_ekpo-meins.
    vcx_cellval it_ekpo-netpr.
    vcx_crlf.
  ENDLOOP.

* como al final hay un salto de línea, restamos uno para rango de suma
  filafin = vcx_y - 1.

* vamos a poner la fila de totales con una raya encima
  vcx_autoclass 'total'.
* desplazamos el cursor a la derecha hasta la columna F
  DO 5 TIMES.
    vcx_cellval ''.
  ENDDO.
* también podríamos haber utilizado VCX_XINC que mueve cursor a derecha

* escribimos la leyenda para el total.
  vcx_cellval 'SUMA:'.

* la siguiente instrucción nos va a meter en la celda la fórmula
* =SUMA(G4:G53)
* aunque nosotros lo hacemos como si dijeramos (7,4):(7,53) y no G4:G53
  vcx_cellsum 7 filainicio 7 filafin.
  vcx_cellval space.

* en este ejemplo VCX_CELLFORMULA, más complicado: permite introducir
* cualquier fórmula siempre que sea en el formato interno de excel, en
* el que el nombre de las funciones no es el español. también debemos
* utilizar coordenadas alfanuméricas y no numéricas. no es problema:

* si queremos convertir una coordenada x en letras,
* lo haremos con VCX_NUMBER2ALFA. no hay límite más allá que
* el del propio excel.

* del mismo modo, VCX_COORDS2ALFA convierte (x,y) en formato A1. si uno
* de los valores es 0, lo transforma en totalidad de fila o columna
* ejemplo: ncoords2alfa 4 3 valor. hace valor = 'D3'.
* ejemplo: ncoords2alfa 4 0 valor. hace valor = 'D'.

* aunque aquí ya no haría falta, desactivamos la clase 'total'.
  vcx_autoclass ''.

* generamos el excel dentro de la tabla
  vcx_excelgenerate it_excel.

* y lo descargamos localmente.
  vcx_exceldownload it_excel 'c:prueba.xls'.

* importante: si excel ya tiene abierto el fichero, no lo podremos
* descargar.

* otros macros están descritos dentro de la librería y no
* necesitan mucha explicación

Lo único que lamento de esta librería es el hecho de que todavía no está lo suficientemente completa para mi gusto, puesto que las posibilidades de ampliación son enormes. Pero no dudo que lo iré haciendo a medida que necesite funcionalidades.

Como siempre, recomiendo el uso de esta librería siempre que se respete la autoría, el link a este glob, etc… y animando a aquellas personas que decidan ampliarla a compartir su trabajo tras hacerlo.

Podéis descargar el include zvcExcel y el programa de ejemplo aquí.

archivado en: ABAP/IV Inventos

22 abril 2010

ruta de la seimeira

archivado en: Admirable Eo Rutas

Durante mi estancia esta semana santa en torno al río Eo hice un montón de rutas y de fotos.

La ruta de la seimeira (seimeira significa salto de agua en eonaviego) parte de Pumares, un pueblecito que está a apenas un kilómetro de Santa Eulalia de Oscos, también conocida como Santalla. El código de la ruta es P.R.AS-116.

un árbol precioso de camino a pumares desde santa eulalia
Es importante no despistarse. Aunque desde la rotonda de Santalla indica la dirección correctamente, algún desvío que nos encontremos por el camino puede confundirnos. Si vamos andando, debemos ir por el asfalto hasta llegar a Pumares propiamente dicho.

panorámica de pumares
Como se ve Pumares es un pueblo precioso muy cuidado y construido con el material tradicional de la zona, que es la pizarra. En él hay un par de hoteles rurales bastante lujosos.

No debemos cruzar el puente, sino seguir recto dejando siempre el río Agüeria a nuestra izquierda.

el río agüeria al principio de la ruta
Al principio de la ruta parece que está todo cuidado como un parque, y los colores de la vegetación de principio de primavera ayuda a hacerlo mágico.

Sin embargo, esta sensación desaparece pronto a medida que el camino se va haciendo cada vez más salvaje. De todos modos, la ruta en sí no es muy exigente y apenas tiene 150 metros de desnivel y unos 3-4 km de camino desde el pueblo a la cascada. En algún lugar he leído que no se recomienda a niños pero, sinceramente, no creo que revista problema para ellos.

Y efectivamente, a lo largo de unos centenares de metros desde la anterior foto no dejamos de encontrarnos pequeños saltos de agua y árboles caprichosos y recios…

árbol rarito
…aunque otros no tanto.

árbol roto
Al poco comienzan una serie de desniveles bastante accesibles y el camino se estrecha considerablemente, aunque en compensación parace que en cualquier momento van a salir Frodo y Gandalf de un recodo.

frodo y gandalf pasaron por aquí
justo antes de llegar a ancadeira
Después de una pequeña subida por un camino empedrado llegamos a Ancadeira, un pueblo abandonado en medio de un entorno absolutamente increíble (vaya, esta expresión me la ha pegado zor). Este lugar es uno de los más bonitos de toda la ruta.

árbol en los oscos
Aunque yo no recomendaría a nadie entrar en las casas, yo lo hice a pesar de una laja de pizarra que sobrevivía en el techo derrumbado de una de ellas. Concretamente, esta:

casa en ancadeira
Saqué un montón de fotos de y desde su interior, pero me quedo con estas dos:

desde la casa
en el techo se ve la laja de pizarra a la que me refiero
Avanzando por el camino no tardamos en llegar al Valle del Desterrado, muy cercano a la seimeira. El nombre del valle viene por esta leyenda que podemos ver en un panel instalado en un área recreativa con mesas y demás que hay a unos cientos de metros antes de llegar a la cascada:

Hace muchos años habitaba en Santa Eulalia de Oscos, un señor para el que trabajaba un obediente criado.

Una tarde, regresaban de una jornada de caza y se dirigían a escuchar misa, pero se les hizo un poco tarde. El señor, que no quería perderse la celebración, ordenó al muchacho que se adelantara galopando y diera orden al cura de retrasar la ceremonia para que le diera tiempo a llegar. Así hizo: dando fusta a su caballo llegó a la iglesia antes de iniciar la misa. Le dijo al cura que aguardase, que su amo estaba en camino. El párroco, viendo que ya estaba congregado todo el concejo, pese a las súplicas del joven se negó a retrasar la hora de inicio y comenzó la liturgia.

Cuando llegó el señor, ya estaban abandonando todos la iglesia. Pidió explicaciones a su criado y se enfureció de tal modo que le ordenó que matase al cura o que ahí mismo mandaría matarle a él. El criado, viéndose tan acosado, no vió otra solución que obedecer a su amo. Mató al cura con la esperanza de que no le prendieran, pero su mismo amo le delató. La pena que le correspondía al joven era morir en la horca.

Por aquellos tiempos se daba la circunstancia de que todos los vecinos de Santa Eulalia excepto nueve pertenecían a la nobleza. Una disposición real otorgaba el título de hidalguía a todos aquellos habitantes que fuesen autosuficientes, o lo que es lo mismo aquellos, que no necesitaban trabajar ni comerciar con nadie. Como quiera que en los Oscos casi todas las caserías producían todo aquello que necesitaban para vivir, a muchos de sus vecinos se les dio el título de hidalgos. Estos hidalgos no poseían riqueza y trabajaban de sol a sol para sobrevivir, pero tenían título.

Llegado el día del ajusticiamiento se congregó casi todo el concejo. A la hora de levantar la horca, como los nobles no podían ejercer de verdugos no había brazos suficientes capaces de elevarla. Hubo que cambiar la sentencia del criado y, librado de la ejecución se le desterró de por vida a sitio donde no oyera carro chirriar, gallo cantar ni campana sonar. Por aquel entonces poca gente se aventuraba a ir más allá de la aldea de Ancadeira y aquí lo confinaron. Desde entonces este valle se conoce con el nombre de Valle del Desterrado.

La verdad es que la historia tiene retranca. Y justo enfrente del cartel podemos ver una corripa, que es una construcción de piedra donde la gente guardaba las castañas en invierno para que se mantuvieran frescas y comestibles hasta el verano.

una corripa
Una vez pasada este área recreativa nos encontraremos una intersección con puente que nos lleva, si lo cruzamos, hasta la aldea de Busqueimado, a un kilómetro y medio cuesta arriba. Si no cruzamos el puente y seguimos unos centenares de metros no tardaremos en ver la seimeira, de unos 30 metros de altura.

seimeira
Desde este punto podemos aprovechar para subir a Busqueimado por el puente que dejamos atrás o volver por donde vinimos hasta Pumares.

archivado en: Admirable Eo Rutas

20 abril 2010

voet’s ALV template

archivado en: ABAP/IV

Hola sufridos programadores de ABAP:

Este es mi template para cada vez que tengo que hacer una ALV. Es bastante completo, lo justo para que en el 95% de los casos lo único que tenga que hacer es cambiar la definición de campos y programar la interacción del usuario. Quizás te merezca la pena echarle un vistazo a mi post sobre el tratamiento de colores en ALV.

A disfrutar.

REPORT z_alv_template.

DATA it_alv TYPE STANDARD TABLE OF ekko.

SELECT * FROM ekko INTO TABLE it_alv
    UP TO 137 ROWS.

PERFORM alv_display.

*       _          _ _           _
*  __ _| |_   ____| |_)___ _ __ | | __ _ _   _
* / _` | | \ / / _` | | __| '_ \| |/ _` | | | |
*| (_| | |\ V / (_| | |__ \ |_) | | (_| | |_| |
* \__,_|_| \_/ \__,_|_|___/ .__/|_|\__,_|\__, |
*           FORM PRINCIPAL|_|            |___/
* necesaria tener definida globalmente IT_ALV con
*               los campos y contenidos a mostrar
* as seen on http://glob.cranf.net
* ascii art made at http://ascii.cranf.net
***********************************************************************
FORM alv_display.

** VARIABLES NECESARIAS                       *no es necesario tocar
**------------------------------------------------------------------
  TYPE-POOLS slis.
  DATA alv_fieldcat TYPE slis_t_fieldcat_alv.
  DATA alv_layout   TYPE slis_layout_alv.
  DATA alv_sort     TYPE slis_t_sortinfo_alv.
  DATA ls_sort      TYPE slis_sortinfo_alv.
  DATA alv_events   TYPE slis_t_event.
  DATA ls_events    TYPE slis_alv_event.
  DATA lsyrepid LIKE sy-repid. lsyrepid = sy-repid.
  CLEAR alv_fieldcat. REFRESH alv_fieldcat.
  CLEAR alv_layout.

** definición de LAYOUT                                 *recomendado
**------------------------------------------------------------------
  alv_layout-colwidth_optimize = 'X'. "optimización de ancho columna
  alv_layout-cell_merge = 'X'.        "fusión de celdas
  alv_layout-no_vline = 'X'.          "sin línea separadora vertical
  alv_layout-zebra = ' '.             "franjas alternas de color

** campo de COLOR DE LÍNEA                                 *opcional
**------------------------------------------------------------------
* alv_layout-info_fieldname = 'COLOR'. "campo it_alv color de línea

** campo de COLOR DE CELDAS                                *opcional
**------------------------------------------------------------------
* alv_layout-coltab_fieldname = 'COLOR'  "campo it_alv color celdas

** campo de SELECCIÓN DE LÍNEA                             *opcional
** el campo puesto aquí no necesita ir en el fieldcat
**------------------------------------------------------------------
* alv_layout-box_fieldname = 'LOEKZ'.

** SORTING                                                 *opcional
** necesario si queremos que cell_merge funcione la primera vez
**------------------------------------------------------------------
*  ls_sort-fieldname = 'EBELN'.
*  ls_sort-up = 'X'. "contrario es ls_sort-down = 'X'
*  APPEND ls_sort TO alv_sort.

** definición de EVENTS                                    *opcional
**------------------------------------------------------------------
*  ls_events-name = 'TOP_OF_PAGE'.
*  ls_events-form = 'ALV_TOP_OF_PAGE'.
*  APPEND ls_events TO alv_events.

** definición de FIELDCAT       *obligatorio si no se usa estructura
**------------------------------------------------------------------
  PERFORM alv_fields USING alv_fieldcat[].

**                                             *llamada a la función
**------------------------------------------------------------------
  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
       EXPORTING
            is_layout                = alv_layout
            it_fieldcat              = alv_fieldcat
            it_sort                  = alv_sort
            it_events                = alv_events
            i_callback_program       = lsyrepid
            i_callback_pf_status_set = 'ALV_SET_STATUS'
            i_callback_user_command  = 'ALV_COMMAND'
*           I_STRUCTURE_NAME         = 'TABLADICCIONARIO'
*                   en este caso no usaríamos it_fieldcat
       TABLES
            t_outtab                 = it_alv.
*-------------------------------------------------------------------
ENDFORM.                    "alv_display
***********************************************************************
*       _                _
*  __ _| |_   _____ ___ | | ___  _ __
* / _` | | \ / / __| _ \| |/ _ \| '__|
*| (_| | |\ V / (__ (_) | | (_) | |
* \__,_|_| \_/ \___|___/|_|\___/|_|
DEFINE alv_color.
* MACRO para convertir color humano en código color Cxyz
* uso: alv_color campo 'texto en español'

* en EMPHASIZE o en el campo COLOR usamos char(4)
* con la forma Cxyz, x€[0,7]; y,z€[0,1] / y+z<2
* valores de x:
*            0 COL_BACKGROUND fondo (gris)
*            1 COL_HEADING    azul oscuro
*            2 COL_NORMAL     azul claro
*            3 COL_TOTAL      amarillo
*            4 COL_KEY        azul
*            5 COL_POSITIVE   verde
*            6 COL_NEGATIVE   rojo
*            7 COL_GROUP      naranja
* valores de y: 0 = normal, 1 = intenso
* valores de z: 0 = xy afecta fondo, 1 = x invertido

*          OJO: ls_fieldcat-key = 'X' invalida lo anterior

  &1 = 'C000'. "gris fondo por definición
  find 'azul' in &2 ignoring case.
  if sy-subrc = 0.
    &1+1(1) = '4'.
    find 'claro' in &2 ignoring case.
    if sy-subrc = 0. &1+1(1) = '2'. endif.
    find 'oscuro' in &2 ignoring case.
    if sy-subrc = 0. &1+1(1) = '1'. endif.
  endif.
  find 'amar' in &2 ignoring case.
  if sy-subrc = 0. &1+1(1) = '3'. endif.
  find 'verd' in &2 ignoring case.
  if sy-subrc = 0. &1+1(1) = '5'. endif.
  find 'rojo' in &2 ignoring case.
  if sy-subrc = 0. &1+1(1) = '6'. endif.
  find 'nara' in &2 ignoring case.
  if sy-subrc = 0. &1+1(1) = '7'. endif.
  find 'ntens' in &2 ignoring case.
  if sy-subrc = 0. &1+2(2) = '10'. endif.
  find 'nver' in &2 ignoring case.
  if sy-subrc = 0. &1+2(2) = '01'. endif.
END-OF-DEFINITION.
***********************************************************************
* COLOR para su uso dentro del FORM ALV_FIELDS
DEFINE fld_color.
  alv_color ls_fieldcat-emphasize &1.
END-OF-DEFINITION.
***********************************************************************
* TEXTO para su uso dentro del FORM ALV_FIELDS
DEFINE fld_texto.
  ls_fieldcat-seltext_s   = &1.
  ls_fieldcat-seltext_m   = &1.
  ls_fieldcat-seltext_l   = &1.
END-OF-DEFINITION.
***********************************************************************
*       _        __ _      _     _
*  __ _| |_   __/ _|_) ___| | __| |___
* / _` | | \ / / |_| |/ _ \ |/ _` | __|
*| (_| | |\ V /|  _| |  __/ | (_| |__ \
* \__,_|_| \_/ |_| |_|\___|_|\__,_|___/DEFINICIÓN DE CAMPOS
*
FORM alv_fields USING pfieldcat TYPE slis_t_fieldcat_alv..
  DATA ls_fieldcat  TYPE slis_fieldcat_alv.
  REFRESH pfieldcat.

  CLEAR ls_fieldcat.
  ls_fieldcat-fieldname   = 'EBELN'.
  ls_fieldcat-ref_tabname = 'EKKO'.
  ls_fieldcat-key         = ''.
  ls_fieldcat-icon        = ''.
  ls_fieldcat-checkbox    = ''.
  ls_fieldcat-no_out      = ''.
  fld_color 'amarillo'.
  APPEND ls_fieldcat TO pfieldcat.

** TEMPLATE PARA CAMPOS DE DICCIONARIO
*  CLEAR ls_fieldcat.
*  ls_fieldcat-fieldname   = ''.
*  ls_fieldcat-ref_tabname = ''.
*  ls_fieldcat-key         = ''.
*  ls_fieldcat-no_out      = ''.
*  fld_color ''.
*  APPEND ls_fieldcat TO pfieldcat.

** TEMPLATE PARA TEXTOS S/M/L IGUALES
*  CLEAR ls_fieldcat.
*  ls_fieldcat-fieldname   = ''.
*  ls_fieldcat-key         = ''.
*  ls_fieldcat-icon        = ''.
*  ls_fieldcat-checkbox    = ''.
*  ls_fieldcat-no_out      = ''.
*  fld_texto ''.
*  fld_color ''.
*  APPEND ls_fieldcat TO pfieldcat.

** TEMPLATE PARA TEXTOS S/M/L DISTINTOS
*  CLEAR ls_fieldcat.
*  ls_fieldcat-fieldname   = ''.
*  ls_fieldcat-key         = ''.
*  ls_fieldcat-icon        = ''.
*  ls_fieldcat-checkbox    = ''.
*  ls_fieldcat-no_out      = ''.
*  ls_fieldcat-seltext_s   = ''.
*  ls_fieldcat-seltext_m   = ''.
*  ls_fieldcat-seltext_l   = ''.
*  fld_color ''.
*  APPEND ls_fieldcat TO pfieldcat.

ENDFORM.                    "alv_fields
*
* FORMS DE LLAMADA DINÁMICA DESDE LA ALV
***********************************************************************
*       _                _       _        _
*  __ _| |_   _____  ___| |_ ___| |_ __ _| |_ _   _ ___
* / _` | | \ / / __|/ _ \ __| __| __| _` | __| | | | __|
*| (_| | |\ V /\__ \  __/ |_\__ \ |_ (_| | |_| |_| |__ \
* \__,_|_| \_/ |___/\___|\__|___/\__|__,_|\__|\__,_|___/
*  DEFINICIÓN DE STATUS BAR
FORM alv_set_status USING prt_extab TYPE slis_t_extab.
* barra de status de referencia
**--------------------------------------------------------------------
  SET PF-STATUS 'STANDARD_FULLSCREEN' OF PROGRAM 'SAPLKKBL'.
ENDFORM.                    "alv_set_status
***********************************************************************
*       _       _                     __
*  __ _| |_   __ |_ ___  _ __   ___  / _|_ __   __ _  __ _  ___
* / _` | | \ / / __| _ \| '_ \ / _ \| |_| '_ \ / _` |/ _` |/ _ \
*| (_| | |\ V /| |_ (_) | |_) | (_) |  _| |_) | (_| | (_| |  __/
* \__,_|_| \_/  \__|___/| .__/ \___/|_| | .__/ \__,_|\__, |\___|
* CABECERA DE LISTA     |_|             |_|          |___/
FORM alv_top_of_page.

  DATA: lls_list TYPE slis_listheader.
  DATA: lalv_list TYPE slis_t_listheader.

**definición de cada línea de la cabecera
**el typ puede tener valores: H = header, S = selection, A = action
**--------------------------------------------------------------------
  lls_list-typ = 'H'.
  lls_list-info = 'TÍTULO DEL LISTADO'.
  APPEND lls_list TO lalv_list.

*---------------------------------------------------------------------
  CALL FUNCTION 'REUSE_ALV_COMMENTARY_WRITE'
    EXPORTING
      it_list_commentary = lalv_list.
*---------------------------------------------------------------------
ENDFORM.                    "alv_top_of_page
***********************************************************************
*       _                                                    _
*  __ _| |_   _____ ___  _ __ ___  _ __ ___   __ _ _ __   __| |
* / _` | | \ / / __| _ \| '_ ` _ \| '_ ` _ \ / _` | '_ \ / _` |
*| (_| | |\ V / (__ (_) | | | | | | | | | | | (_| | | | | (_| |
* \__,_|_| \_/ \___|___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_|
*  RESPUESTA A COMANDOS
FORM alv_command USING
    r_ucomm LIKE sy-ucomm
    rs_selfield TYPE slis_selfield.

** actualiza celdas editadas en modo edición
**--------------------------------------------------------------------
*  DATA p_ref1 TYPE REF TO cl_gui_alv_grid.
*  CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
*    IMPORTING
*      e_grid = p_ref1.
*  CALL METHOD p_ref1->check_changed_data.

** toma de decisiones en función de UCOMM
**--------------------------------------------------------------------
  CASE r_ucomm.
********* doble click [rs_selfield-tabindex = línea seleccionada]
    WHEN '&IC1'.
********* otros comandos
    WHEN OTHERS.
  ENDCASE.

** cambiamos el field catalog si es necesario hacerlo sobre la marcha
**--------------------------------------------------------------------
*  DATA lfieldcat TYPE slis_t_fieldcat_alv.
*  CALL FUNCTION 'REUSE_ALV_GRID_LAYOUT_INFO_GET'
*    IMPORTING
*      et_fieldcat = lfieldcat[].
*
** podemos escoger el form original o utilizar otro que nos convenga
*  PERFORM alv_fields USING lfieldcat[].
*
*  CALL FUNCTION 'REUSE_ALV_GRID_LAYOUT_INFO_SET'
*    EXPORTING
*      it_fieldcat = lfieldcat[].

* conservamos la posición del click
  rs_selfield-row_stable = 'X'.
  rs_selfield-col_stable = 'X'.

* esencial para que redibuje: NUNCA se debe rellamar a la función
**--------------------------------------------------------------------
  rs_selfield-refresh = 'X'.

ENDFORM.                    "alv_command
***********************************************************************
archivado en: ABAP/IV

18 abril 2010

reloj de avance para tablas internas

archivado en: ABAP/IV


Hola sufridos programadores de ABAP:

A veces es frustrante, tanto para el usuario como para nosotros mismos, no saber cuándo un programa acabará de ejecutarse. Para ello he creado una minilibrería con tres macros, muy sencilla de utilizar, que, convenientemente utilizada en cualquier LOOP, nos mostrará:

  1. lo que está haciendo (que se lo especificaremos nosotros como texto opcional)
  2. el porcentaje de avance de recorrido de la tabla
  3. el tiempo total estimado
  4. el tiempo faltante para acabar
  5. línea procesada y total de líneas de la tabla interna

Las macros son las siguientes:

  • vc_clocktimer texto: Se sitúa dentro del LOOP que nos interesa especificándole un texto corto descriptivo. No hace falta nada más ya que el cálculo se hace a partir de la variable de sistema sy-tabix. Fuera de un LOOP no funcionará.
  • vc_clockreset tabla: se sitúa antes del LOOP para poner el contador de tiempo a cero y analizar la tabla interna en la que vamos a meter el reloj.

  • vc_seconds2hms: de uso interno, convierte segundos a formato HH:MM:SS indicando “+100h” en caso de que el tiempo resultante sea superior a 100 horas. También es utilizable de manera independiente

Al final del código de ejemplo que pongo más abajo, en negrita, se puede ver lo sencilla que es su inserción. El gráfico superior, que muestra el reloj en funcionamiento, corresponde a este mismo programa.

Es importante señalar que asume que la resolución temporal de la sentencia GET RUN TIME está afinada al microsegundo; la opción por defecto de los sistemas SAP. De otro modo dará resultados absurdos.

Más importante aún es avisar de que en conexiones lentas la actualización del reloj en el SAPgui puede afectar negativamente al rendimiento. En ese caso convendrá llamar a la macro vc_clocktimer sólo cada n pasos.

REPORT  z_percentage_clock.
* _  __        _            _
*(_)/ /    ___| | ___   ___| | __
*  / /    / __| |/ _ \ / __| |/ /
* / /_   | (__| | (_) | (__|   <
*/_/(_)   \___|_|\___/ \___|_|\_\
* as seen in http://glob.cranf.net
* ASCII art made using http://ascii.cranf.net
*
***********************************************************************
DATA vc_clocktlines TYPE i.
DATA vc_time0 TYPE f.
***********************************************************************
DEFINE vc_seconds2hms.
  perform vc_seconds2hms using &1 changing &2.
END-OF-DEFINITION.
*
FORM vc_seconds2hms USING pseconds CHANGING phms.
  DATA vc_segundos TYPE uzeit.
  DATA vc_segundosi TYPE i.
  vc_segundosi = pseconds.
  IF vc_segundosi < 360000.
    vc_segundos = vc_segundosi.
    WRITE vc_segundos TO phms.
  ELSE.
    phms = '+100h'.
  ENDIF.
ENDFORM.                    "vc_seconds2hms
***********************************************************************
DEFINE vc_clockreset.
  vc_clocktlines = 0.
  describe table &1 lines vc_clocktlines.
  get run time field vc_time0.
END-OF-DEFINITION.
***********************************************************************
DEFINE vc_clocktimer.
  perform vc_clocktimer using &1.
END-OF-DEFINITION.
*
FORM vc_clocktimer USING vc_ptexto.

  DATA vc_porcentaje TYPE i.
  DATA vc_porcentajet(4).
  DATA vc_pend TYPE f.
  DATA vc_tabixt(8).
  DATA vc_tfillt(8).
  DATA vc_linest(20).

  DATA vc_time TYPE f.
  DATA vc_timetotal TYPE f.
  DATA vc_timetotalt(8).
  DATA vc_timefalta TYPE f.
  DATA vc_timefaltat(8).
  DATA vc_timefin TYPE uzeit.
  DATA vc_timefint(8).
  DATA vc_mensaje(100).

  CHECK vc_clocktlines > 0.
  CHECK sy-tabix > 0.

  GET RUN TIME FIELD vc_time.

  vc_pend = ( vc_time - vc_time0 ) / sy-tabix .
  vc_timetotal = vc_pend * vc_clocktlines / 1000000.
  vc_timefalta = vc_pend * ( vc_clocktlines - sy-tabix ) / 1000000 + 1.
  vc_porcentaje = sy-tabix * 100 / vc_clocktlines.

  WRITE vc_porcentaje TO vc_porcentajet.
  CONCATENATE vc_porcentajet '%' INTO vc_porcentajet.
  CONDENSE vc_porcentajet NO-GAPS.
  WRITE sy-tabix TO vc_tabixt.
  WRITE vc_clocktlines TO vc_tfillt.
  CONCATENATE '[' vc_tabixt '/' vc_tfillt ']' INTO vc_linest.
  CONDENSE vc_linest NO-GAPS.
  vc_seconds2hms vc_timetotal vc_timetotalt.
  vc_seconds2hms vc_timefalta vc_timefaltat.
  vc_timefin = sy-uzeit + vc_timefalta.
  WRITE vc_timefin TO vc_timefint.

  CONCATENATE
    vc_ptexto
    vc_porcentajet
    'Total'
    vc_timetotalt
    'Resta'
    vc_timefaltat
    'Final'
    vc_timefint
    vc_linest
  INTO vc_mensaje SEPARATED BY space.
  CONDENSE vc_mensaje.

  CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
       EXPORTING
            percentage = vc_porcentaje
            text       = vc_mensaje.
ENDFORM.                    "vc_clocktimer
***********************************************************************

*       _                      _
*  ___ (_) ___ _ __ ___  _ __ | | ___
* / _ \| |/ _ \ '_ ` _ \| '_ \| |/ _ \
*|  __/| |  __/ | | | | | |_) | | (_) |
* \___|/ |\___|_| |_| |_| .__/|_|\___/
*    |__/               |_|
*______________________________________________________________________

DATA it_dd02l TYPE TABLE OF dd02l WITH HEADER LINE.

START-OF-SELECTION.

  SELECT *
  FROM dd02l
  INTO TABLE it_dd02l
  UP TO 10 ROWS.

* pone a cero el reloj
  vc_clockreset it_dd02l.

* recorremos la tabla
  LOOP AT it_dd02l.

*   dibuja el reloj con el texto elegido
    vc_clocktimer 'Proceso'.

*   retraso temporal de un segundo por ciclo
    CALL FUNCTION 'RZL_SLEEP'
         EXPORTING
              seconds        = 1
         EXCEPTIONS
              argument_error = 1
              OTHERS         = 2.
  ENDLOOP.
archivado en: ABAP/IV

5 abril 2010

cachopomán en casa pedro (santa eulalia)

archivado en: Cachopomán Eo

Ni aún de retiro espiritual Cachopomán baja la guardia ni por un momento.

Así tuve oportunidad de tomar un cachopo en Casa Pedro, un hotel-restaurante estupendo sito en Santa Eulalia de Oscos. He aquí el artefacto, con paquete de tabaco como referencia:


Ciertamente no es muy grande comparado con otros cachopos que he analizado. Sin embargo, esto no se puede tener en cuenta aquí, sencillamente porque en la zona, muy próxima a Galicia, no existe tradición de cachopo. Más aún, ni siquiera es conocido por muchos hosteleros afincados aquí, como pude comprobar para mi sorpresa. Por tanto, al no haber competencia, el tamaño es, sin más, razonable.

Toca entonces analizar la calidad. Y es ex-qui-si-ta. La carne es realmente buena, así como el jamón y el queso de relleno. Las patatas no parecían congeladas, aunque no pondría la mano en el fuego, y los champiñones de acompañamiento, ligeramente sofritos con ajo, estaban realmente ricos y hasta crujientes. En una época donde es rarísimo encontrar un restaurante con champis que no saben a lata, esto es muy de agradecer. Por su presencia no eché de menos algo de ensalada.

El tamaño es adecuado para una sola persona, que si no es muy voraz se quedará satisfecha. El precio, correcto, teniendo en cuenta que cuesta 11.50€ con pan (muy bueno) incluido.

Mi diagnóstico es que la relación calidad/precio es muy buena, aunque la cantidad se queda un poco atrás; pero debemos tener en cuenta la falta de tradición en la zona. Aun así, por otros platos que probé (pornográficas las croquetas de cabrales) puedo afirmar que el sitio es muy recomendable para zampar en general.

archivado en: Cachopomán Eo

río eo (2)

archivado en: Comer Eo Naturaleza

Ya estoy de vuelta a la vida cotidiana y por tanto, a este mi/tu/vuestro superglob.

En mi último post puse una de las muchas fotos que hice durante mi escapada de semana santa a la zona del río Eo. Fuí hasta allí buscando reencontrarme con la Naturaleza después de pasar 6 meses completamente volcado en un proyecto de implantación de un sistema informático, es decir, delante de una pantalla.

Como el objetivo de este pequeño viaje era más de carácter interno que externo, encuentro cierta dificultad a la hora de contar lo que vi y viví, ya que prefiero guardar reserva. Así que al contrario que en otros viajes que hice, que conté de modo cronológico, haré un pequeño resumen de mi estancia y en futuros posts contaré algunas de las rutas de senderismo que hice durante este tiempo, algunas de belleza sobrecogedora.

PRIMERAS IMPRESIONES

Ciertamente me llevé una sorpresa nada más llegar a la zona, ya que no me la encontré tan salvaje como pudiera esperar, al menos aparentemente. La fuerte inversión con fondos europeos sobre estos concejos ha potenciado su carácter turístico y los pueblos, caminos y carreteras están tan cuidados y arreglados que da la sensación de haber caído en una trampa para turistas. Pero como pude comprobar, nada más lejos de la realidad.

Las comarcas que rodean al Eo (a las que se les suele llamar sin más Los Oscos) están formadas por siete ayuntamientos distintos (incluyendo los Oscos propiamente dichos) que viven en gran parte, efectivamente, del turismo. Pero no es el típico turismo masivo que se puede encontrar en la costa mediterránea; al contrario, se basa sobretodo en el respeto al patrimonio natural y está dirigido a gente amante de la montaña y de las rutas por naturaleza. Y aunque mucha gente ha criticado y critica este enfoque, puedo afirmar que al menos por lo que fui testigo, esta inversión ha potenciado positivamente una zona que estaba prácticamente condenada al aislamiento y la autarquía económica.

Si no eres de Asturias o Galicia lo más probable es que la palabra Oscos no te diga nada. Pero esta zona destacaba por su peculiar modo de vida. Ancestralmente desconectada del resto del mundo por su geografía y situación, la gente que aquí vivía prácticamente era autosuficiente en todos los sentidos. Mucha de la gente que aquí vivía poseía casas Y terrenos que les proveían de todo al precio de trabajar de sol a sol. La gente se hacía con sus propios medios desde el pan hasta el licor, y la única industria, de carácter artesanal, que tenía cierta relevancia hacia el mundo exterior estaba basada en el hierro. Aún se conservan unas cuantas herrerías tradicionales impulsadas por energía hidráulica, y hay toda una cultura de navajas y cuchillos artesanales en plena vigencia.

ALOJAMIENTO

Aunque inicialmente me iba a hospedar en San Martín de Oscos, allí conocido como Samartín d’Ozcos, al final encontré habitación en Santa Eulalia, localmente llamado Santalla d’Ozcos. Véase foto.


Allí me hospedé en Casa Pedro, un bonito hotel atendido por, como es fácil deducir, Pedro, un hostelero multigalardonado por la calidad de sus servicios. Y ciertamente es un reconocimiento merecido pues Pedro es una persona que se desvive porque estés agusto y vuelvas. No sólamente se preocupa de dar comida y alojamiento de excelente calidad (hasta el café que sirve es de primera) a un precio muy asequible sino que demuestra que para esto de la hostelería no vale cualquiera: hay que saber y valer para ello.



LA GENTE

Toda la gente con la que tuve oportunidad de tratar, tanto en Santalla como en otros muchos sitios que estuve, es extraordinariamente amable. No sólo es abierta por naturaleza sino que en ningún momento tienes la sensación de que te intenten timar, como pasa en muchos sitios que viven principalmente del turismo.

Me produjo una gran impresión la manera de hablar. Al ser una zona que experimentó un aislamiento durante siglos, en él se mezclan influencias del gallego y del asturiano y el dialecto que se habla, el eonaviego (que toma el nombre por estar la zona comprendida entre el río Eo y el Navia) está muy vivo, como pude comprobar al oir a un montón de gente charlar en el primer bar-tienda en el que entré y durante el resto de mi estancia.

Yo que aprendí a hablar el bable que me enseñó mi abuela y estoy bastante familirizado con la lengua asturiana más o menos oficial que se habla en Asturias lo pasé mal para entender a la gente de allí. Aunque las palabras no son muy distintas y con atención son reconocibles, la fonética es muy distinta a la que se utiliza en el resto de Asturias: el uso de la che vaqueira, por ejemplo, está muy extendido. ¿A qué suena? pues un poco a como los argentinos pronuncian la y griega y la elle: ¿shegaste sha?.

EL PAISAJE

Es bastante montañoso, aunque variado. Mientras Taramundi tiene montes surcados por valles profundos y umbríos, de modo similar a Grandas de Salime o Pesoz, San Martín es mucho más abierto y formado por colinas redondeadas. En Santa Eulalia se pueden encontrar ambos tipos de paisajes.

En la zona ha habido muy poca industrialización, al contrario que en el resto de Asturias; eso no significa que esté exenta del todo de ella. Cerca de Grandas de Salime, sobre el río Navia, hay una presa con un conjunto industrial en ruinas que parece una especie de Minas Tirith que hubiera sido conquistada por Mordor. Es hasta bello por lo ominoso que resulta.


Y que valga la referencia a El Señor de los Anillos para mandarle un mensaje a Peter Jackson: Macho, entiendo que para rodar tus películas sobre la obra de Tolkien hayas escogido tu tierra, Nueva Zelanda. Pero si hubieras necesitado una alternativa, tendrías que haber venido por aquí.

Los paisajes, una vez que nos alejamos de las zonas más o menos urbanizadas, exquisitamente cuidadas, son sobrecogedores. Como veréis en futuros posts, o mismamente en la foto del anterior, no es difícil imaginarse a Gandalf y sus hobbits apareciendo por un recodo del camino.

A todo ello contribuye la omnipresente presencia de la pizarra, que domina la geología y la construcción en la zona. La increíble variedad de azules y violetas de esta piedra supone un contraste continuo con la infinita variedad de verdes, desde el limón hasta el turquesa, que explotan continuamente delante de los ojos, así como con los ocres y rojos de la madera y las hojas secas. Hay momentos en el que la belleza del entorno es tan sobrecogedora que deseas, sin éxito, parar los procesos mentales para poder percibirlos plenamente sin sentirse abrumado. Tanto es así que en algún caso, sólo tras ver las fotos en casa, he podido percatarme de lo obsceno de la belleza de algunos de los sitios en los que estuve.


Los muros que separan los campos están prácticamente todos hechos de esta pizarra, más o menos inundada de vida: cuando no lo es por el sedum y otras plantas más o menos suculentas, el musgo invade la totalidad de la piedra.


Es propio de esta zona utilizar lajas de pizarra clavadas en la tierra para delimitar los pastos y caminos de modo rapido y eficaz, sin el esfuerzo de levantar los muros de piedra clásicos. Se llaman chantos, como estos de Martul, entre Santa Eulalia y San Martín.


COMER

Se come de maravilla. Típicos productos de los Oscos son el queso y la miel, de los cuales traje alguna muestra para revivir la estancia una vez de nuevo en la ciudad. Pero aparte de los productos típicos para adquirir hay un montón de sitios para comer bien. El propio hotel Casa Pedro, donde me alojé, es un sitio excelente para probar la gastronomía de la zona, de carácter rural. Aquí no espere uno encontrarse pato a la naranja y melifluás parecidas: aquí lo que hay que tomarse son unos buenos huevos fritos con jamón, chorizo y patatas fritas. Y no digo ya el pan, riquisimo, que se hace y toma por aquí.


Uno de los sitios donde me gustó mucho comer, aunque probé unos cuantos, fue en el restaurante Las Cortes (no tienen web, sorry) de Pesoz. Precisamente allí me tomé el plato que acabo de mencionar. Lo que pasa es que me pilló con tanta hambre que no me acordé de hacer foto cuando me lo sirvieron, pero la ración, aparte de considerable, tenía un precio más que razonable. En la foto superior se ve el gusto con el que reformaron la antigua cuadra que ahora es comedor.

Hablaré de otros sitios para comer en los futuros posts sobre las rutas de la zona. Pero ya puedo adelantar que en todos ellos la casa os convidará seguramente a un pornográfico licor de orujo con miel.

Curiosamente, y para mi pesar, no hay apenas tradición de cachopo. De hecho, muchos hosteleros nisiquiera conocen el plato. De todos modos me tomé uno en Casa Pedro que analizo aquí.

EL CLIMA

Pues no muy distinto del resto de Asturias. Obviamente, cuando hay buen tiempo da gusto estar aquí. Pero cuando no, llueve; y bastante. Aunque por el hecho de estar relativamente bajos respecto al nivel del mar no hace mucho frío a no ser que tiremos en dirección a Galicia o León. A mí no me importa especialmente que el día esté gris o llueva: gran parte de la belleza del lugar se debe a la gran cantidad de agua presente en la zona, y alguna de las rutas que hice fueron bajo la lluvia. No por ello fueron menos hermosas.

Y como muestra un botón. Os pongo a continuación dos panorámicas realizadas en dirección al Mar Cantábrico desde el Alto de la Garganta, que separa la Ría del Eo del interior, a mi llegada (buen tiempo) y a mi partida cinco días después (mal tiempo).



¿Cuál os parece más bonita? Seguramente la segunda. Por eso el hecho de que no haya Sol no debería preocupar a la hora de acercarse por aquí. Otra cosa es que haya temporal, claro.

Y nada. Como ya he repetido varias veces a lo largo del artículo, en el futuro contaré con detalle algunas de las rutas que hice.

archivado en: Comer Eo Naturaleza
índice   cranf.net   wordnadapress
1