Introduction.
Draft functionality is a widely used feature in SAP Fiori applications, enabling users to temporarily save changes without committing them to the active data .
In this blog, I will demonstrate how to enable draft functionality using BOPF (Business Object Processing Framework) for a custom Sales Order Request business object.
The requirement is to build a Sales Order Request object with draft capability using BOPF. The key .
Step 1: Activate the Persistence Table
We begin by creating the active persistence table that stores finalized sales order requests.
@EndUserText.label : ‘Sales Order Request’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zmuk_dt_so_req {
key client : mandt not null;
key so_req_id : /bobf/conf_key not null;
cust_id : zmuk_de_cust_id;
mat_id : abap.char(10);
quantity : abap.numc(10);
req_date : abap.dats;
@Semantics.amount.currencyCode : ‘zmuk_dt_so_req.currency’
price : abap.curr(15,2);
currency : abap.cuky;
total_price : abap.numc(10);
status : abap.char(15);
created_by : syuname;
created_on : abap.dats;
}
Step 2: Generating the Draft Table Automatically
When we use the draft enablement annotations in the CDS interface view, the system automatically generates the draft persistence table.
In our case, we specify:
@ObjectModel.writeDraftPersistence: ‘ZMUK_DT_DRAFT_SO’
Here is the auto-generated draft table:
@EndUserText.label : ‘ZMUK_I_DRAFT_SO_REQ ZMUK_I_DRAFT_SO_REQ’
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zmuk_dt_draft_so {
key mandt : mandt not null;
key so_req_id : /bobf/conf_key not null;
cust_id : zmuk_de_cust_id;
mat_id : abap.char(10);
quantity : abap.numc(10);
req_date : abap.dats;
@Semantics.amount.currencyCode : ‘zmuk_dt_draft_so.currency’
price : abap.curr(15,2);
currency : abap.cuky;
total_price : abap.numc(10);
status : abap.char(15);
created_by : syuname;
created_on : abap.dats;
activeuuid : /bobf/uuid;
hasactiveentity : sdraft_has_active;
include sdraft_write_draft_admin;
}
Additional fields are added by the framework:
These fields enable BOPF to manage the draft lifecycle automatically.
Step 3 : Create a CDS interface view with Draft Handling
We now define the CDS interface view, which links the active and draft persistence tables and enables draft functionality.
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Draft for sales order request’
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
@ObjectModel : {
representativeKey: ‘so_req_id’,
modelCategory: #BUSINESS_OBJECT,
semanticKey: [ ‘so_req_id’ ],
transactionalProcessingEnabled: true,
compositionRoot: true,
createEnabled: true,
updateEnabled: true,
deleteEnabled: true,
draftEnabled: true,
writeActivePersistence: ‘ZMUK_DT_SO_REQ’,
writeDraftPersistence: ‘ZMUK_DT_DRAFT_SO’
}
@OData.publish: true
define view entity ZMUK_I_DRAFT_SO_REQ as select from zmuk_dt_so_req
{
@UI.facet: [{
type: #IDENTIFICATION_REFERENCE,
position: 10,
label: ‘Sales Order Request’,
purpose: #STANDARD
}]
@UI.lineItem: [{ position: 10, label: ‘SO requets ID’ }]
@UI.identification: [{ position: 10, label: ‘SO requets ID’ }]
key so_req_id,
@UI.lineItem: [{ position: 20, label: ‘Customer ID’ }]
@UI.identification: [{ position: 20, label: ‘Customer ID’ }]
cust_id,
@UI.lineItem: [{ position: 30, label: ‘Material ID’ }]
@UI.identification: [{ position: 30, label: ‘Material ID’ }]
mat_id,
@UI.lineItem: [{ position: 40, label: ‘Quatity’ }]
@UI.identification: [{ position: 40, label: ‘Quatity’ }]
quantity,
@UI.lineItem: [{ position: 50, label: ‘Request ID’ }]
@UI.identification: [{ position: 50, label: ‘Request ID’ }]
req_date,
@UI.lineItem: [{ position: 60, label: ‘Price’ }]
@UI.identification: [{ position: 60, label: ‘Price’ }]
@Semantics.amount.currencyCode : ‘currency’
price,
@UI.lineItem: [{ position: 70, label: ‘Currency’ }]
@UI.identification: [{ position: 70, label: ‘Currency’ }]
currency,
@UI.lineItem: [{ position: 80, label: ‘Total Price’ }]
@UI.identification: [{ position: 80, label: ‘Total Price’ }]
total_price,
@UI.lineItem: [{ position: 90, label: ‘Status’ }]
@UI.identification: [{ position: 90, label: ‘Status’ }]
status,
created_by,
created_on
}
Note :
draftEnabled: true activates draft handling in BOPF. writeActivePersistence → points to the active table (ZMUK_DT_SO_REQ). writeDraftPersistence → triggers automatic generation of the draft table (ZMUK_DT_DRAFT_SO). @odata.publish: true → exposes the CDS view as an OData service with draft actions.
Step 4: Creating OData Service
Go to /IWFND/MAINT_SERVICES to add the service.
Click on Add services to add the service.
Provide the system alias name and technical service name, while adding the technical service it should be same as the CDS view which we have created above along with _CDS and then press enter.
Example: – ZMUK_I_DRAFT_SO_REQ_CDS
The backend service will appear, now select the backend service and click on Add selected services
Provide the package name and save it.
It will ask to save it in the tr.
It will display a successful message that the service was created.
Now, go back and click on SAP GATEWAY CLIENT.
Test the service in the Gateway client — you will notice the draft entity sets and actions (Edit, SaveDraft, Activate) are available.
Now, go back and call the service in browser by clicking on CALL BROWSER.
Copy the service name.
Step 5 : Consume in Fiori Application
To consume the service in a Fiori app:
Open VS Code with SAP Fiori tools installed. Create a new Fiori Elements application.
Select the list report and click on next.
Select the data source as Odata service and add the odata service URL and provide the user name and password then click on next.
Click on next again.
Click on finish.
Run the application.
Existing records.
Create a record and don’t save it.
Check for Own draft, the record is saved in the draft.
If displayed with All records.
Conclusion :
Draft Handling in BOPF ensures safe, consistent editing by separating draft data from active data. It improves user experience, prevents data loss, and enables smooth, interruption-free transactions.
Introduction. Draft functionality is a widely used feature in SAP Fiori applications, enabling users to temporarily save changes without committing them to the active data . In this blog, I will demonstrate how to enable draft functionality using BOPF (Business Object Processing Framework) for a custom Sales Order Request business object. The requirement is to build a Sales Order Request object with draft capability using BOPF. The key . Step 1: Activate the Persistence Table We begin by creating the active persistence table that stores finalized sales order requests. @EndUserText.label : ‘Sales Order Request’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zmuk_dt_so_req {
key client : mandt not null;
key so_req_id : /bobf/conf_key not null;
cust_id : zmuk_de_cust_id;
mat_id : abap.char(10);
quantity : abap.numc(10);
req_date : abap.dats;
@Semantics.amount.currencyCode : ‘zmuk_dt_so_req.currency’
price : abap.curr(15,2);
currency : abap.cuky;
total_price : abap.numc(10);
status : abap.char(15);
created_by : syuname;
created_on : abap.dats;
} Step 2: Generating the Draft Table Automatically When we use the draft enablement annotations in the CDS interface view, the system automatically generates the draft persistence table. In our case, we specify: @ObjectModel.writeDraftPersistence: ‘ZMUK_DT_DRAFT_SO’ Here is the auto-generated draft table:@EndUserText.label : ‘ZMUK_I_DRAFT_SO_REQ ZMUK_I_DRAFT_SO_REQ’
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zmuk_dt_draft_so {
key mandt : mandt not null;
key so_req_id : /bobf/conf_key not null;
cust_id : zmuk_de_cust_id;
mat_id : abap.char(10);
quantity : abap.numc(10);
req_date : abap.dats;
@Semantics.amount.currencyCode : ‘zmuk_dt_draft_so.currency’
price : abap.curr(15,2);
currency : abap.cuky;
total_price : abap.numc(10);
status : abap.char(15);
created_by : syuname;
created_on : abap.dats;
activeuuid : /bobf/uuid;
hasactiveentity : sdraft_has_active;
include sdraft_write_draft_admin;
}
Additional fields are added by the framework: These fields enable BOPF to manage the draft lifecycle automatically. Step 3 : Create a CDS interface view with Draft Handling We now define the CDS interface view, which links the active and draft persistence tables and enables draft functionality. @AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Draft for sales order request’
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
@ObjectModel : {
representativeKey: ‘so_req_id’,
modelCategory: #BUSINESS_OBJECT,
semanticKey: [ ‘so_req_id’ ],
transactionalProcessingEnabled: true,
compositionRoot: true,
createEnabled: true,
updateEnabled: true,
deleteEnabled: true,
draftEnabled: true,
writeActivePersistence: ‘ZMUK_DT_SO_REQ’,
writeDraftPersistence: ‘ZMUK_DT_DRAFT_SO’
}
@OData.publish: true
define view entity ZMUK_I_DRAFT_SO_REQ as select from zmuk_dt_so_req
{
@UI.facet: [{
type: #IDENTIFICATION_REFERENCE,
position: 10,
label: ‘Sales Order Request’,
purpose: #STANDARD
}]
@UI.lineItem: [{ position: 10, label: ‘SO requets ID’ }]
@UI.identification: [{ position: 10, label: ‘SO requets ID’ }]
key so_req_id,
@UI.lineItem: [{ position: 20, label: ‘Customer ID’ }]
@UI.identification: [{ position: 20, label: ‘Customer ID’ }]
cust_id,
@UI.lineItem: [{ position: 30, label: ‘Material ID’ }]
@UI.identification: [{ position: 30, label: ‘Material ID’ }]
mat_id,
@UI.lineItem: [{ position: 40, label: ‘Quatity’ }]
@UI.identification: [{ position: 40, label: ‘Quatity’ }]
quantity,
@UI.lineItem: [{ position: 50, label: ‘Request ID’ }]
@UI.identification: [{ position: 50, label: ‘Request ID’ }]
req_date,
@UI.lineItem: [{ position: 60, label: ‘Price’ }]
@UI.identification: [{ position: 60, label: ‘Price’ }]
@Semantics.amount.currencyCode : ‘currency’
price,
@UI.lineItem: [{ position: 70, label: ‘Currency’ }]
@UI.identification: [{ position: 70, label: ‘Currency’ }]
currency,
@UI.lineItem: [{ position: 80, label: ‘Total Price’ }]
@UI.identification: [{ position: 80, label: ‘Total Price’ }]
total_price,
@UI.lineItem: [{ position: 90, label: ‘Status’ }]
@UI.identification: [{ position: 90, label: ‘Status’ }]
status,
created_by,
created_on
} Note : draftEnabled: true activates draft handling in BOPF. writeActivePersistence → points to the active table (ZMUK_DT_SO_REQ). writeDraftPersistence → triggers automatic generation of the draft table (ZMUK_DT_DRAFT_SO). @odata.publish: true → exposes the CDS view as an OData service with draft actions. Step 4: Creating OData Service Go to /IWFND/MAINT_SERVICES to add the service. Click on Add services to add the service. Provide the system alias name and technical service name, while adding the technical service it should be same as the CDS view which we have created above along with _CDS and then press enter. Example: – ZMUK_I_DRAFT_SO_REQ_CDS The backend service will appear, now select the backend service and click on Add selected services Provide the package name and save it. It will ask to save it in the tr. It will display a successful message that the service was created. Now, go back and click on SAP GATEWAY CLIENT. Test the service in the Gateway client — you will notice the draft entity sets and actions (Edit, SaveDraft, Activate) are available. Now, go back and call the service in browser by clicking on CALL BROWSER. Copy the service name. Step 5 : Consume in Fiori Application To consume the service in a Fiori app: Open VS Code with SAP Fiori tools installed. Create a new Fiori Elements application. Select the list report and click on next. Select the data source as Odata service and add the odata service URL and provide the user name and password then click on next. Click on next again. Click on finish. Run the application. Existing records.Create a record and don’t save it. Check for Own draft, the record is saved in the draft. If displayed with All records. Conclusion :Draft Handling in BOPF ensures safe, consistent editing by separating draft data from active data. It improves user experience, prevents data loss, and enables smooth, interruption-free transactions. Read More Technology Blog Posts by Members articles
#SAP
#SAPTechnologyblog