Skip to content

Project 4 — Service-Ready Purchase Request Application ​

When to start: after Lesson 18 of ABAP SQL, CDS & RAP and Project 3 — Purchase Request Workflow Manager.

Approximate duration: one working day

Project overview ​

Extend your Purchase Request Business Object into a service-ready RAP application.

Use the concepts learned in Lessons 15–18 in an independent coding challenge. You receive the contract and acceptance tests. You design, implement, activate, debug, and prove the solution.

Build on your working Project 3 objects. Add ownership and authorization, persistent draft editing, a Review Note child entity, and a service layer for Fiori Elements consumption.

Use your own student/group suffix in place of ###.


What you are building ​

An internal purchasing team needs to create requests, record review notes, and complete the approval workflow through a Fiori Elements application.

Each Purchase Request owns its Review Notes:

text
Purchase Request
└── Review Notes

Keep the workflow from Project 3:

text
N = New
S = Submitted
A = Approved
X = Rejected

N ──submit──→ S
              ├──approve──→ A
              └──reject───→ X

The application must support:

  • owner-based read and modification authorization;
  • draft editing for the root and its Review Notes;
  • an OData V4 UI service with Currency value help and Fiori Elements metadata;
  • the existing workflow actions, field control, and backend validations;
  • independent EML verification of permissions and parent/child operations.

Target architecture:

text
Fiori Elements
      ↓
OData V4 UI Service
      ↓
Service Definition
      ↓
Projection Layer
ZC_PURCHASE_REQ_###
        │ composition
        ↓
ZC_PREQ_NOTE_###
      ↓
Base RAP BO
ZR_PURCHASE_REQ_###
        │ composition
        ↓
ZR_PREQ_NOTE_###
      ↓
Managed Behavior + Authorization + Draft
      ↓
Active / Draft Persistence

The purpose of the checkpoint is to prove that the behavior contract remains in the BO. EML and OData consumers must receive the same business rules, authorization, and feature control.


Before you start — Create a package ​

Create a new package for the new Project 4 artifacts.

In ADT, select your course superpackage.

For local BTP ABAP Trial development this is typically:

text
ZLOCAL

unless your course uses another superpackage.

Then:

text
Project Explorer
→ right-click superpackage
→ New
→ ABAP Package

Suggested package:

text
ZPKG_CP4_###

Description:

text
Checkpoint 4 - Service Ready Purchase Request

Use this new package for the new Project 4 objects.

Existing Project 3 objects remain in their original package and are modified in place.


1. Challenge scope ​

Start from your working Project 3 objects:

text
ZDM_PRSTAT_###
ZDE_PRSTAT_###
ZPREQ_###
ZR_PURCHASE_REQ_###
base Behavior Definition
ZBP_R_PREQ_###
ZCL_PREQ_FLOW_###

Add the service-ready architecture:

text
ResponsibleUser ownership
CDS DCL
RAP global + instance authorization
Draft
root draft persistence
Review Note child
child draft persistence
projection views
projection DCL
projection behavior
Currency value help
metadata extensions
Service Definition
OData V4 UI Service Binding
Fiori Elements Preview
EML verification

2. Required new objects ​

Create new artifacts equivalent to:

text
ZDPREQ_###
ZPREQNOTE_###
ZR_PREQ_NOTE_###
ZDPRNOTE_###

Access Control for ZR_PURCHASE_REQ_###
Access Control for ZR_PREQ_NOTE_###

ZC_PURCHASE_REQ_###
ZC_PREQ_NOTE_###

Access Control for ZC_PURCHASE_REQ_###
Access Control for ZC_PREQ_NOTE_###

Projection Behavior Definition
Metadata Extension for ZC_PURCHASE_REQ_###
Metadata Extension for ZC_PREQ_NOTE_###

ZUI_PURCHASE_REQ_###
ZUI_PREQ_###_O4

ZCL_PREQ_VERIFY_###

3. Secure the existing root ​

Add business ownership ​

Extend:

text
ZPREQ_###

with:

abap
responsible_user : abp_creation_user;

Keep the field nullable because Project 3 rows already exist.

Expose it in:

text
ZR_PURCHASE_REQ_###

as:

text
ResponsibleUser

Change the root CDS annotation to:

abap
@AccessControl.authorizationCheck: #CHECK

Root read authorization ​

Create an Access Control for:

text
ZR_PURCHASE_REQ_###

Use:

abap
@EndUserText.label: 'Purchase Request Access'
@MappingRole: true

define role ZR_Purchase_Req_###
{
  grant select on ZR_Purchase_Req_###
    where ResponsibleUser = aspect user;
}

The rule is:

text
current user
=
ResponsibleUser

Old Project 3 rows with initial ResponsibleUser should no longer appear through the protected root CDS.

They still physically exist in:

text
ZPREQ_###

System-derived owner ​

Make:

text
ResponsibleUser

read-only to external consumers.

Add:

abap
determination setResponsibleUser on modify { create; }

Required behavior:

text
new Purchase Request
→ ResponsibleUser = current technical user

Use:

abap
cl_abap_context_info=>get_user_technical_name( )

inside provider logic.

Use local-mode EML.


Enable RAP authorization ​

Replace:

abap
authorization master ( none )

with:

abap
authorization master ( global, instance )

For this BTP Trial checkpoint, implement the global authorization contract so requested:

text
create
update
delete

are allowed.

This is a training authorization stub. Productive business-role / authorization-object design is outside the Trial setup used here.

Implement instance authorization:

text
ResponsibleUser = current user
→ update allowed
→ delete allowed

otherwise
→ update unauthorized
→ delete unauthorized

The existing:

text
submit
approve
reject

actions must delegate authorization to:

text
update

Use:

abap
authorization: update

on the business actions.


Draft-aware authorization ​

Draft introduces authorization requests that were not present in Project 3.

Your authorization implementation must correctly handle the draft lifecycle.

At minimum, include the update-style authorization result for:

text
Edit
Activate

according to the current owner policy.

Do not add authorization control to the draft determine action:

text
Prepare

For newly created instances with no persistent before-image, create authorization applies.


4. Enable draft for the root ​

Add draft to the base BDEF ​

Add:

abap
with draft;

Declare:

abap
draft table zdpreq_###

Use ADT Quick Fix:

text
Ctrl+1

to generate:

text
ZDPREQ_###

Do not manually design the draft administration fields.


Add total ETag ​

The root already has:

text
LocalLastChangedAt
LastChangedAt

Keep:

abap
etag master LocalLastChangedAt

and add:

abap
total etag LastChangedAt

to the lock master.

The two timestamps have different concurrency roles.


Add draft actions ​

Declare:

abap
draft action Edit;
draft action Activate optimized;
draft action Discard;
draft action Resume;

Add:

abap
draft determine action Prepare
{
  validation validateRequest;
  validation validateNeededBy;
  validation validateCost;
}

Reuse the Project 3 validations.

Do not create draft-specific duplicate validation methods.


5. Add the Review Note child ​

Create child persistence ​

Create:

text
ZPREQNOTE_###

One row represents one Review Note belonging to one Purchase Request.

Use fields:

FieldSuggested type
clientclient type
note_uuidSYSUUID_X16
request_uuidSYSUUID_X16
note_noNUMC(4)
note_typeCHAR(20)
note_textCHAR(255)
local_last_changed_atABP_LOCINST_LASTCHANGE_TSTMPL

Technical key:

text
client + note_uuid

Create the child CDS entity ​

Create:

text
ZR_PREQ_NOTE_###

Use:

text
define view entity

not:

text
define root view entity

Required relationship:

abap
association to parent ZR_Purchase_Req_### as _PurchaseRequest
  on $projection.RequestUuid = _PurchaseRequest.Uuid

Expose:

text
_PurchaseRequest
NoteUuid
RequestUuid
NoteNo
NoteType
NoteText
LocalLastChangedAt

Annotate:

text
LocalLastChangedAt

with the local-instance-last-changed semantic annotation.


Add composition to the root ​

Extend:

text
ZR_PURCHASE_REQ_###

with:

abap
composition [0..*] of ZR_Preq_Note_### as _Notes

Expose:

text
_Notes

in the root select list.

The BO hierarchy is now:

text
PurchaseRequest
└── Notes

Protect child reads ​

Create Access Control for:

text
ZR_PREQ_NOTE_###

The child read policy must follow parent ownership.

Use the parent association to enforce:

text
_PurchaseRequest.ResponsibleUser
=
current user

The child will later be exposed as its own OData entity set, so it needs explicit read protection.


6. Extend managed behavior for the child ​

Enable create through the composition ​

In the root behavior add:

abap
association _Notes
{
  create ( authorization: update );
  with draft;
}

The child is created:

text
through the parent association

not with a normal child create;.


Add child behavior ​

Add a second behavior entity for:

text
ZR_PREQ_NOTE_###

Requirements:

text
persistent table ZPREQNOTE_###
draft table ZDPRNOTE_###
authorization dependent by _PurchaseRequest
lock dependent by _PurchaseRequest
etag master LocalLastChangedAt

update
delete

NoteUuid
→ managed numbering + read-only

RequestUuid
→ read-only

LocalLastChangedAt
→ read-only

NoteNo
→ mandatory:create + readonly:update

_PurchaseRequest
→ with draft

complete mapping

Generate:

text
ZDPRNOTE_###

with ADT Quick Fix.


Base BDEF target ​

Your resulting behavior must contain the equivalent of:

abap
managed implementation in class zbp_r_preq_### unique;
strict ( 2 );
with draft;

define behavior for ZR_Purchase_Req_### alias PurchaseRequest
persistent table zpreq_###
draft table zdpreq_###
lock master
total etag LastChangedAt
authorization master ( global, instance )
etag master LocalLastChangedAt
{
  create;
  update;
  delete;

  ...

  determination setInitialStatus on modify { create; }
  determination setResponsibleUser on modify { create; }

  validation validateRequest on save
    { create; field RequesterName, ItemDescription; }

  validation validateNeededBy on save
    { create; field NeededBy; }

  validation validateCost on save
    { create; field Quantity, UnitPrice, CurrencyCode; }

  action ( features : instance, authorization: update )
    submit result [1] $self;

  action ( features : instance, authorization: update )
    approve result [1] $self;

  action ( features : instance, authorization: update )
    reject result [1] $self;

  draft action Edit;
  draft action Activate optimized;
  draft action Discard;
  draft action Resume;

  draft determine action Prepare
  {
    validation validateRequest;
    validation validateNeededBy;
    validation validateCost;
  }

  association _Notes
  {
    create ( authorization: update );
    with draft;
  }

  ...
}

define behavior for ZR_Preq_Note_### alias Note
persistent table zpreqnote_###
draft table zdprnote_###
authorization dependent by _PurchaseRequest
lock dependent by _PurchaseRequest
etag master LocalLastChangedAt
{
  update;
  delete;

  field ( numbering : managed, readonly ) NoteUuid;

  field ( readonly )
    RequestUuid,
    LocalLastChangedAt;

  field ( mandatory:create, readonly:update )
    NoteNo;

  association _PurchaseRequest { with draft; }

  mapping for zpreqnote_###
  {
    NoteUuid           = note_uuid;
    RequestUuid        = request_uuid;
    NoteNo             = note_no;
    NoteType           = note_type;
    NoteText           = note_text;
    LocalLastChangedAt = local_last_changed_at;
  }
}

Keep the complete Project 3 root mapping and field-control declarations.


7. Build the projection layer ​

Create root projection ​

Create:

text
ZC_PURCHASE_REQ_###

Use:

abap
define root view entity ZC_Purchase_Req_###
  provider contract transactional_query
  as projection on ZR_Purchase_Req_###

Requirements:

text
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@Search.searchable: true
all important root fields projected
_Notes projected

Make:

text
RequestNo
RequesterName

default search elements.


Add Currency value help ​

Add to projection field:

text
CurrencyCode

a current:

text
@Consumption.valueHelpDefinition

using:

text
I_CurrencyStdVH

and element:

text
Currency

Use:

text
useForValidation: true

Create child projection ​

Create:

text
ZC_PREQ_NOTE_###

as a projection on:

text
ZR_PREQ_NOTE_###

Project all child fields.

Redirect the parent association:

abap
_PurchaseRequest
  : redirected to parent ZC_Purchase_Req_###

In the root projection redirect:

abap
_Notes
  : redirected to composition child ZC_Preq_Note_###

Protect projection reads ​

Create Access Control for:

text
ZC_PURCHASE_REQ_###

using a condition equivalent to:

abap
where inheriting conditions from entity ZR_Purchase_Req_###;

Create Access Control for:

text
ZC_PREQ_NOTE_###

using:

abap
where inheriting conditions from entity ZR_Preq_Note_###;

Both projection DCL objects must use:

text
@MappingRole: true

Do not assume CDS access control automatically propagates through projection layers.


8. Expose projection behavior ​

Create the projection BDEF ​

Create a projection Behavior Definition for:

text
ZC_PURCHASE_REQ_###

Use:

abap
projection;
strict ( 2 );
use draft;

Root behavior must expose:

text
use etag
create
update
delete
Edit
Activate
Discard
Resume
Prepare
submit
approve
reject
_Notes create with draft

Child behavior must expose:

text
use etag
update
delete
_PurchaseRequest with draft

Equivalent structure:

abap
projection;
strict ( 2 );
use draft;

define behavior for ZC_Purchase_Req_### alias PurchaseRequest
use etag
{
  use create;
  use update;
  use delete;

  use action Edit;
  use action Activate;
  use action Discard;
  use action Resume;
  use action Prepare;

  use action submit;
  use action approve;
  use action reject;

  use association _Notes { create; with draft; }
}

define behavior for ZC_Preq_Note_### alias Note
use etag
{
  use update;
  use delete;

  use association _PurchaseRequest { with draft; }
}

9. Add UI metadata ​

Root metadata extension ​

Create a Metadata Extension for:

text
ZC_PURCHASE_REQ_###

Use:

text
@Metadata.layer: #CORE

Required UI design:

text
header title
→ RequestNo

list columns
→ RequestNo
→ RequesterName
→ ItemDescription
→ Quantity
→ UnitPrice
→ CurrencyCode
→ NeededBy
→ Status
→ ResponsibleUser

selection fields
→ Status
→ NeededBy
→ CurrencyCode

identification fields
→ all major business fields

Add an object-page facet for:

text
Review Notes

using:

text
#LINEITEM_REFERENCE
targetElement = _Notes

Expose workflow actions in the UI ​

Add Fiori action-button annotations using:

text
#FOR_ACTION

for:

text
submit
approve
reject

Use either or both:

text
@UI.lineItem
@UI.identification

depending on where you want the buttons.

The dataAction names must match the projected RAP action names.

The buttons must still respect the existing instance feature-control matrix at runtime.


Child metadata extension ​

Create Metadata Extension for:

text
ZC_PREQ_NOTE_###

Add child line-item fields:

text
NoteNo
NoteType
NoteText

The Review Notes facet must render a child collection rather than a flattened root field.


10. Expose the business service ​

Create Service Definition ​

Create:

text
ZUI_PURCHASE_REQ_###

Expose:

abap
ZC_Purchase_Req_### as PurchaseRequests;
ZC_Preq_Note_###    as ReviewNotes;
I_CurrencyStdVH     as Currencies;

The service definition answers:

text
WHAT is exposed

Create OData V4 UI binding ​

Create:

text
ZUI_PREQ_###_O4

Binding:

text
OData V4 - UI

Use:

text
ZUI_PURCHASE_REQ_###

as the Service Definition.

Activate and publish the local service.

Launch the Fiori Elements Preview.

The Service Binding answers:

text
HOW the service is exposed

11. Verify the BO through EML ​

Create ZCL_PREQ_VERIFY_### ​

Create an external EML verification class.

Implement:

text
IF_OO_ADT_CLASSRUN

Use normal EML only.

The class must demonstrate:

text
GET PERMISSIONS
create-by-association
read-by-association
MAPPED
LINK
COMMIT ENTITIES

Authorization and feature verification ​

For an owned active Purchase Request, request:

text
instance authorization
instance features

At minimum inspect:

text
update authorization
delete authorization
submit feature
approve feature
reject feature
UnitPrice feature
CurrencyCode feature

For the owned instance:

text
update authorization = allowed
delete authorization = allowed

The verification class must show that:

text
authorization
and
feature control

are separate permission dimensions.


Create a Review Note with EML ​

For an owned active Purchase Request, create one child through:

text
CREATE BY \_Notes

Use:

text
%target
child %cid

Do not supply:

text
NoteUuid
RequestUuid

Inspect:

text
MAPPED
FAILED
REPORTED

Commit.


Read Review Notes by association ​

Use:

text
READ ENTITIES
ENTITY PurchaseRequest
BY \_Notes

Return:

text
RESULT
LINK

The runtime evidence must prove the parent-to-child relationship.


12. Required test cases ​

Complete the tests in order. For each test, record the setup, expected result, and actual result. Capture relevant Fiori Elements screens, persistence previews, or EML output so the result can be reviewed.

Use 99004001 and 99004002 only for this project. Before repeating the acceptance run, check for existing active rows and drafts with those Request Numbers. Resume the existing test state or remove only your test instances through the BO and discard their drafts through the UI. Do not reset active or draft persistence with direct SQL.

Test 1 — Read security ​

Prove DCL before creating new data ​

Project 3 left active rows:

text
99000001
99000002

They were created before ResponsibleUser existed.

After root DCL activation, those old rows should normally be hidden from:

text
ZR_PURCHASE_REQ_###
ZC_PURCHASE_REQ_###
Fiori Elements service

while still visible physically in:

text
ZPREQ_###

Use:

text
F8
Alt+F8

to prove the difference.

If you already removed those old rows, use another pre-ownership row if one exists; do not create an unauthorized row with direct SQL just for the test.


Test 2 — Draft create and activation ​

Create request 99004001 ​

In the Fiori Elements preview choose:

text
Create

Enter valid data:

text
RequestNo        = 99004001
RequesterName    = Casey Walker
RequesterEmail   = casey@example.com
ItemDescription  = Mobile test devices
Quantity         = 3
UnitPrice         = 850.00
CurrencyCode     = EUR
NeededBy         = current date + 21
Notes            = Approval branch

Do not enter:

text
Uuid
Status
ResponsibleUser
admin fields

Expected provider defaults:

text
Status = N
ResponsibleUser = current user

Prove new-draft persistence ​

Before Save:

text
ZPREQ_###
→ no active 99004001 row

ZDPREQ_###
→ draft 99004001 exists

This must be visible through Table Data Preview.


Add Review Notes before Save ​

While the new Purchase Request is still a draft, add at least two Review Notes:

text
0001 / TECHNICAL / Device specifications reviewed
0002 / BUDGET    / Budget owner confirmation required

Before Save:

text
ZPREQNOTE_###
→ no active notes

ZDPRNOTE_###
→ child drafts exist

Save the composition tree ​

Choose:

text
Save

Expected:

text
Prepare runs root validations
Activate succeeds
root becomes active
both Review Notes become active
root draft removed
child drafts removed

One Save activates the composition tree.


Test 3 — Validation ​

Prove invalid draft cannot activate ​

Edit:

text
99004001

Set:

text
NeededBy = yesterday

Choose:

text
Save

Required:

text
validation message shown
active row remains unchanged
invalid draft remains available for correction

Correct the date and Save successfully.


Test 4 — Value help and metadata ​

Prove the service metadata ​

In Fiori Elements verify:

text
RequestNo shown as object title
expected list columns visible
Status / NeededBy / Currency filters available
CurrencyCode has value help
Review Notes facet visible
NoteNo / NoteType / NoteText shown in child table

Choose a currency through the value help at least once.


Test 5 — Approval workflow through OData ​

Submit 99004001 ​

While Status is:

text
N

verify:

text
submit available
approve unavailable
reject unavailable
purchase fields editable

Execute:

text
submit

Expected:

text
N → S

Verify submitted feature control ​

After Submit:

text
submit unavailable
approve available
reject available

ItemDescription
Quantity
UnitPrice
CurrencyCode
NeededBy
→ read-only

Attempt to edit one controlled purchase field through the UI.

The service must respect the backend feature-control result.


Approve 99004001 ​

Execute:

text
approve

Expected:

text
S → A

Final state:

text
all workflow actions unavailable
purchase fields read-only
Review Notes still visible

Test 6 — Rejection workflow ​

Create request 99004002 ​

Create a second valid Purchase Request in the UI.

Use:

text
RequestNo = 99004002

Save it.

Drive:

text
N
→ submit
→ S
→ reject
→ X

Add at least one Review Note before or during the draft editing lifecycle.


Test 7 — Discard a child draft ​

Prove child draft discard ​

Edit:

text
99004002

Add a new Review Note but do not Save.

Verify:

text
active child table
→ note not present

child draft table
→ note present

Choose:

text
Cancel / Discard

Required:

text
draft note removed
active child data unchanged

Test 8 — Root total ETag ​

Prove child change updates the root BO ​

Capture root:

text
LastChangedAt

for an active Purchase Request.

Edit and save only:

text
Review Note data

Read root:

text
LastChangedAt

again.

Required:

text
root total ETag changed

This proves the total ETag represents the complete compositional BO.


13. Final runtime state ​

Required active root states:

text
99004001 → A
99004002 → X

Required child state:

text
99004001
→ at least two active Review Notes

99004002
→ at least one active Review Note

Required security/service state:

text
root DCL active
child DCL active
projection DCL active
owner-based modification authorization active
Draft active
OData V4 UI service published
Currency value help working
composition navigation working
feature control working
backend validations working

14. Technical constraints ​

Your solution must keep business rules in the RAP provider.

Required:

text
no direct SQL business writes from consumers
no IN LOCAL MODE in external consumers
no direct consumer Status update
no consumer-supplied UUIDs
no user-entered ResponsibleUser
no duplicated validation logic in UI code
no duplicated ownership logic in child handler
no separate child lock master
no normal child create operation

Use:

text
DCL
RAP authorization
determinations
validations
actions
feature control
draft
composition behavior

for the responsibilities they were designed to handle.


15. Deliverables ​

Submit the modified and new objects, using your own suffix:

text
modified ZPREQ_###
modified ZR_PURCHASE_REQ_###
modified base BDEF
modified ZBP_R_PREQ_###

ZDPREQ_###

ZPREQNOTE_###
ZR_PREQ_NOTE_###
ZDPRNOTE_###

root + child Access Controls

ZC_PURCHASE_REQ_###
ZC_PREQ_NOTE_###

projection Access Controls
projection BDEF

root Metadata Extension
child Metadata Extension

ZUI_PURCHASE_REQ_###
ZUI_PREQ_###_O4

ZCL_PREQ_VERIFY_###

Include:

  • the actual names of the modified Project 3 objects and all new Project 4 objects;
  • an architecture diagram and explanation covering the root, child, projections, service, authorization, and active/draft persistence;
  • the setup, expected result, and actual result for all eight required tests;
  • EML output showing GET PERMISSIONS, child MAPPED, and read-by-association RESULT and LINK, together with any FAILED and REPORTED responses;
  • evidence of the final approved and rejected requests and their active Review Notes;
  • an explanation of how DCL, RAP authorization, feature control, and validations each contribute to the application.

If no pre-ownership row exists for the read-security test, record that limitation. Do not report an unexecuted check as passed.


16. Submission checklist ​

Ownership and authorization ​

Draft and composition behavior ​

Projection and service layer ​

Runtime evidence ​


17. Certification focus ​

This checkpoint is intentionally close to practical system work.

You must navigate and connect multiple repository objects correctly:

text
table
root CDS
child CDS
DCL
base BDEF
Behavior Pool
draft tables
projection CDS
projection BDEF
metadata extensions
Service Definition
Service Binding
EML verification class

The important skill is not memorizing one code block.

It is being able to diagnose which layer owns a problem:

text
wrong rows visible
→ DCL

wrong user can modify
→ RAP authorization

invalid data saves
→ validation / Prepare

Edit/Save flow missing
→ Draft

child cannot be created
→ composition behavior

child navigation broken
→ CDS/projection redirection

action missing in UI
→ projection behavior / UI action metadata

value help missing
→ consumption annotation / service exposure

service endpoint missing
→ Service Binding

This checkpoint emphasizes building, activating, testing, and repairing the RAP stack. It is not intended to simulate the entire certification; later lessons add events, extensibility, and quality tooling.


18. Next steps ​

The project is complete when you can demonstrate the persisted approval and rejection paths, the root and child draft lifecycle, owner-based access, and the required EML results.

Continue with Lesson 19 — RAP Business Events →

Revisit the Lesson 18 companion for composition guidance or the EML Cheat Sheet for permissions, associations, and transactions.

Browse all projects →