Appearance
Lesson 20 Practice — Extend a Calibration Request BO
Your assignment
A factory tracks calibration requests for its measuring instruments. The original application records an instrument code, a result code, and a note. Another team has asked for two enhancements:
- Make the result easy to understand:
Pshould display Passed, andFshould display Failed. - Reject unsupported result codes instead of silently allowing them into the system.
You're the extension developer. The original team permits extension points, but you must put your two enhancements in separate extension artifacts.
text
Calibration Request BO
|
+-- CDS extension -> friendly result label
|
+-- BDEF extension -> result validationThis SAP BTP ABAP Trial project is independent of the teacher's inspection BO and all earlier course applications. You may consult the guided lesson and Companion for syntax, but adapt the design and code yourself.
Student work: independent practice following the teacher-led lesson; no chapter-by-chapter timing constraints.
1. Required new objects
Create a fresh package ZPKG_L20P_###.
| Object | Purpose |
|---|---|
ZCAL_### | Calibration request table |
ZR_CAL_### | RAP root CDS |
BDEF for ZR_CAL_### | Managed provider behavior |
ZBP_R_CAL_### | Base behavior pool |
ZX_CAL_V_### | CDS extension |
ZX_CAL_B_### | Behavior extension |
ZBP_X_CAL_### | Extension behavior pool |
ZCL_CAL_TEST_### | External EML test class |
Use your own three-digit suffix for ###. Keep the extension field suffix Z20 as required below.
2. Build the provider table
Create ZCAL_### as a transparent, client-dependent application table.
Fields:
| Field | Type | Notes |
|---|---|---|
client | abap.clnt | Key, not null |
uuid | sysuuid_x16 | Key, not null |
instrument_code | abap.char(20) | Business identifier |
result_code | abap.char(1) | P or F |
calibration_note | abap.char(120) | Free text |
local_last_changed_at | abp_locinst_lastchange_tstmpl | Managed ETag |
Use table annotations equivalent to the guided project and enable the table with:
abap
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANYAcceptance: the table activates; Data Preview shows an initially empty table.
3. Build the provider CDS entity
Create a root CDS ZR_CAL_### selecting from ZCAL_### with alias:
text
CalibrationExpose these fields:
text
Uuid
InstrumentCode
ResultCode
CalibrationNote
LocalLastChangedAtAnnotate the timestamp with the correct RAP local-instance-last-changed semantic annotation.
The view must be extension-enabled using:
text
@AbapCatalog.viewEnhancementCategory: [#PROJECTION_LIST]
@AbapCatalog.extensibility.extensible: true
@AbapCatalog.extensibility.elementSuffix: 'Z20'
@AbapCatalog.extensibility.dataSources: ['Calibration']You may use the structured @AbapCatalog.extensibility: { ... } form from the guided lesson. Include a small quota and disallow new data sources for this exercise.
Acceptance: CDS root activates before creating its extension.
4. Build the base managed RAP behavior
Create a managed BDEF for ZR_CAL_### with entity alias:
text
CalibrationRequestUse:
text
managed implementation in class ZBP_R_CAL_###
strict (2)
standard create/update/delete
managed UUID numbering
lock master
authorization master (none)
ETag on LocalLastChangedAt
explicit persistence mappingEnable behavior extensibility both at BDEF-header level and entity level. Allow:
abap
with validations on save;Use a mapping that supports extensibility (the corresponding extensible pattern from the guided lesson).
Acceptance: generate and activate ZBP_R_CAL_###. At this stage no validation has been declared or implemented.
Part A — CDS Extension
5. Add the readable result
Create a separate CDS View Entity Extension:
text
ZX_CAL_V_###Use a new CDS Data Definition with extend view entity ZR_Cal_### with to add a calculated element:
text
ZZResultLabelZ20Required calculation:
text
ResultCode = P -> Passed
ResultCode = F -> Failed
anything else -> UnknownUse CASE with the permitted data-source alias Calibration. Give the result branches a consistent character type/length, and put the new name at the end with as ZZResultLabelZ20.
Do not add zz_result_label to ZCAL_###; this is a derived read field, not new storage.
Acceptance: activate ZX_CAL_V_### and confirm the extra element appears in Data Preview for ZR_CAL_###.
6. Show that the base source remained unchanged
Open ZR_CAL_### and inspect its select list. The code should still contain only its base fields.
Open ZX_CAL_V_###; it alone should contain the new label expression.
Explain what the CDS view runtime now offers that the persistence table does not.
Part B — Behavior Extension
7. Add a save validation
Create a separate BDEF extension:
text
ZX_CAL_B_###It must add a validation named:
text
checkResultCodeTrigger it:
text
on create
when ResultCode is changedThe extension's implementation class must be:
text
ZBP_X_CAL_###Use the self-owned base BO directly; a separate released BO interface is not necessary for this practice.
Acceptance: extension activates, with no modifications to the provider's original BDEF behavior body.
8. Implement the validation
Generate the extension behavior pool and handler method through ADT Quick Fix.
Your method should:
- Read
ResultCodefrom the current BO instances withREAD ENTITIES ... IN LOCAL MODE. - Allow
PandF. - Reject
X, blank, and any other unsupported value. - Add the failing instance to
FAILED. - Add an explanatory error message to
REPORTEDand mark%element-ResultCode.
Do not write SQL INSERT, UPDATE, or DELETE in this validation.
Acceptance: behavior extension and pool activate.
Part C — EML Verification
9. Run the valid case
Create an external console class:
text
ZCL_CAL_TEST_###Implement IF_OO_ADT_CLASSRUN and use normal EML to create a calibration request:
text
InstrumentCode = GAUGE-20
ResultCode = P
CalibrationNote = Passed calibration checkUse MAPPED, FAILED, REPORTED, and COMMIT ENTITIES RESPONSE OF.
Expected:
text
Saved active row: yes
ResultCode: P
ZZResultLabelZ20: PassedRepeat with ResultCode = F. Expected label: Failed; save also succeeds.
10. Verify invalid creates and updates
Reject an unsupported code on create
Create a new request with:
text
InstrumentCode = GAUGE-X
ResultCode = XExpected:
text
MODIFY may place request in buffer
COMMIT rejects it
FAILED contains instance
REPORTED contains validation message
No invalid active row persistsAfter a failed save, clear the failed transaction with ROLLBACK ENTITIES before a new test.
Reject a blank code on create
Repeat the create test with InstrumentCode = GAUGE-BLANK and an initial ResultCode (space). Keep ResultCode in the CREATE FIELDS list. Inspect the commit response: it must contain a failed instance and the validation message. After rollback, confirm that no row for this test was persisted.
Reject invalid changes to an existing row
Use the UUID of a successfully saved P request from section 9. Obtain it from the create response's MAPPED result or Data Preview.
- Use normal EML for entity
CalibrationRequestwithUPDATE FIELDS ( ResultCode ), supplying thatUuidandResultCode = X. - Check the modify response, then inspect
COMMIT ENTITIES RESPONSE OF ZR_CAL_###. The commit must fail with the validation message. - Run
ROLLBACK ENTITIES, then confirm in Data Preview that the stored result is stillPand the label is stillPassed. - Repeat in a fresh transaction with a blank
ResultCode. Expect the same rejection and unchanged stored row. - Finally, update the same row from
PtoF. Commit must succeed, and Data Preview must showF/Failed.
Run each case separately and clear every failed transaction before continuing. These tests prove that the field trigger protects updates as well as creates.
11. Experiment: temporarily disable the extension's check
Keep the BDEF extension's validation declaration and the generated handler method signature intact. The method signature references CalibrationRequest~checkResultCode, so removing only the validation declaration would leave an inconsistent implementation.
Perform this experiment in your own practice BO:
- Preserve the validation method's implementation, then temporarily comment out only the code inside
METHOD checkResultCode ... ENDMETHODinZBP_X_CAL_###. Keep both method delimiters and activate the extension pool. - Create a separate request with
InstrumentCode = GAUGE-OFFandResultCode = X. Check both modify and commit responses. With the check disabled, the commit should succeed. Record the new UUID and confirm the row in Data Preview. - Restore the method body and activate the extension pool again.
- Delete the temporary
GAUGE-OFFrow by its UUID using normal EMLDELETEforCalibrationRequest, then commit and check the responses. Confirm the row is gone. Restoring a validation does not retroactively remove invalid stored data. - Repeat the
Xcreate test with the restored check. Commit must fail again; clear the buffer withROLLBACK ENTITIES.
The provider BDEF and provider behavior pool remain unchanged throughout. Explain how changing only the extension implementation changes the save outcome. Submit with the check restored, all objects active, and the temporary invalid row removed.
12. Verify technical ownership
Check that:
text
ZCAL_### and ZR_CAL_###
-> remain provider objects
ZX_CAL_V_###
-> owns the read-field addition
ZX_CAL_B_### and ZBP_X_CAL_###
-> own the validation additionExplain why this separation could make future upgrades or maintenance easier.
Part D — Understand the Real SAP Boundary
13. Would this work on an SAP-delivered S/4HANA BO?
Explain the difference between:
- a BO you own and have made extensible;
- a C0-released SAP extension point that permits selected enhancements;
- a C1-released API intended for consumption;
- a BTP side-by-side extension consuming an S/4HANA API.
Important: do not claim you changed an SAP standard object in Trial. This practice demonstrates the technique, not access to on-stack S/4HANA customization.
14. Design challenge — a persisted custom field
A product manager now asks for a stored InspectorComment that users can edit.
Without building it, write down which layers would need attention:
text
storage append / extension include
CDS base model extension
behavior mapping / field control
projection + UI metadata
Draft persistence if Draft existedExplain why just adding a CASE expression would not satisfy the new requirement.
15. Submission evidence
Your review should show:
- the activated provider table/root/BDEF and base behavior pool;
- both distinct extensions and the extension behavior pool;
- the calculated field in
ZR_CAL_###Data Preview; - an EML test with a successful
Pcreate; - an EML test with a successful
Fcreate; - an EML test with a failed
Xcreate and its message; - an EML test with a failed blank-code create and its message;
- failed updates to
Xand blank, with evidence that the original stored value remains unchanged; - a successful update from
PtoF, showing the updated label; - the temporary-check experiment, followed by restoration, cleanup, and a failed
Xcreate; - the original provider source untouched by the two changes.
16. Student checklist
17. Reference and troubleshooting
Use the Lesson 20 Companion for the Inspection implementation pattern; translate entity aliases and fields to CalibrationRequest / ResultCode.
If you become stuck, check, in order:
- provider objects all active;
- CDS
extensible,dataSources: ['Calibration'],elementSuffix: 'Z20'; - base BDEF
strict ( 2 ), headerwith validations on save, entityextensible; - BDEF extension class/method generated in the correct behavior pool;
COMMIT ENTITIESresponse, not justMODIFY ENTITIESresponse.
SAP reference: Enabling and Developing Behavior Extensions.
Verification boundary: Syntax patterns are derived from current official SAP examples. Runtime activation must be confirmed in the actual BTP Trial tenant used for the course.