Skip to content

Project 3 — Purchase Request Workflow Manager ​

When to start: after ABAP Foundations and Lesson 14 of ABAP SQL, CDS & RAP.

Approximate duration: one working day

Project overview ​

Build a complete managed RAP Business Object for an internal purchase-request workflow.

Use the concepts learned through Lesson 14 in an independent coding challenge.

You receive the contract and acceptance tests. You design, implement, activate, debug, and prove the solution.

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


What you are building ​

You are building a Purchase Request Business Object (BO).

The BO represents one internal request to purchase an item or service. Each request has its own identity, requester details, item information, quantity, price, required date, status, notes, and RAP administrative data.

The Business Object is more than a database row. It defines the approved transactional API for working with a purchase request:

text
Purchase Request BO
├─ data
│  ├─ requester
│  ├─ item
│  ├─ quantity
│  ├─ price / currency
│  ├─ needed-by date
│  └─ status
│
└─ behavior
   ├─ create / update / delete
   ├─ managed UUID generation
   ├─ default Status
   ├─ validations
   ├─ messages
   ├─ submit
   ├─ approve
   ├─ reject
   └─ dynamic field/action control

Its workflow is:

text
New
 ↓ submit
Submitted
 ├─ approve → Approved
 └─ reject  → Rejected

Consumers work with the BO through:

text
EML

rather than writing directly to ZPREQ_###.

The purpose of the checkpoint is to prove that the behavior contract lives in the BO, so any valid consumer gets the same rules, workflow, validations, and feature control.


Before you start — Create a package ​

Create a new ABAP package for this checkpoint before creating any project objects.

In ADT, select the package that should be the superpackage for the checkpoint.

For local development in SAP BTP ABAP Trial, this is typically:

text
ZLOCAL

unless your course setup already uses another course superpackage.

Then:

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

Use a package name such as:

text
ZPKG_CP3_###

Description:

text
Checkpoint 3 - Purchase Request

Create it as a development subpackage of the selected superpackage.

Create all Checkpoint 3 objects inside this package:

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

Keeping the project in its own package makes the checkpoint easier to navigate, review, activate, and submit.


1. Challenge scope ​

Build:

text
custom persistence
root CDS entity
managed BDEF
Behavior Pool
external EML consumer
determination
validations + messages
actions
static field control
dynamic instance feature control
transactional-buffer tests

Target architecture:

text
ZCL_PREQ_FLOW_###
        ↓
       EML
        ↓
ZR_PURCHASE_REQ_###
        ↓
Managed RAP Behavior
        ↓
ZPREQ_###

2. Workflow ​

Status values:

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

Allowed transitions:

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

Status must not be directly writable by normal consumers.

Workflow changes happen through RAP actions.


3. Required objects ​

Create:

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

4. Persistence contract ​

Create:

text
ZDM_PRSTAT_###

as CHAR(1) with:

ValueMeaning
NNew
SSubmitted
AApproved
XRejected

Create:

text
ZDE_PRSTAT_###

using that Domain.

Create:

text
ZPREQ_###

as a client-dependent transparent application table with restricted data maintenance and no extensibility.

Technical key:

text
client + uuid

Required fields:

FieldRequirement
clientkey, client type
uuidkey, SYSUUID_X16
request_noNUMC(8)
requester_nameCHAR(60)
requester_emailCHAR(100)
item_descriptionCHAR(100)
quantityINT4
unit_priceCURR(10,2)
currency_codeCUKY
needed_byDATS
statusZDE_PRSTAT_###
notesCHAR(255)

Add the five RAP administrative fields:

text
local_created_by
local_created_at
local_last_changed_by
local_last_changed_at
last_changed_at

Apply:

text
unit_price → currency_code

amount/currency semantics.


5. Root CDS contract ​

Create:

text
ZR_PURCHASE_REQ_###

with:

abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Purchase Request'

define root view entity ZR_Purchase_Req_###
  as select from zpreq_### as Request
{
  ...
}

Expose:

text
Uuid
RequestNo
RequesterName
RequesterEmail
ItemDescription
Quantity
UnitPrice
CurrencyCode
NeededBy
Status
Notes
LocalCreatedBy
LocalCreatedAt
LocalLastChangedBy
LocalLastChangedAt
LastChangedAt

Requirements:

text
Uuid = CDS key
client not exposed
CamelCase aliases
UnitPrice → CurrencyCode semantics
correct semantic annotations on all five admin fields

Activate and verify with:

text
F8

6. Managed behavior contract ​

Use:

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

define behavior for ZR_Purchase_Req_### alias PurchaseRequest
persistent table zpreq_###
lock master
authorization master ( none )
etag master LocalLastChangedAt
{
  ...
}

Training scope

authorization master ( none ) is intentional here. Authorization is not part of this checkpoint.

Enable:

abap
create;
update;
delete;

Required field behavior:

text
Uuid
→ numbering : managed
→ readonly

Status
→ readonly

RequestNo
→ mandatory:create
→ readonly:update

admin fields
→ readonly

ItemDescription
Quantity
UnitPrice
CurrencyCode
NeededBy
→ features : instance

Map every BO field explicitly to ZPREQ_###.


7. Determination and validation contract ​

Initial Status determination ​

Declare:

abap
determination setInitialStatus on modify { create; }

Behavior:

text
initial Status
→ N

Implementation must use provider-side local-mode EML.

Do not use direct SQL.

Request validation ​

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

Rules:

text
RequesterName required
ItemDescription required

Date validation ​

abap
validation validateNeededBy on save
  { create; field NeededBy; }

Rule:

text
NeededBy >= current system date

Use the ABAP Cloud API:

abap
cl_abap_context_info=>get_system_date( )

Cost validation ​

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

Rules:

text
Quantity > 0
UnitPrice > 0
CurrencyCode required

For invalid instances:

text
FAILED
→ identifies failed instance

REPORTED
→ error message

%element
→ targets relevant field

8. Action contract ​

Declare:

abap
action ( features : instance ) submit
  result [1] $self;

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

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

Implement in:

text
ZBP_R_PREQ_###

Use:

text
MODIFY ENTITIES ... IN LOCAL MODE
READ ENTITIES ... IN LOCAL MODE

No direct SQL.

Required action effects:

text
submit
→ Status = S

approve
→ Status = A

reject
→ Status = X

Every successful action must:

text
update Status in the transactional buffer
read the changed instance
fill result [1] $self

The legal source state is enforced by instance feature control in the next section.

For normal external EML, RAP evaluates the feature control before executing the action handler. A disabled action is rejected by the runtime rather than entering the action implementation.


9. Feature-control contract ​

Implement this matrix:

StatusSubmitApproveRejectPurchase fields
NEnabledDisabledDisabledEditable
SDisabledEnabledEnabledRead-only
ADisabledDisabledDisabledRead-only
XDisabledDisabledDisabledRead-only

Purchase fields:

text
ItemDescription
Quantity
UnitPrice
CurrencyCode
NeededBy

Use operation feature constants for actions:

text
if_abap_behv=>fc-o-enabled
if_abap_behv=>fc-o-disabled

Use field feature constants for fields:

text
if_abap_behv=>fc-f-unrestricted
if_abap_behv=>fc-f-read_only

Implement:

text
get_instance_features

from the current Status.


10. EML consumer contract ​

Create:

text
ZCL_PREQ_FLOW_###

implementing:

text
IF_OO_ADT_CLASSRUN

Rules:

text
external consumer
normal EML only
no IN LOCAL MODE
no direct SQL business writes

For repeatable cleanup, use a read-only lookup on:

text
ZR_PURCHASE_REQ_###

to discover old test UUIDs. Perform the actual delete through EML.

Reserved request numbers:

text
99000001
99000002

Final required state:

text
99000001 → A
99000002 → X

At startup, remove only old rows with those two request numbers:

text
discover UUIDs
DELETE through EML
inspect FAILED / REPORTED
COMMIT cleanup

11. Approval path ​

Create:

text
RequestNo        = 99000001
RequesterName    = Alex Morgan
RequesterEmail   = alex@example.com
ItemDescription  = Development laptop
Quantity         = 2
UnitPrice        = 1450.00
CurrencyCode     = EUR
NeededBy         = current system date + 14
Status           = omitted
Notes            = Approval path

Required proof:

text
CREATE returns managed UUID in MAPPED
UUID resolved from %cid
READ before COMMIT shows Status N
COMMIT succeeds

Run:

text
GET PERMISSIONS ONLY INSTANCE FEATURES

Required New-state result:

text
submit enabled
approve disabled
reject disabled
purchase fields unrestricted

Update one controlled field while New and prove the update persists.

Execute:

text
submit

Required:

text
N → S
RESULT contains changed entity
COMMIT succeeds

Run permissions again:

text
submit disabled
approve enabled
reject enabled
purchase fields read-only

Attempt a normal external EML update to:

text
UnitPrice

Required proof:

text
update is rejected as read-only
FAILED contains the instance
%fail-cause is READONLY
persisted UnitPrice remains unchanged

Execute:

text
approve

Required:

text
S → A
RESULT contains changed entity
COMMIT succeeds

After approval:

text
all workflow actions disabled
purchase fields read-only

Then deliberately execute:

text
submit

again through normal external EML.

Because submit is disabled for Status A, RAP must reject the request before the action handler executes.

Required:

text
Status remains A
FAILED contains the instance
FAILED marks the submit action
REPORTED is empty for this feature-control rejection

This is the negative runtime proof that action feature control is enforced, not merely returned by GET PERMISSIONS.


12. Validation-failure path ​

Attempt:

text
RequestNo        = 99000002
RequesterName    = initial
ItemDescription  = initial
Quantity         = 0
UnitPrice        = 0
CurrencyCode     = initial
NeededBy         = yesterday
Status           = omitted

Run the invalid create with:

text
MODIFY ENTITIES

Then trigger the save sequence with a response form equivalent to:

abap
COMMIT ENTITIES
  RESPONSE OF ZR_Purchase_Req_###
    FAILED DATA(failed_commit)
    REPORTED DATA(reported_commit).

Required save-phase evidence:

text
FAILED contains the invalid instance identified by its technical key / %tky
REPORTED contains multiple field-targeted errors
the failed instance can be correlated to the create request through %cid / MAPPED
no row with RequestNo 99000002 is persisted

Then reset the transactional buffer before retrying the same test identifier:

abap
ROLLBACK ENTITIES.

13. Rejection path ​

Create 99000002 again with valid data.

Drive:

text
Create
→ N

submit
→ S

reject
→ X

Commit each successful transition.

Final permissions:

text
submit disabled
approve disabled
reject disabled
purchase fields read-only

14. Final proof ​

Read both rows through EML.

Required state:

text
99000001 → A
99000002 → X

Verify with:

text
F8 on ZR_PURCHASE_REQ_###

Inspect at least one row's:

text
LocalCreatedBy
LocalCreatedAt
LocalLastChangedBy
LocalLastChangedAt
LastChangedAt

15. Submission checklist ​

Submit:

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

The solution must demonstrate:


16. Certification focus ​

This checkpoint is intentionally task-driven.

The important skill is being able to work inside ADT and move between:

text
table
root CDS
BDEF
Behavior Pool
EML consumer
runtime results

while diagnosing:

text
activation errors
behavior-contract errors
incorrect state transitions
validation failures
feature-control failures
transaction/save behavior

It is not intended to simulate the entire certification. Later course stages add authorization, service exposure, Draft, advanced RAP, quality tooling, and a final S/4HANA side-by-side integration project.


17. Next steps ​

The project is complete when you can demonstrate the persisted approval and rejection paths, explain the behavior contract, and show the required failure cases.

Continue with Lesson 15 — Authorization, then add the service layer and draft editing in Lessons 16 and 17. Return to the ABAP SQL, CDS & RAP roadmap to see the full sequence, or revisit the Lesson 14 companion and EML Cheat Sheet to review actions, permissions, and transactions.

Browse all projects →