martes, 20 de noviembre de 2007

Leer checkboxes en un listado

Este problema se me planteó durante un borrado masivo de documentos preliminares.

Para ello, el propio SAP propone un report en su nota 971193. (Estaría bien poner los pdf en el blog, pero como son de SAP y tendrán copyright, mejor no).

El report que propone, básicamente, lista todos los documentos preliminares (parked documents), y al pulsar el botón de SAVE (para el que proponen un diskete, en el status DELE) se borran. Pero, ¿qué pasa si no quiero borrarlos todos?

Lo más fácil es poner, como encabezado de cada línea, un checkbox; y si está marcado se borra el documento correspondiente a esta línea. Para ello, hay que tener en cuenta lo siguiente:

1. El estatus DELE (al menos cuando empecé a trabajar con el report) tenía en el botón de “marcar todos” el código de función &ALL, que también estaba asociado a la tecla de función F2. Esto provocaba que, si se intentaba seleccionar sólo una línea, saltase el código de F2 que tenía asociada la función “Seleccionar todos (&ALL)”, y no funcionase. Para corregirlo, a la tecla de función “F2” le asocié el nuevo código “&ONE”, y para el código de función “&ALL” asocié la tecla de función F5, como puede apreciarse en las imágenes.
Además, agregué una función de refresco de imagen.




2. En el report propuesto, hice las siguientes modificaciones:
2.1. En la tabla TBKPF, añadí un campito para que recogiese/mostrase el valor del checkbox asociado:

data: begin of tbkpf occurs 5,
tratar type c.
include structure vbkpf.
data: end of tbkpf.

2.2. Tanto la selección de datos como la impresión por pantalla del listado, los metí en dos performs. Además, cambié la selección porque los bucles SELECT...ENDSELECT no me gustan. No tiene sentido mostrar el código de dichos performs (al final de esta entrada lo incluiré entero), sin embargo sus nombres son:

*Meto el select en un perform
perform seleccionar_vbkpf.

*Meto la impresión de la pantalla en otro perfrom
perform escribir_listado.

2.3. Al pulsar el botón de borrado (sy-ucomm = ‘DELE’), le pedí que revisara los documentos marcados con instrucciones READ LINE en la subrutina revisar_marcados; previamente, había guardado el número de líneas que se muestran por pantalla en la variable global gv_lineas declarada así:
data: gv_lineas like sy-linno.

Esta variable se establece después de hacer el listado con sentencias write (en concreto, en el perform “escribir_listado”, haciendo:
gv_lineas = sy-linno.
2.4. También al pulsar el botón de borrado, se llamaba al form “fbv0_dele.”. modifiqué una sóla línea de este form para que tuviera en cuenta que hiciera el loop a la tabla TBKPF, con la condición de que el campo “tratar” estuviera marcado, lo cual se revisaba como he dicho en el form revisar_marcados.

loop at tbkpf
"Añado condición
where tratar eq 'X'.
(...)
endloop.

3. Al escribir/leer el contenido de la tabla en el listado, hay que tener en cuenta que es preferible meter el checkbox en una variable global (gv_check) de tipo carácter, que escribir el campo de la tabla en sí (tbkpf-tratar), ya que en caso contrario cada vez que seleccionásemos una línea habría que modificar la tabla y reescribir el listado, con los inconvenientes que ello conlleva: modificar una tabla (aunque sea interna) es un proceso costoso, y un listado no se puede reescribir más de un número determinado de veces (según la parametrización del sistema donde hice el report, 20).
4. El botón “BACK” provocaba, durante el listado, que mostrase la pantalla anterior no por evento sino por apariencia; es decir, en lugar de mostrarse la pantalla de selección, aparecía la modificación anterior hecha en la pantalla. Por ejemplo, si después de ejecutar la pantalla de selección, durante el listado, primero seleccionábamos todos los check-boxes, luego los deseleccionábamos, luego marcábamos uno, y dábamos para atrás, resultaría que se mostrarían todos desmarcados (primera pulsación) luego todos marcados (segunda pulsación) y luego la pantalla de selección. ¡Un engorro! Por eso, deshabilité el botón “DELE” en el PF-STATUS:
start-of-selection.
set pf-status 'DELE' excluding 'BACK'.
Y lo volvía a incluirlo al pulsar el botón ‘DELE’.
end-of-selection.

at user-command.
(…)
case sy-ucomm.
when 'DELE'.
perform revisar_marcados. "Comprueba los marcados
set pf-status 'DELE'. "Habilita el botón “Back”


Estas han sido las modificaciones, ahora el report enterito:

*&---------------------------------------------------------------------*
*& Report ZDELEPARKED *&
*&---------------------------------------------------------------------*
*&
*& Borrado masivo de documentos preliminares
*& Modificado por Alberto García de Haro
*& a partir del report propuesto por SAP en la nota971193
*& 20 de noviembre de 2007
*& http://misprogramasabap.blogspot.com
*&---------------------------------------------------------------------*

report zdeleparked.

tables: vbkpf, t020.

data: mode type c value 'N'.
data: begin of tbkpf occurs 5,
tratar type c.
include structure vbkpf.
data: end of tbkpf.
data: begin of it020 occurs 0.
include structure t020.
data: end of it020.
data: begin of bdcdata occurs 0.
include structure bdcdata.
data: end of bdcdata.
data: begin of messtab occurs 0.
include structure bdcmsgcoll.
data: end of messtab.
data: char(20) type c,
count type i,
xdele.
data: wa_tbkpf like line of tbkpf,
gv_check type c,
gv_veces type i, "Veces que se ha mostrado el listado
gv_belnr like vbkpf-belnr,
gv_lineas like sy-linno,
gv_index like sy-tabix.

data: tmarcados like tbkpf occurs 10.
********pantalla de selección
select-options:
bukrs for vbkpf-bukrs memory id buk,
belnr for vbkpf-belnr,
gjahr for vbkpf-gjahr memory id gjr
default sy-datum(4).


selection-screen skip 1.
selection-screen begin of block 2 with frame title text-001.
select-options:
budat for vbkpf-budat,
bldat for vbkpf-bldat,
blart for vbkpf-blart,
xblnr for vbkpf-xblnr,
bktxt for vbkpf-bktxt,
usnam for vbkpf-usnam. "default sy-uname.
selection-screen end of block 2.

selection-screen begin of block 3 with frame title text-002.
select-options:
xwffr for vbkpf-xwffr,
xprfg for vbkpf-xprfg,
xfrge for vbkpf-xfrge.
selection-screen end of block 3.

**************eventos
initialization.
clear: gv_lineas, gv_veces.

at selection-screen.

start-of-selection.
set pf-status 'DELE' excluding 'BACK'.

call function 'AUTHORITY_CHECK_TCODE'
exporting
tcode = 'FBV0'
exceptions
ok = 1
others = 4.
if sy-subrc = 4.
message id sy-msgid type sy-msgty number sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
exit.
endif.

*Meto el select en un perform
perform seleccionar_vbkpf.

*Meto la impresión de la pantalla en otro perfrom
perform escribir_listado.

end-of-selection.


at user-command.
clear gv_belnr.
clear: char.
if count > 0.

case sy-ucomm.
when 'DELE'.
perform revisar_marcados. "Comprueba los marcados
set pf-status 'DELE'. "Habilita el botón “Back”
if xdele = space.
call function 'POPUP_TO_CONFIRM_LOSS_OF_DATA'
exporting
titel = text-003
textline1 = text-004
textline2 = space
importing
answer = char(1).
if char(1) = 'J'.
perform fbv0_dele.
xdele = 'X'.
else.
exit.
endif.
else.
message i899(f5) with text-007.
endif.
when 'BACK'.

exit.

when '&ONE'. "Marca/desmarca uno.
perform marcar_uno.

when '&ALL'. "Marcar todos
perform marcar_todos.

when '&SAL'.
perform desmarcar_todos.
* when 'BACK'.

when 'FRSH'. "Refrescar
if gv_veces lt 20. "Sólo se permiten 20 anidamientos
perform seleccionar_vbkpf.
perform escribir_listado.
else.
message e368(00) with text-e01.
endif.
when others.
exit.
endcase.
endif.

*&---------------------------------------------------------------------*
*& Form FBV0_DELE
*&---------------------------------------------------------------------* *
*& Find out, how document was parked (classically or by ENJOY)
*&---------------------------------------------------------------------* *

form fbv0_dele.
refresh messtab.
loop at tbkpf
"Añado condición
where tratar eq 'X'.
clear t020.
select single * from t020 where tcode = tbkpf-tcode.
if t020-gener = space. "parked by classical transaction
perform fbv0_dele1 using tbkpf-gjahr
tbkpf-belnr
tbkpf-bukrs.
else. "parked by ENJOY
perform fbv0_dele2 using tbkpf-gjahr
tbkpf-belnr
tbkpf-bukrs.
endif.
endloop.
check sy-subrc eq 0.
loop at messtab.
write: / messtab.
endloop.
endform. "FBV0_DELE

*&---------------------------------------------------------------------*
*& Form FBV0_DELE1
*&---------------------------------------------------------------------*
*& Classically parked documents
*&---------------------------------------------------------------------*

form fbv0_dele1 using i_gjahr like bkpf-gjahr
i_belnr like bkpf-belnr
i_bukrs like bkpf-bukrs.

data: xdate(10) type c.
refresh bdcdata.
perform bdc_dynpro using 'SAPMF05V'
'0100'.
perform bdc_field using 'BDC_OKCODE'
'/00'.
perform bdc_field using 'BDC_CURSOR'
'RF05V-GJAHR'.
perform bdc_field using 'RF05V-BUKRS'
i_bukrs.
perform bdc_field using 'RF05V-BELNR'
i_belnr.
perform bdc_field using 'RF05V-GJAHR'
i_gjahr.
perform bdc_dynpro using 'SAPLF040'
'0700'.
perform bdc_field using 'BDC_CURSOR'
'BKPF-XBLNR'.
perform bdc_field using 'BDC_OKCODE'
'BL'.
perform bdc_dynpro using 'SAPLSPO1'
'0200'.
perform bdc_field using 'BDC_CURSOR'
'SPOP-OPTION1'. "Button YES
perform bdc_field using 'BDC_OKCODE'
'ENTER'.

call transaction 'FBV0' using bdcdata
mode mode
update 'S'.

call function 'MESSAGE_TEXT_BUILD'
exporting
msgid = sy-msgid
msgnr = sy-msgno
msgv1 = sy-msgv1
msgv2 = sy-msgv2
msgv3 = sy-msgv3
msgv4 = sy-msgv4
importing
message_text_output = messtab
exceptions
others = 4.
append messtab.
endform. "FBV0_DELE2

*&---------------------------------------------------------------------*
*& Form fbv0_dele2
*&---------------------------------------------------------------------*
*& Documents parked by ENJOY
*&---------------------------------------------------------------------*

form fbv0_dele2 using i_gjahr
i_belnr
i_bukrs.
data: xdate(10) type c.
refresh bdcdata.
perform bdc_dynpro using 'SAPMF05V'
'0100'.
perform bdc_field using 'BDC_OKCODE'
'/00'.
perform bdc_field using 'BDC_CURSOR'
'RF05V-GJAHR'.
perform bdc_field using 'RF05V-BUKRS'
i_bukrs.
perform bdc_field using 'RF05V-BELNR'
i_belnr.
perform bdc_field using 'RF05V-GJAHR'
i_gjahr.

if t020-koart = 'D'.
perform bdc_dynpro using 'SAPMF05A'
'1200'.
perform bdc_field using 'BDC_CURSOR'
'INVFO-ACCNT'.
elseif t020-koart = 'K'.
perform bdc_dynpro using 'SAPMF05A'
'1100'.
perform bdc_field using 'BDC_CURSOR'
'INVFO-ACCNT'.
elseif t020-koart = 'S'.
perform bdc_dynpro using 'SAPMF05A'
'1001'.
perform bdc_field using 'BDC_CURSOR'
'ACGL_HEAD-BLDAT'.
endif.

perform bdc_field using 'BDC_OKCODE'
'=9-PD'.

perform bdc_dynpro using 'SAPLSPO1'
'0200'.
perform bdc_field using 'BDC_CURSOR'
'SPOP-OPTION1'. "Button YES
perform bdc_field using 'BDC_OKCODE'
'ENTER'.

call transaction 'FBV0' using bdcdata
mode mode
update 'S'.

call function 'MESSAGE_TEXT_BUILD'
exporting
msgid = sy-msgid
msgnr = sy-msgno
msgv1 = sy-msgv1
msgv2 = sy-msgv2
msgv3 = sy-msgv3
msgv4 = sy-msgv4
importing
message_text_output = messtab
exceptions
others = 4.

append messtab.

endform. " fbv0_dele2

*&---------------------------------------------------------------------*
*& Form BDC_DYNPRO
*&---------------------------------------------------------------------*
form bdc_dynpro using program dynpro.
clear bdcdata.
bdcdata-program = program.
bdcdata-dynpro = dynpro.
bdcdata-dynbegin = 'X'.
append bdcdata.
endform. "BDC_DYNPRO

*&---------------------------------------------------------------------*
*& Form BDC_FIELD
*&---------------------------------------------------------------------*
form bdc_field using fnam fval.
clear bdcdata.
bdcdata-fnam = fnam.
bdcdata-fval = fval.
append bdcdata.
endform. "BDC_FIELD
*&---------------------------------------------------------------------*
*& Form seleccionar_vbkpf
*&---------------------------------------------------------------------*
* Selección de datos de la tabla vbkpf
*----------------------------------------------------------------------*

form seleccionar_vbkpf .
*Añado into corresponding fields
*Y cambio este select...endselect por un select
*into corresponding fields of table y hacer count = sy-dbcnt

* select * from vbkpf into corresponding fields of tbkpf
* where ausbk in bukrs
* and belnr in belnr
* and gjahr in gjahr
* and budat in budat
* and bldat in bldat
* and blart in blart
* and bktxt in bktxt
* and xblnr in xblnr
* and usnam in usnam
* and bstat eq 'V'
* and xwffr in xwffr
* and xfrge in xfrge
* and xprfg in xprfg.
* append tbkpf.
* count = count + 1.
* endselect.
* sort tbkpf by ausbk belnr gjahr.

"La tabla, bien limpita
clear tbkpf. refresh tbkpf.

select * from vbkpf
into corresponding fields of table tbkpf
where ausbk in bukrs
and belnr in belnr
and gjahr in gjahr
and budat in budat
and bldat in bldat
and blart in blart
and bktxt in bktxt
and xblnr in xblnr
and usnam in usnam
and bstat eq 'V'
and xwffr in xwffr
and xfrge in xfrge
and xprfg in xprfg
order by ausbk belnr gjahr.
clear count.
count = sy-dbcnt.

endform. " seleccionar_vbkpf
*&---------------------------------------------------------------------*
*& Form escribir_listado
*&---------------------------------------------------------------------*
* Muestra por pantalla el contenido de TBKPF, salvo el campo
* TBKPF-TRATAR; en su lugar escribe un CHECKBOX
*----------------------------------------------------------------------*

form escribir_listado .

if count = 0.
write: /, text-005.
else.
write: /, text-006, count, /.
uline.
*Una cabecera

write:
* at 3(5) 'AUSBK',
* at 9(5) 'BUKRS',
* at 15(10) 'BELNR',
* at 26(7) 'GJAHR'.
at 3(5) 'Soc.',
at 9(5) 'Soc.',
at 15(10) 'Doc.',
at 26(9) 'Ejercicio'.
uline.

format hotspot on.
loop at tbkpf.
gv_check = space.
write:
/,
gv_check as checkbox, "No escribe de tabla...
at 3(5) tbkpf-ausbk,
at 9(5) tbkpf-bukrs,
at 15(10) tbkpf-belnr,
at 26(7) tbkpf-gjahr.
hide: tbkpf-tratar, tbkpf-belnr.
endloop.
gv_lineas = sy-linno.
endif.
add 1 to gv_veces.


endform. " escribir_listado
*&---------------------------------------------------------------------*
*& Form revisar_marcados
*&---------------------------------------------------------------------*
* Al activar un checkbox, no se genera evento (no hay fcode)
* en este perform se revisan las casillas que están marcadas
* y se actualiza la tabla tbkpf usando la tabla local lt_tbkpf.
*----------------------------------------------------------------------*

form revisar_marcados.

data: wa_linea(255) type c,
lt_tbkpf like tbkpf occurs 5 with header line,
wa_tbkpf like line of tbkpf,
lv_belnr(10) type c,
lv_tratar type c,
lv_long type i,
lv_times type i.

clear:
wa_linea, lv_belnr, lv_tratar, lv_long, lv_times, lt_tbkpf.
refresh lt_tbkpf.

do gv_lineas times.

read line sy-index line value into wa_linea.
"Comprobamos que sea una línea con "datos"
"Y también que se tenga que tratar dicha línea
if wa_linea+2(4) eq 'AENA' and
wa_linea(1) eq 'X'.
"Comprobamos el CHECKBOX
lv_tratar = wa_linea(1).
"COMPROBAMOS EL PEDIDO
lv_belnr = wa_linea+14(10).
lv_long = strlen( lv_belnr ).
lv_times = 10 - lv_long.
do lv_times times.
concatenate '0' lv_belnr into lv_belnr.
enddo.
read table tbkpf with key belnr = lv_belnr.
"Modificamos la tabla si es necesario
if sy-subrc eq 0. "Ha leído la tabla
tbkpf-tratar = lv_tratar.
move-corresponding tbkpf to lt_tbkpf.
append lt_tbkpf. clear lt_tbkpf.
endif.
"La tabla se ha modificado
endif.
enddo.


* "Ahora modifico la tabla
loop at lt_tbkpf.
loop at tbkpf where belnr = lt_tbkpf-belnr.
tbkpf-tratar = 'X'.
modify tbkpf.
endloop.
endloop.

endform. " revisar_marcados
*&---------------------------------------------------------------------*
*& Form marcar_todos
*&---------------------------------------------------------------------*
* Marca todos los checkboxes del listado
*----------------------------------------------------------------------*

form marcar_todos .

do gv_lineas times.

read line sy-index field value gv_check.
gv_check = 'X'.
modify line sy-index field value gv_check.

enddo.

endform. " marcar_todos
*&---------------------------------------------------------------------*
*& Form DESMARCAR_TODOS
*&---------------------------------------------------------------------*
* Desmarca todos los checkboxes del listado
*----------------------------------------------------------------------*
form desmarcar_todos .

do gv_lineas times.

read line sy-index field value gv_check.
gv_check = ' '.
modify line sy-index field value gv_check.

enddo.


endform. " DESMARCAR_TODOS

*&---------------------------------------------------------------------*
*& Form marcar_uno
*&---------------------------------------------------------------------*
* Marca un solo elemento,
* - cuando se selecciona una línea (ya que FORMAT HOTSPOT = ON)
* - cuando se pulsa el botón &ONE (actualmente deshabilitado)
*----------------------------------------------------------------------*

form marcar_uno .

data: lv_lilli like sy-lilli.
lv_lilli = sy-lilli.

read line sy-lilli field value gv_check.
if gv_check eq 'X'.
gv_check = space.
else.
gv_check = 'X'.
endif.

modify line lv_lilli field value gv_check.

endform. " marcar_uno

No hay comentarios: