~/abap.dev — code, crafted in SAP
florin @ s4hana : ~/abap/posts/todo-list-100 $ cat README.md

RAP To-Do List

A full-stack ABAP RAP application — CDS views, behavior definitions, draft handling, and an OData V4 Fiori Elements UI

Overview

A managed RAP business object for a simple to-do list. The application covers the full vertical slice of a RAP stack: a database table, two CDS layers, a managed behavior definition with draft support, an OData V4 service binding, and a Fiori Elements List Report.

Data model

The root table ZTODO_100 stores tasks with the following fields:

FieldTypeNote
TASK_UUIDRAW(16)Primary key, UUID
TITLEStringMandatory
DESCRIPTIONStringOptional
STATUSDomain ZTODO_STATUSOPEN / IN_PROGRESS / DONE
PRIORITYInteger
DUE_DATEDate
COMPLETED_ATTimestamp

Administrative fields (CREATED_BY, CREATED_AT, LOCAL_LAST_CHANGED_AT, LAST_CHANGED_AT) are managed by the framework via @Semantics annotations.

CDS layer

Two views follow the standard RAP naming convention:

ZR_TODO_100 — the root view entity. Maps directly from the database table and exposes all fields including the administrative ones annotated with @Semantics so the framework fills them automatically.

ZC_TODO_100 — the consumption (projection) view. Declares provider contract transactional_query and adds a value help on Status pointing to ZI_TODO_STATUS_VH_100. Metadata extensions are enabled via @Metadata.allowExtensions: true, allowing the Fiori UI layout to be defined in a separate .ddlx file without touching the view itself.

Behavior definition

The behavior is managed — the framework handles the persistence layer automatically.

managed implementation in class ZBP_TODO_100 unique;
strict ( 2 );
with draft;

Draft

Draft is enabled with a dedicated draft table ZTODO_100_D. This gives the Fiori UI full draft support: users can start editing a record, navigate away, and return to their unfinished changes without losing data.

Early numbering

UUIDs are assigned in the behavior handler before the record reaches the database:

METHOD earlynumbering_create.
  LOOP AT entities INTO DATA(entity).
    IF entity-TaskUUID IS INITIAL.
      DATA(lv_uuid) = cl_system_uuid=>create_uuid_x16_static( ).
      APPEND VALUE #( %cid = entity-%cid  TaskUUID = lv_uuid ) TO mapped-zr_todo_100.
    ENDIF.
  ENDLOOP.
ENDMETHOD.

Using early numbering rather than late numbering means the key is known client-side immediately after creation, which is required for draft.

Determination — setDefaultStatus

When a task is created without a status, the determination sets it to OPEN:

METHOD setDefaultStatus.
  LOOP AT todos INTO DATA(todo).
    IF todo-Status IS INITIAL.
      APPEND VALUE #( %tky = todo-%tky  Status = 'OPEN' ) TO update_todos.
    ENDIF.
  ENDLOOP.
  MODIFY ENTITIES OF zr_todo_100 IN LOCAL MODE
    ENTITY zr_todo_100 UPDATE FIELDS ( Status ) WITH update_todos.
ENDMETHOD.

IN LOCAL MODE bypasses authorization checks for the internal update — correct since this is framework-driven logic, not a user action.

Validation — validateTitle

Fires on save for create and update, rejects blank titles with a field-level message:

%msg = new_message_with_text(
         severity = if_abap_behv_message=>severity-error
         text     = 'Title must not be empty' )

Field-level messages (%element-Title = if_abap_behv=>mk-on) highlight the exact input in the Fiori UI.

Service exposure

The OData V4 service is exposed via ZUI_TODO_100_O4:

  • Service definition (SRVD) — binds ZC_TODO_100 to a service
  • Service binding (SRVB) — publishes the service as OData V4, consumed directly by Fiori Elements as a List Report page