jueves, 24 de enero de 2013

Oreka IT: Blog de SAP y tecnología

Para febrero, espero poder revisar este blog e ir añadiendo nuevas entradas.

Mientras tanto, os enlazo un blog de SAP, Oreka IT que os dejo de forma permanente en los enlaces.

viernes, 16 de diciembre de 2011

Editor SQL y generador de ALV para SAP

De parte de Tamas Holics me llegan dos herramientas que ha programado para sap:

- Un editor de SQL para SAP, que podéis encontrar aquí:
http://www.hovitaga.com/opensql-editor-overview

- Un generador de Reports, que puede crear ALV basados en consultas SQL, y que podéis encontrar aquí
http://www.hovitaga.com/report-generator-overview
con un vídeo de muestra aquí:
http://www.youtube.com/watch?v=cKSBvz_sd98

Amablemente ha creado versiones gratuitas de estas herramientas que podéis bajar y probar en vuestro sistema. Solo hay que bajarlas, hacerles un copy/paste en la SE38 y activar.

Las versiones gratuitas se pueden descargar aquí:
http://www.hovitaga.com/try

jueves, 6 de marzo de 2008

Entradas en tabla con la SE16N

La transacción SE16N permite modificar, sin programas de carga, las entradas de una tabla. Para ello, hay que seguir los siguientes pasos:

1. Introducir la transacción SE16N (evidentemente).

2. Introducimos el nombre de la tabla (ZTABLA), y le damos a INTRO para que aparezcan los campos de dicha tabla


3. En la ventana donde introducimos las transacciones de la barra de herramientas, escribimos &SAP_EDIT



4. Finalmente, introducimos los criterios de selección que querramos modificar, ejecutamos (con F8) y nos aparecerá el modo de edición, donde podremos crear entradas, borrarlas o modificarlas.



Modificar denominaciones de campo sin la SE11

En ocasiones, nos es imposible modificar las denominaciones de campo de un elemento de datos.

Normalmente, introducimos en la transacción SE11 el nombre del elemento (en este caso, ZANIO), le damos a MODIFICAR y a la pestaña Denom. Campo








Pero habrá casos en los cuales la opción de modificar textos estará deshabilitada. ¿Qué hacemos entonces?

Pues fácil: nos vamos a la transacción CMOD, y navegamos por
Pasar a > Ampliaciones glob. > Palabras clave > Modificar




En la ventana, introducimos el Elemento de datos


Y desde aquí, podremos modificar todas las denominaciones de campo, así como la descripción, del elemento de datos.


martes, 4 de marzo de 2008

Inserción/modificación masiva o línea a línea - LUW

A la hora de insertar/modificar registros en una tabla del diccionario a partir de otra tabla (cuyo contenido podría venir de un fichero de texto, o de una consulta de selección) podemos elegir entre insertar los registros uno a uno, o de forma masiva.

Si se hace de registro en registro, corremos el riesgo de que se abra una unidad lógica de trabajo (LUW, por sus siglas en inglés), que bloquea la tabla momentáneamente. Se trata de unos microsegundos, pero si se intenta cualquier otra modificación sobre la tabla, tendremos un fallo de ejecución.
Por el contrario, si lo hacemos de forma masiva podemos pedir al programa que acabe el trabajo (que finalice la LUW) y que se espere, con la sentencia
COMMIT WORK AND WAIT.

Esto también lo podemos hacer registro a registro, pero el WAIT hará que la espera se extienda y, probablemente, tengamos un dump por tiempo de ejecución sobrepasado.

Aunque recomiendo siempre la segunda opción (es decir, un insert/update masivo) veremos ambas.

Para el ejemplo, tendremos dos tablas Z: ZORIGEN y ZDESTINO. Ambas tienen la misma estructura: el mandante de toda la vida, un campo clave llamado ZCLAVE, de tipo N y longitud 5, y dos campos para almacenar textos pequeñitos (25 caracteres) ZTEXTO1 y ZTEXTO2.

En la pantalla de selección, podremos elegir entre hacer la inserción masiva o línea a línea.

He aquí el programa:

*&---------------------------------------------------------------------*
*& Report Z_LUW
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

REPORT z_luw.

*&---------------------------------------------------------------------*
*& TOP
*&---------------------------------------------------------------------*
TABLES:
zorigen,
zdestino.

DATA:
"Para selección de datos
gt_origen LIKE TABLE OF zorigen WITH HEADER LINE,
"Para leer ZDESTINO
gt_destino LIKE TABLE OF zdestino WITH HEADER LINE,
"Para insertar en zdestino masivamente
gt_inser LIKE TABLE OF zdestino WITH HEADER LINE,
"Para modificar zdestino masivamente
gt_modif LIKE TABLE OF zdestino WITH HEADER LINE,

"Para insertar/modificar zdestino línea a línea, un work-area
wa LIKE LINE OF zorigen,

"Variables para los resultados
gv_leidas LIKE sy-dbcnt, "Leidas
gv_inser1 LIKE sy-dbcnt, "Deberían haberse insertado
gv_modif1 LIKE sy-dbcnt, "Deberían haberse modificado
gv_inser2 LIKE sy-dbcnt, "Se insertaron realmente
gv_modif2 LIKE sy-dbcnt. "Se modificaron realmente

*&---------------------------------------------------------------------*
*& PANTALLA DE SELECCIÓN
*&---------------------------------------------------------------------*
PARAMETERS:
p_mas RADIOBUTTON GROUP gr1, "Masivamente
p_1a1 RADIOBUTTON GROUP gr1. "Línea a línea

*&---------------------------------------------------------------------*
*& EVENTOS
*&---------------------------------------------------------------------*
INITIALIZATION.
REFRESH: gt_origen, gt_destino, gt_inser, gt_modif.
CLEAR: gt_origen, gt_destino, gt_inser, gt_modif,
gv_leidas, gv_insertadas, gv_modificadas.
*&---------------------------------------------------------------------*
START-OF-SELECTION.
"Seleccionamos todo ZORIGEN y ZDESTINO
SELECT * FROM zorigen INTO CORRESPONDING FIELDS OF TABLE gt_origen.
SELECT * FROM zdestino INTO CORRESPONDING FIELDS OF TABLE gt_destino.

"Número de líneas leidas
DESCRIBE TABLE gt_origen LINES gv_leidas.

"Ahora, insertamos/modificamos
IF p_mas = 'X'. "Masivamente
LOOP AT gt_origen.
"Leemos la tabla zdestino con el mismo campo clave,
"Si existe el registro lo modificamos y si no lo insertamos
READ TABLE gt_destino WITH KEY zclave = gt_origen-zclave.
IF sy-subrc EQ 0. "Modificamos
MOVE-CORRESPONDING gt_origen TO gt_modif.
APPEND gt_modif. CLEAR gt_modif.
ELSE. "Insertamos
MOVE-CORRESPONDING gt_origen TO gt_inser.
APPEND gt_modif. CLEAR gt_modif.
ENDIF.
ENDLOOP.

"Número de líneas insertadas/modificadas
DESCRIBE TABLE gt_inser LINES gv_inser1.
DESCRIBE TABLE gt_modif LINES gv_modif1.
"Ahora llega la modificación e inserción masiva
INSERT zdestino FROM TABLE gt_inser.
MOVE sy-dbcnt TO gv_inser2.
COMMIT WORK AND WAIT.

UPDATE zdestino FROM TABLE gt_modif.
MOVE sy-dbcnt TO gv_modif2.
COMMIT WORK AND WAIT.

ELSE. "p_1a1 = 'X'

LOOP AT gt_origen.
"Leemos la tabla zdestino con el mismo campo clave,
"Si existe el registro lo modificamos y si no lo insertamos
MOVE-CORRESPONDING gt_origen TO wa.
READ TABLE gt_destino WITH KEY zclave = wa-zclave.
IF sy-subrc EQ 0. "Modificamos
UPDATE zdestino FROM wa.
IF sy-subrc EQ 0.
ADD 1 TO gv_modif2.
ELSE.
ADD 1 TO gv_modif1.
ENDIF.
ELSE. "Insertamos
INSERT into zdestino values wa.
IF sy-subrc EQ 0.
ADD 1 TO gv_inser2.
ELSE.
ADD 1 TO gv_inser1.
ENDIF.
ENDIF.
ENDLOOP.

gv_modif1 = gv_modif1 + gv_modif2.
gv_inser1 = gv_inser1 + gv_inser2.

ENDIF.

*&---------------------------------------------------------------------*
END-OF-SELECTION.
"Mostramos resultados
"OJO! Quizá habría que restar 1 a alguna variable...

WRITE: /,
'Se insertaron......: ', gv_inser2, /,
'Se modificaron.....:', gv_modif2.

IF gv_inser1 NE gv_inser2.
WRITE: /, 'Error!, deberían haberse insertado ', gv_inser1, ' líneas.'.
ENDIF.
IF gv_modif1 NE gv_modif2.
WRITE: /, 'Error!, deberían haberse modificado, ' gv_modif1, ' líneas.'.
ENDIF.

lunes, 14 de enero de 2008

Busquedas en LOOPS anidados

En ocasiones tendremos tablas internas muy pesadas, y deberemos recorrer una dentro de otra para hacer cualquier modificación. En este ejemplo, que hecho con una carga de datos razonable, selecciono las facturas de un mes en it_tab1, luego las de un día en it_tab2, y recorro it_tab2 dentro de it_tab1.

Suponiendo que en it_tab1 haya m entradas y en it_tab2 haya n, si lo hacemos sin más el número de vueltas sería m x n. Incluso, aunque hayamos puesto la cláusula WHERE.

En estos casos, donde además ambas tablas tengan los mismos campos clave, recomiendo usar un índice interno para empezar la búsqueda en la segunda tabla, de esta forma se reducirá el número de vueltas.

En el PERFORM EXCLUIR se puede ver cómo se ha hecho. El resto es paja necesaria.

*&---------------------------------------------------------------------*
*& Report ZINDICES
*&
*&---------------------------------------------------------------------*
*&
*& Un loop dentro de otro loop, con índices
*&---------------------------------------------------------------------*

REPORT zindices.


*&---------------------------------------------------------------------*
*& TOP
*& declaración de tablas, tipos, variables, indices, tablas internas.
*&---------------------------------------------------------------------*
TABLES:
vbrk,
vbrp.

RANGES: rg_fecha FOR vbrk-fkdat.


TYPES: BEGIN OF ty_tab,
"campos de vbrk
vbeln LIKE vbrk-vbeln,
vtweg LIKE vbrk-vtweg,
spart LIKE vbrk-spart,
erdat LIKE vbrk-erdat,
fkdat LIKE vbrk-fkdat,
"Campos de vbrp
posnr LIKE vbrp-posnr,
matnr LIKE vbrp-matnr,
fkimg LIKE vbrp-fkimg,
END OF ty_tab.

DATA:
it_tab1 TYPE TABLE OF ty_tab,
it_tab2 TYPE TABLE OF ty_tab,
it_tab3 TYPE TABLE OF ty_tab,
indice LIKE sy-tabix,
vbeln1 LIKE vbrk-vbeln,
vbeln2 LIKE vbrk-vbeln,
posnr1 LIKE vbrp-posnr,
posnr2 LIKE vbrp-posnr.

************************************************************************

*&---------------------------------------------------------------------*
*& SEL
*& Pantalla de selección
*& Elementos de texto:
*& text-001: "Opciones de selección"
*& text-002: "Opciones del índice"
*& p_anno --> año a elegir
*& p_mes --> mes a elegir
*& p_dia --> día a excluir
*&---------------------------------------------------------------------*
SELECTION-SCREEN BEGIN OF BLOCK bl1 WITH FRAME TITLE text-001.

PARAMETERS:
p_anno(4) TYPE n obligatory,
p_mes(2) TYPE n obligatory,
p_dia(2) TYPE n obligatory.
SELECTION-SCREEN END OF BLOCK bl1.

SELECTION-SCREEN BEGIN OF BLOCK bl2 WITH FRAME TITLE text-002.
PARAMETERS:
p_vkorg LIKE vbrk-vkorg,
p_vtweg LIKE vbrk-vtweg.
SELECTION-SCREEN END OF BLOCK bl2.

************************************************************************



*&---------------------------------------------------------------------*
*& EVE
*& Eventos
*&---------------------------------------------------------------------*

START-OF-SELECTION.

PERFORM seleccion_t1 TABLES it_tab1. "Selección de vbrk-vbrp en it_tab1
PERFORM seleccion_t2 TABLES it_tab2. "Selección de vbrk-vbrp en it_tab2

PERFORM excluir
TABLES it_tab1 it_tab2 it_tab3. "it_tab3 = it_tab2 - it_tab1.

************************************************************************
*&---------------------------------------------------------------------*
*& F01
*& Subrutinas
*&---------------------------------------------------------------------*

*&---------------------------------------------------------------------*
*& Form seleccion_t1
*&---------------------------------------------------------------------*
* Selecciona las facturas y posiciones del año y mes elegido
*----------------------------------------------------------------------*
* -->P_TAB1 it_tab1
*----------------------------------------------------------------------*

FORM seleccion_t1 TABLES p_tab1 LIKE it_tab1.

DATA: lv_fecha_low LIKE sy-datum,
lv_fecha_high LIKE sy-datum.

CONCATENATE p_anno p_mes '01' INTO lv_fecha_low.
PERFORM buscar_ultimo_dia USING lv_fecha_low
CHANGING lv_fecha_high.

CLEAR rg_fecha.
MOVE 'I' TO rg_fecha-sign.
MOVE 'BT' TO rg_fecha-option.
MOVE lv_fecha_high TO rg_fecha-high.
MOVE lv_fecha_low TO rg_fecha-low.
APPEND rg_fecha. CLEAR rg_fecha.

READ TABLE rg_fecha INDEX 1.

SELECT
a~vbeln a~vtweg a~spart a~erdat a~fkdat
b~posnr b~matnr b~fkimg
INTO CORRESPONDING FIELDS OF TABLE p_tab1
FROM vbrk AS a
INNER JOIN vbrp AS b ON a~vbeln = b~vbeln
WHERE a~fkdat IN rg_fecha
AND a~vkorg = p_vkorg
AND a~vtweg = p_vtweg.


ENDFORM. " seleccion_t1
*&---------------------------------------------------------------------*
*& Form seleccion_t2
*&---------------------------------------------------------------------*
* Selecciona las facturas del día elegido
*----------------------------------------------------------------------*
* -->P_TAB2 it_tab2
*----------------------------------------------------------------------*
FORM seleccion_t2 TABLES p_tab2 LIKE it_tab2.

DATA: lv_fecha LIKE vbrk-fkdat.

CONCATENATE p_anno p_mes p_dia INTO lv_fecha.

SELECT
a~vbeln a~vtweg a~spart a~erdat a~fkdat
b~posnr b~matnr b~fkimg
INTO CORRESPONDING FIELDS OF TABLE p_tab2
FROM vbrk AS a
INNER JOIN vbrp AS b ON a~vbeln = b~vbeln
WHERE a~fkdat = lv_fecha
AND a~vkorg = p_vkorg
AND a~vtweg = p_vtweg.


ENDFORM. " seleccion_t2
*&---------------------------------------------------------------------*
*& Form excluir
*&---------------------------------------------------------------------*
* hacer que p_tab3 = p_tab1 - p_tab2
*----------------------------------------------------------------------*
* -->P_TAB1 it_tab1
* -->P_TAB2 it_tab2
* -->P_TAB3 it_tab3
*----------------------------------------------------------------------*
FORM excluir TABLES p_tab1 LIKE it_tab1
p_tab2 LIKE it_tab2
p_tab3 LIKE it_tab3.

*Ahora, tengo que eliminar de t_no_asis los pedidos y posiciones que ESTÉN en t_handling

*Ordeno las tablas
SORT p_tab1 BY vbeln posnr.
SORT p_tab2 BY vbeln posnr.

*Ahora, busco
CLEAR indice. ADD 1 TO indice.
LOOP AT p_tab1.
vbeln1 = p_tab1-vbeln.
posnr1 = p_tab1-posnr.
LOOP AT p_tab2 FROM indice.
ADD 1 TO indice.
vbeln2 = p_tab2-vbeln.
posnr2 = p_tab2-posnr.
IF vbeln2 > vbeln1 OR
( vbeln2 EQ vbeln1 AND posnr2 > posnr1 ).
EXIT.
ELSE.
IF vbeln2 EQ vbeln1 AND posnr2 EQ posnr1.
MOVE-CORRESPONDING p_tab1 TO p_tab3.
MOVE-CORRESPONDING p_tab2 TO p_tab3.
APPEND p_tab3. CLEAR p_tab3.
ENDIF.
ENDIF.
ENDLOOP.
ENDLOOP.

ENDFORM. " excluir
*&---------------------------------------------------------------------*
*& Form buscar_ultimo_dia
*&---------------------------------------------------------------------*
* Busca el último día de un mes.
*----------------------------------------------------------------------*
* -->P_FECHA_LOW Primer día del mes
* <--P_FECHA_HIGH Último día del mes
*----------------------------------------------------------------------*
FORM buscar_ultimo_dia USING p_fecha_low
CHANGING p_fecha_high.

CALL FUNCTION 'RP_LAST_DAY_OF_MONTHS'
EXPORTING
day_in = p_fecha_low
IMPORTING
last_day_of_month = p_fecha_high
* EXCEPTIONS
* DAY_IN_NO_DATE = 1
* OTHERS = 2
.
IF sy-subrc <> 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.


ENDFORM. " buscar_ultimo_dia

jueves, 3 de enero de 2008

Mis 8

De El blog tecnológico de Blag me llegó una lista de ocho cosas que probablemente no sé de él, un meme de estos. En fin, mis ocho cosas:

· De pequeño quería ser inventor, después escritor, y después sólo quería estudiar matemáticas. Al final soy programador así que más o menos hago las tres cosas :D
· Aprendí a leer con 3 años. A atarme los zapatos con 7, y a atármelos bien con 21. A montar en bicicleta con 15.
· Escribí una novela sólo para regalarla.
· Cuando empecé con ABAP, pensé que era una mierda de lenguaje.
· Hice el camino de Santiago, y soy ateo.
· Leí el quinto libro de Harry Potter en el viaje de vuelta (que duró 9 horas)
· Leo mucho más de lo que escribo y publico, soy adicto a leer blogs así que de publicar, poquito. De esto sí os habréis dado cuenta :) [Una de las reglas de los blogs: antes de publicar, asegúrate de que no está ya publicado Vía: Microsiervos
· Misprogramasabap.blogspot.com tenía el propósito de ser mi libreta de apuntes personal de abap, así que me sorprende que la gente entre. Lo mismo paso del punto anterior y me animo. No le paso el relevo a nadie, soy así de soso (además no sabría a quién pasárselo).

Feliz año!!