Appearance
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 controlIts workflow is:
text
New
↓ submit
Submitted
├─ approve → Approved
└─ reject → RejectedConsumers work with the BO through:
text
EMLrather 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
ZLOCALunless your course setup already uses another course superpackage.
Then:
text
Project Explorer
→ right-click the superpackage
→ New
→ ABAP PackageUse a package name such as:
text
ZPKG_CP3_###Description:
text
Checkpoint 3 - Purchase RequestCreate 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 testsTarget 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 = RejectedAllowed transitions:
text
N ──submit──→ S
├──approve──→ A
└──reject───→ XStatus 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:
| Value | Meaning |
|---|---|
N | New |
S | Submitted |
A | Approved |
X | Rejected |
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 + uuidRequired fields:
| Field | Requirement |
|---|---|
client | key, client type |
uuid | key, SYSUUID_X16 |
request_no | NUMC(8) |
requester_name | CHAR(60) |
requester_email | CHAR(100) |
item_description | CHAR(100) |
quantity | INT4 |
unit_price | CURR(10,2) |
currency_code | CUKY |
needed_by | DATS |
status | ZDE_PRSTAT_### |
notes | CHAR(255) |
Add the five RAP administrative fields:
text
local_created_by
local_created_at
local_last_changed_by
local_last_changed_at
last_changed_atApply:
text
unit_price → currency_codeamount/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
LastChangedAtRequirements:
text
Uuid = CDS key
client not exposed
CamelCase aliases
UnitPrice → CurrencyCode semantics
correct semantic annotations on all five admin fieldsActivate and verify with:
text
F86. 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 : instanceMap 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
→ NImplementation 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 requiredDate validation
abap
validation validateNeededBy on save
{ create; field NeededBy; }Rule:
text
NeededBy >= current system dateUse 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 requiredFor invalid instances:
text
FAILED
→ identifies failed instance
REPORTED
→ error message
%element
→ targets relevant field8. 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 MODENo direct SQL.
Required action effects:
text
submit
→ Status = S
approve
→ Status = A
reject
→ Status = XEvery successful action must:
text
update Status in the transactional buffer
read the changed instance
fill result [1] $selfThe 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:
| Status | Submit | Approve | Reject | Purchase fields |
|---|---|---|---|---|
N | Enabled | Disabled | Disabled | Editable |
S | Disabled | Enabled | Enabled | Read-only |
A | Disabled | Disabled | Disabled | Read-only |
X | Disabled | Disabled | Disabled | Read-only |
Purchase fields:
text
ItemDescription
Quantity
UnitPrice
CurrencyCode
NeededByUse operation feature constants for actions:
text
if_abap_behv=>fc-o-enabled
if_abap_behv=>fc-o-disabledUse field feature constants for fields:
text
if_abap_behv=>fc-f-unrestricted
if_abap_behv=>fc-f-read_onlyImplement:
text
get_instance_featuresfrom the current Status.
10. EML consumer contract
Create:
text
ZCL_PREQ_FLOW_###implementing:
text
IF_OO_ADT_CLASSRUNRules:
text
external consumer
normal EML only
no IN LOCAL MODE
no direct SQL business writesFor 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
99000002Final required state:
text
99000001 → A
99000002 → XAt startup, remove only old rows with those two request numbers:
text
discover UUIDs
DELETE through EML
inspect FAILED / REPORTED
COMMIT cleanup11. 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 pathRequired proof:
text
CREATE returns managed UUID in MAPPED
UUID resolved from %cid
READ before COMMIT shows Status N
COMMIT succeedsRun:
text
GET PERMISSIONS ONLY INSTANCE FEATURESRequired New-state result:
text
submit enabled
approve disabled
reject disabled
purchase fields unrestrictedUpdate one controlled field while New and prove the update persists.
Execute:
text
submitRequired:
text
N → S
RESULT contains changed entity
COMMIT succeedsRun permissions again:
text
submit disabled
approve enabled
reject enabled
purchase fields read-onlyAttempt a normal external EML update to:
text
UnitPriceRequired proof:
text
update is rejected as read-only
FAILED contains the instance
%fail-cause is READONLY
persisted UnitPrice remains unchangedExecute:
text
approveRequired:
text
S → A
RESULT contains changed entity
COMMIT succeedsAfter approval:
text
all workflow actions disabled
purchase fields read-onlyThen deliberately execute:
text
submitagain 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 rejectionThis 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 = omittedRun the invalid create with:
text
MODIFY ENTITIESThen 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 persistedThen 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
→ XCommit each successful transition.
Final permissions:
text
submit disabled
approve disabled
reject disabled
purchase fields read-only14. Final proof
Read both rows through EML.
Required state:
text
99000001 → A
99000002 → XVerify with:
text
F8 on ZR_PURCHASE_REQ_###Inspect at least one row's:
text
LocalCreatedBy
LocalCreatedAt
LocalLastChangedBy
LocalLastChangedAt
LastChangedAt15. 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 resultswhile diagnosing:
text
activation errors
behavior-contract errors
incorrect state transitions
validation failures
feature-control failures
transaction/save behaviorIt 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.