Opciones de Procesamiento de Actions en RAP: Elegir el Patrón Correcto

Kategorie
RAP
Veröffentlicht
Autor
Johannes

Las Actions son el corazón de la lógica de negocio en RAP. Pero, ¿qué tipo de Action elegir en cada caso? Este artículo compara todas las opciones de procesamiento y muestra con ejemplos de reserva de vuelos cómo se implementa cada tipo.

Visión General de los Tipos de Action

RAP ofrece diferentes tipos de Action para distintos casos de uso:

Tipo de ActionVinculaciónPropósitoEjemplo
Instance ActionVinculada a entidadOperación sobre instancia seleccionadaCancelar reserva
Static ActionSin vinculación a instanciaOperaciones masivas, nueva creaciónLiberar todas las reservas pendientes
Factory ActionVinculada a entidadCrear nueva instancia desde existenteCopiar reserva
Action con ParámetroInstance o StaticEntradas de usuario antes de ejecuciónCancelación con motivo
Action con ResultadoInstance o StaticDevolución de datosRecalcular precio

Instance Action: Operación sobre una Entidad

Las Instance Actions trabajan sobre una o más instancias seleccionadas. El tipo de Action más común.

Behavior Definition

managed implementation in class zbp_i_flightbooking unique;
strict ( 2 );
define behavior for ZI_FlightBooking alias FlightBooking
persistent table zflight_book
lock master
authorization master ( instance )
{
create;
update;
delete;
// Instance Action sin parámetro
action confirmBooking result [1] $self;
// Instance Action con devolución de estado
action cancelBooking result [1] $self;
// Instance Action sin resultado
action markAsNoShow;
}

Implementación

CLASS lhc_flightbooking DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS confirmBooking FOR MODIFY
IMPORTING keys FOR ACTION FlightBooking~confirmBooking
RESULT result.
METHODS cancelBooking FOR MODIFY
IMPORTING keys FOR ACTION FlightBooking~cancelBooking
RESULT result.
METHODS markAsNoShow FOR MODIFY
IMPORTING keys FOR ACTION FlightBooking~markAsNoShow.
ENDCLASS.
CLASS lhc_flightbooking IMPLEMENTATION.
METHOD confirmBooking.
" Confirmar reservas
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status )
WITH VALUE #( FOR key IN keys (
%tky = key-%tky
booking_status = 'C' " Confirmed
) ).
" Leer datos actualizados
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
" Devolver resultado
result = VALUE #( FOR booking IN bookings (
%tky = booking-%tky
%param = booking
) ).
" Mensaje de éxito
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = |{ lines( bookings ) } reserva(s) confirmada(s)| )
) TO reported-flightbooking.
ENDMETHOD.
METHOD cancelBooking.
" Primero verificar si la cancelación está permitida
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
FIELDS ( booking_status flight_date )
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
LOOP AT bookings INTO DATA(booking).
" Solo se pueden cancelar reservas abiertas o confirmadas
IF booking-booking_status <> 'O' AND booking-booking_status <> 'C'.
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Reserva en estado { booking-booking_status
} no puede cancelarse| )
) TO reported-flightbooking.
APPEND VALUE #( %tky = booking-%tky ) TO failed-flightbooking.
CONTINUE.
ENDIF.
" Ejecutar cancelación
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status cancellation_date )
WITH VALUE #( (
%tky = booking-%tky
booking_status = 'X' " Cancelled
cancellation_date = cl_abap_context_info=>get_system_date( )
) ).
ENDLOOP.
" Leer y devolver resultado
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(updated_bookings).
result = VALUE #( FOR bk IN updated_bookings (
%tky = bk-%tky
%param = bk
) ).
ENDMETHOD.
METHOD markAsNoShow.
" Action simple sin devolución de resultado
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status no_show_date )
WITH VALUE #( FOR key IN keys (
%tky = key-%tky
booking_status = 'N' " No-Show
no_show_date = cl_abap_context_info=>get_system_date( )
) ).
" Solo mensaje, sin result
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = 'Marcado como No-Show' )
) TO reported-flightbooking.
ENDMETHOD.
ENDCLASS.

Cardinalidad del Result

SintaxisSignificadoUso
result [1] $selfExactamente una instancia de la misma entidadEstándar para cambios de estado
result [0..1] $selfOpcionalmente una instanciaSi el resultado no está garantizado
result [0..*] $selfCualquier cantidad de instanciasOperaciones masivas
result [1] EntityNameUna entidad diferenteNavegación a entidad relacionada
(sin result)Sin devoluciónOperaciones fire-and-forget

Static Action: Operaciones Masivas y Nueva Creación

Las Static Actions no están vinculadas a una instancia. Se usan típicamente para operaciones globales.

Behavior Definition

define behavior for ZI_FlightBooking alias FlightBooking
{
// Static Action para operación masiva
static action releaseAllPending;
// Static Action con resultado
static action createQuickBooking result [1] $self;
// Static Action con Feature Control
static action ( features: global ) sendDailyReport;
// Para Global Feature Control
static features;
}

Implementación

CLASS lhc_flightbooking IMPLEMENTATION.
METHOD releaseAllPending.
" Encontrar todas las reservas con estado 'Pending'
SELECT * FROM zflight_book
WHERE booking_status = 'P'
INTO TABLE @DATA(pending_bookings).
IF pending_bookings IS INITIAL.
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-information
text = 'No se encontraron reservas pendientes' )
) TO reported-flightbooking.
RETURN.
ENDIF.
" Establecer todas a 'Confirmed'
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status )
WITH VALUE #( FOR booking IN pending_bookings (
booking_id = booking-booking_id
booking_status = 'C' " Confirmed
) ).
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = |{ lines( pending_bookings ) } reservas liberadas| )
) TO reported-flightbooking.
ENDMETHOD.
METHOD createQuickBooking.
" Crear reserva rápida con valores predeterminados
DATA(today) = cl_abap_context_info=>get_system_date( ).
DATA(user) = cl_abap_context_info=>get_user_technical_name( ).
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
CREATE FIELDS (
flight_date customer_id flight_id booking_status
flight_price currency_code created_by created_at
)
WITH VALUE #( (
%cid = 'QUICK_BOOKING'
flight_date = today + 14 " En 2 semanas
customer_id = get_default_customer( user )
flight_id = get_next_available_flight( today + 14 )
booking_status = 'P' " Pending
flight_price = 299
currency_code = 'EUR'
created_by = user
created_at = cl_abap_context_info=>get_system_time( )
) )
MAPPED DATA(mapped).
" Leer reserva creada
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH VALUE #( ( %tky = mapped-flightbooking[ 1 ]-%tky ) )
RESULT DATA(created_bookings).
" Devolver resultado con %cid_ref
result = VALUE #( (
%cid_ref = 'QUICK_BOOKING'
%param = created_bookings[ 1 ]
) ).
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = 'Reserva rápida creada' )
) TO reported-flightbooking.
ENDMETHOD.
METHOD get_global_features.
" Verificar si el usuario tiene autorización para el reporte
DATA(has_report_auth) = check_report_authorization( ).
result = VALUE #(
%action-sendDailyReport = COND #(
WHEN has_report_auth = abap_true
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
).
ENDMETHOD.
ENDCLASS.

Factory Action: Nueva Instancia desde Existente

Las Factory Actions crean una nueva instancia basada en una existente. Ideal para escenarios de copia y plantillas.

Behavior Definition

define behavior for ZI_FlightBooking alias FlightBooking
{
// Factory Action crea copia
factory action copyBooking [1];
// Factory Action con parámetros
factory action rebookToDate parameter ZA_RebookParams [1];
}

Implementación

CLASS lhc_flightbooking IMPLEMENTATION.
METHOD copyBooking.
" Leer reservas originales
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
DATA: lt_mapped TYPE RESPONSE FOR MAPPED EARLY zi_flightbooking.
LOOP AT bookings INTO DATA(booking).
DATA(lv_cid) = |COPY_{ sy-tabix }|.
" Crear copia con nuevo estado
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
CREATE FIELDS (
flight_date customer_id flight_id
flight_price currency_code booking_status
created_by created_at
)
WITH VALUE #( (
%cid = lv_cid
flight_date = booking-flight_date
customer_id = booking-customer_id
flight_id = booking-flight_id
flight_price = booking-flight_price
currency_code = booking-currency_code
booking_status = 'P' " Nueva reserva como Pending
created_by = cl_abap_context_info=>get_user_technical_name( )
created_at = cl_abap_context_info=>get_system_time( )
) )
MAPPED DATA(mapped_single).
" Agregar mapped
APPEND VALUE #(
%cid_ref = keys[ sy-tabix ]-%cid_ref
%key = mapped_single-flightbooking[ 1 ]-%key
) TO mapped-flightbooking.
ENDLOOP.
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = |{ lines( bookings ) } reserva(s) copiada(s)| )
) TO reported-flightbooking.
ENDMETHOD.
METHOD rebookToDate.
" Leer reservas originales
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>).
READ TABLE bookings INTO DATA(booking)
WITH KEY booking_id = <key>-booking_id.
" Nueva fecha del parámetro
DATA(new_date) = <key>-%param-NewFlightDate.
" Validación
IF new_date < cl_abap_context_info=>get_system_date( ).
APPEND VALUE #(
%tky = <key>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'La nueva fecha debe estar en el futuro' )
) TO reported-flightbooking.
APPEND VALUE #( %tky = <key>-%tky ) TO failed-flightbooking.
CONTINUE.
ENDIF.
" Crear nueva reserva con nueva fecha
DATA(lv_cid) = |REBOOK_{ sy-tabix }|.
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
CREATE FIELDS (
flight_date customer_id flight_id
flight_price currency_code booking_status
original_booking_id
)
WITH VALUE #( (
%cid = lv_cid
flight_date = new_date
customer_id = booking-customer_id
flight_id = booking-flight_id
flight_price = booking-flight_price
currency_code = booking-currency_code
booking_status = 'P' " Pending
original_booking_id = booking-booking_id " Referencia a original
) )
MAPPED DATA(mapped_single).
" Establecer reserva original en 'Rebooked'
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status )
WITH VALUE #( (
%tky = <key>-%tky
booking_status = 'R' " Rebooked
) ).
APPEND VALUE #(
%cid_ref = <key>-%cid_ref
%key = mapped_single-flightbooking[ 1 ]-%key
) TO mapped-flightbooking.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Action con Parámetro de Resultado

Las Actions pueden devolver resultados estructurados que van más allá de $self.

Abstract Entity para Resultado

@EndUserText.label: 'Resultado de Cálculo de Precio'
define abstract entity ZA_PriceCalculation
{
BookingId : abap.numc(10);
NetPrice : abap.curr(15,2);
TaxAmount : abap.curr(15,2);
TotalPrice : abap.curr(15,2);
Currency : abap.cuky;
DiscountPct : abap.dec(5,2);
DiscountText : abap.string(100);
}

Behavior Definition

define behavior for ZI_FlightBooking alias FlightBooking
{
// Action con tipo de resultado propio
action recalculatePrice result [1] ZA_PriceCalculation;
// Action que devuelve otra entidad
action createInvoice result [1] ZI_Invoice;
}

Implementación

METHOD recalculatePrice.
" Leer datos de reserva
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
FIELDS ( booking_id flight_price currency_code customer_id )
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
LOOP AT bookings INTO DATA(booking).
" Determinar descuento del cliente
DATA(discount_pct) = get_customer_discount( booking-customer_id ).
DATA(net_price) = booking-flight_price * ( 1 - discount_pct / 100 ).
DATA(tax_amount) = net_price * '0.19'.
DATA(total_price) = net_price + tax_amount.
" Actualizar precio en reserva
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( flight_price )
WITH VALUE #( (
%tky = booking-%tky
flight_price = total_price
) ).
" Llenar estructura de resultado
APPEND VALUE #(
%tky = booking-%tky
%param = VALUE za_pricecalculation(
BookingId = booking-booking_id
NetPrice = net_price
TaxAmount = tax_amount
TotalPrice = total_price
Currency = booking-currency_code
DiscountPct = discount_pct
DiscountText = COND #(
WHEN discount_pct > 0
THEN |Descuento cliente habitual: { discount_pct }%|
ELSE 'Sin descuento' )
)
) TO result.
ENDLOOP.
ENDMETHOD.

Manejo de Errores en Actions

Las Actions robustas deben manejar y reportar errores correctamente.

Patrón: Validación Antes de Ejecución

METHOD processBooking.
DATA: lt_failed TYPE TABLE FOR FAILED EARLY zi_flightbooking,
lt_reported TYPE TABLE FOR REPORTED EARLY zi_flightbooking.
" Leer reservas
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
LOOP AT bookings INTO DATA(booking).
" === FASE DE VALIDACIÓN ===
" Error 1: Verificar estado
IF booking-booking_status = 'X'.
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Las reservas canceladas no pueden procesarse' )
) TO lt_reported.
APPEND VALUE #( %tky = booking-%tky ) TO lt_failed.
CONTINUE.
ENDIF.
" Error 2: Verificar fecha
IF booking-flight_date < cl_abap_context_info=>get_system_date( ).
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'La fecha de vuelo está en el pasado' )
%element-flight_date = if_abap_behv=>mk-on
) TO lt_reported.
APPEND VALUE #( %tky = booking-%tky ) TO lt_failed.
CONTINUE.
ENDIF.
" Advertencia: Precio alto
IF booking-flight_price > 2000.
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-warning
text = |Precio superior a 2.000 { booking-currency_code }| )
%element-flight_price = if_abap_behv=>mk-on
) TO lt_reported.
" ¡NO se añade entrada failed en Warning!
ENDIF.
" === FASE DE EJECUCIÓN ===
TRY.
" Ejecutar lógica de negocio
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status processed_at )
WITH VALUE #( (
%tky = booking-%tky
booking_status = 'D' " Done
processed_at = cl_abap_context_info=>get_system_time( )
) ).
CATCH cx_root INTO DATA(lx_error).
" Error inesperado
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Error del sistema: { lx_error->get_text( ) }| )
) TO lt_reported.
APPEND VALUE #( %tky = booking-%tky ) TO lt_failed.
ENDTRY.
ENDLOOP.
" Pasar errores y mensajes
failed-flightbooking = lt_failed.
reported-flightbooking = lt_reported.
" Resultado para reservas exitosas
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(updated_bookings).
result = VALUE #( FOR bk IN updated_bookings
WHERE ( booking_status = 'D' ) (
%tky = bk-%tky
%param = bk
) ).
ENDMETHOD.

Matriz de Decisión: ¿Qué Action Cuándo?

RequisitoTipo de ActionEjemplo
Cambio de estado de una reservaInstance ActionconfirmBooking, cancelBooking
Liberar todas las reservas abiertasStatic ActionreleaseAllPending
Copiar reservaFactory ActioncopyBooking
Cancelación con motivoInstance Action + ParámetrocancelWithReason
Nueva reserva sin plantillaStatic Action + ResultcreateQuickBooking
Cambio de reserva a nueva fechaFactory Action + ParámetrorebookToDate
Recalcular precio y mostrarInstance Action + Result EntityrecalculatePrice
Crear factura desde reservaInstance Action + otra EntitycreateInvoice

¿Cuándo Instance vs. Static?

┌─────────────────────────────────────────────────────────────────┐
│ ¿La Action necesita │
│ contexto de instancia? │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ ▼ ▼ │
│ [ SÍ ] [ NO ] │
│ │ │ │
│ ┌──────────┴──────────┐ │ │
│ │ │ ▼ │
│ ▼ ▼ STATIC ACTION │
│ ¿Crea nueva ¿Modifica │
│ instancia? existente? │
│ │ │ │
│ ▼ ▼ │
│ FACTORY INSTANCE │
│ ACTION ACTION │
└─────────────────────────────────────────────────────────────────┘

¿Cuándo con o sin Parámetro?

EscenarioCon ParámetroSin Parámetro
Entrada de usuario requeridaDiálogo popup
Confirmación sin detallesEjecución directa
Información adicional opcionalParámetro con defaults
Action con opciones de selecciónParámetro con Value Help

¿Cuándo usar Result?

EscenarioTipo de Result
UI debe actualizarseresult [1] $self
Fire-and-ForgetSin Result
Mostrar resultado de cálculoresult [1] ZA_ResultEntity
Operación masiva con resultados individualesresult [0..*] $self
Entidad relacionada creadaresult [1] OtherEntity

Mejores Prácticas

1. Convención de Nombres Consistente

" Verbos para Actions
action confirmBooking ...
action cancelBooking ...
action recalculatePrice ...
" Evitar sustantivos
action confirmation ... " No está claro qué pasa
action cancellation ...

2. Siempre Indicar Result para Actualización de UI

" La UI se actualiza después de la Action
action confirmBooking result [1] $self;
" La UI permanece en estado antiguo
action confirmBooking; " Sin result

3. Detectar Errores Temprano

METHOD myAction.
" Validación PRIMERO, antes de cualquier cambio
READ ENTITIES ...
LOOP AT ...
IF NOT valid( ).
" Reportar error y CONTINUE
CONTINUE.
ENDIF.
ENDLOOP.
" Solo después de validación: realizar cambios
MODIFY ENTITIES ...
ENDMETHOD.

4. Usar IN LOCAL MODE

" Omite verificación de autorización dentro de la Action
READ ENTITIES OF zi_flightbooking IN LOCAL MODE ...
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE ...

5. Combinar con Feature Control

" Behavior Definition
action ( features: instance ) cancelBooking result [1] $self;
determination setFeatures on modify { field booking_status; }
" Implementación: cancelBooking solo activo para reservas abiertas

Resumen

Tipo de ActionCuándo UsarKeys PresentesCrea Instancia
InstanceOperación sobre selecciónNo
StaticOperación globalNoOpcional
FactoryCopiar, plantillas

La elección correcta del tipo de Action marca la diferencia entre una interfaz de usuario intuitiva y una confusa. Usa Instance Actions para operaciones individuales, Static Actions para acciones globales y Factory Actions para crear nuevas instancias desde existentes.

Artículos relacionados: RAP Actions y Functions para fundamentos, Popup Actions para Actions con diálogo, RAP Mensajes para manejo de errores y Feature Control para activación dinámica.