← Volver a tutoriales

RAP desde cero · Lección 4 de 6

Validaciones en RAP: failed, reported y mensajes en el campo correcto

Añade comportamiento a la biblioteca (crear, modificar, borrar) y valida los datos al guardar, con mensajes de error que Fiori muestra junto al campo.

Also available in English →

Hasta ahora la app de la biblioteca solo consulta. En esta lección le damos comportamiento: crear, modificar y borrar libros. Y como no queremos libros con 0 páginas ni valoraciones de 9 estrellas, añadimos validaciones.

1. Behavior Definition de la entidad raíz

Clic derecho en ZR_LibBook → New Behavior Definition, tipo Managed. Ajusta el resultado para que quede así:

managed implementation in class zbp_r_libbook unique;
strict ( 2 );

define behavior for ZR_LibBook alias Book
persistent table zlib_book
lock master
authorization master ( instance )
etag master LocalLastChangedAt
{
  create;
  update;
  delete;

  field ( readonly, numbering : managed ) BookUuid;
  field ( readonly ) LocalCreatedBy, LocalCreatedAt, LocalLastChangedBy,
                     LocalLastChangedAt, LastChangedAt;
  field ( mandatory ) BookId, Title, AuthorId;

  validation validatePages  on save { create; field Pages; }
  validation validateRating on save { create; field Rating; }

  mapping for zlib_book corresponding
  {
    BookUuid           = book_uuid;
    BookId             = book_id;
    AuthorId           = author_id;
    CurrencyCode       = currency_code;
    LocalCreatedBy     = local_created_by;
    LocalCreatedAt     = local_created_at;
    LocalLastChangedBy = local_last_changed_by;
    LocalLastChangedAt = local_last_changed_at;
    LastChangedAt      = last_changed_at;
  }
}

Línea a línea, lo importante:

Línea Qué hace
managed El framework se encarga de leer, guardar y bloquear; tú solo escribes la lógica de negocio
strict ( 2 ) Activa las comprobaciones más estrictas de sintaxis y buenas prácticas
persistent table zlib_book Dónde guarda los datos el framework
lock master Bloqueo pesimista mientras alguien edita
etag master LocalLastChangedAt Bloqueo optimista: si otro usuario modificó el libro mientras tanto, tu cambio se rechaza
numbering : managed RAP genera el UUID al crear
mapping … corresponding Relaciona campos CDS y columnas de la tabla. Los que se llaman igual (Title/title) no hace falta listarlos
validation … on save { create; field Pages; } Se ejecuta al guardar, si el libro es nuevo o si cambió Pages

Pon el cursor en zbp_r_libbook y usa el quick fix (Ctrl+1) para que ADT genere la clase de implementación con los métodos vacíos.

2. Behavior Definition de la proyección

La proyección decide qué operaciones del objeto de negocio ofrece esta app. Clic derecho en ZC_LibBook → New Behavior Definition:

projection;
strict ( 2 );

define behavior for ZC_LibBook alias Book
use etag
{
  use create;
  use update;
  use delete;
}

Si quisieras una app que solo modifica, bastaría con quitar use create y use delete. El objeto de negocio sigue siendo el mismo.

3. Las validaciones

En la clase zbp_r_libbook, pestaña Local Types, el handler queda así:

CLASS lhc_book DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.
    METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
      IMPORTING keys REQUEST requested_authorizations FOR Book RESULT result.

    METHODS validatePages FOR VALIDATE ON SAVE
      IMPORTING keys FOR Book~validatePages.

    METHODS validateRating FOR VALIDATE ON SAVE
      IMPORTING keys FOR Book~validateRating.
ENDCLASS.

CLASS lhc_book IMPLEMENTATION.

  METHOD get_instance_authorizations.
    " Sin restricciones por ahora: lo vemos en la lección de seguridad
  ENDMETHOD.

  METHOD validatePages.
    READ ENTITIES OF ZR_LibBook IN LOCAL MODE
      ENTITY Book
        FIELDS ( Pages )
        WITH CORRESPONDING #( keys )
      RESULT DATA(books).

    LOOP AT books INTO DATA(book).
      " 1. Limpiar los mensajes anteriores de esta validación
      APPEND VALUE #( %tky        = book-%tky
                      %state_area = 'VALIDATE_PAGES' ) TO reported-book.

      IF book-Pages <= 0.
        " 2. Marcar la instancia como fallida: no se guardará
        APPEND VALUE #( %tky = book-%tky ) TO failed-book.

        " 3. Explicar por qué, y en qué campo
        APPEND VALUE #( %tky           = book-%tky
                        %state_area    = 'VALIDATE_PAGES'
                        %msg           = new_message_with_text(
                                           severity = if_abap_behv_message=>severity-error
                                           text     = 'El número de páginas debe ser mayor que cero' )
                        %element-Pages = if_abap_behv=>mk-on ) TO reported-book.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD validateRating.
    READ ENTITIES OF ZR_LibBook IN LOCAL MODE
      ENTITY Book
        FIELDS ( Rating )
        WITH CORRESPONDING #( keys )
      RESULT DATA(books).

    LOOP AT books INTO DATA(book).
      APPEND VALUE #( %tky        = book-%tky
                      %state_area = 'VALIDATE_RATING' ) TO reported-book.

      IF book-Rating > 5.
        APPEND VALUE #( %tky = book-%tky ) TO failed-book.
        APPEND VALUE #( %tky            = book-%tky
                        %state_area     = 'VALIDATE_RATING'
                        %msg            = new_message_with_text(
                                            severity = if_abap_behv_message=>severity-error
                                            text     = |La valoración va de 0 a 5; recibí { book-Rating }| )
                        %element-Rating = if_abap_behv=>mk-on ) TO reported-book.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

ENDCLASS.

El patrón, paso a paso

  1. READ ENTITIES … IN LOCAL MODE: una validación solo recibe las claves (keys) de los libros afectados, así que primero hay que leer los datos. IN LOCAL MODE se salta las comprobaciones de autorización y de feature control, porque la lectura la hace el propio objeto de negocio. WITH CORRESPONDING #( keys ) es el CORRESPONDING de la lección 1.
  2. %tky: la clave «transaccional» de la instancia. Úsala siempre para referirte a un libro en failed y reported.
  3. failed: dice qué instancias fallaron. Si una validación deja algo aquí, el guardado completo se rechaza.
  4. reported: dice por qué. Lleva el mensaje (%msg) y el campo afectado (%element-Pages), que Fiori resalta en rojo.
  5. %state_area: agrupa los mensajes de una misma validación. Al empezar se añade una entrada vacía con esa área para borrar los mensajes de la ejecución anterior: así, cuando el usuario corrige el dato, el error desaparece. Cobra todo su sentido con el modo draft, pero conviene acostumbrarse desde el principio.

Regla práctica: cada APPEND … TO failed-… va acompañado de un APPEND … TO reported-…. Un fallo sin mensaje deja al usuario sin saber qué corregir; un mensaje sin fallo es solo un aviso y el guardado sigue adelante.

Mensajes con clase de mensajes

new_message_with_text es cómodo para aprender. En un proyecto real, los textos suelen ir en una clase de mensajes para poder traducirlos:

%msg = new_message( id       = 'ZLIB_MESSAGES'
                    number   = '001'
                    v1       = book-BookId
                    severity = if_abap_behv_message=>severity-error )

4. Pruébalo

Activa todo y vuelve a la vista previa del Service Binding (lección 3). Ahora aparecen los botones Create y Delete.

  1. Crea un libro con 0 páginas y pulsa Save: el guardado se rechaza y el campo Pages aparece en rojo con tu mensaje.
  2. Pon 350 páginas y una valoración de 9: ahora el error está en Rating.
  3. Corrige la valoración a 4 y guarda: el libro se crea y los mensajes desaparecen.

Resumen

  • La Behavior Definition declara qué se puede hacer (create, update, delete) y qué reglas se aplican (validation).
  • La proyección decide qué parte de ese comportamiento ofrece cada app.
  • Una validación lee los datos, marca las instancias erróneas en failed y explica el motivo en reported, en el campo correcto.

Con esto termina el bloque básico de la serie: tienes un objeto de negocio RAP completo, con datos, servicio, interfaz y reglas. El siguiente nivel entra en la lógica de negocio: numeración propia, determinaciones y acciones.