Singleton Entities in RAP Design, Multi Line Edit, and Best Practices

Estimated read time 15 min read

In  this blog I have explained creating of an RAP Singleton entity  with multiline edit  functionalities , and also coevered below topics 
1.copy action

2. Depricate actions

3.invalidating entries

4.transport organizer 

Add Copy Action : Helps to create new entries in object page 

In case we have  lot of fields and we  need to refer existing entries –> and based on that if we want to create new entries we can make use of this action 

Add Deprecate Action :  If we want to deprecate some entries ( make it invalid )  

We can make use of this action  

Select to include a deprecate and invalidate action in the generated app 

Pre requisite : the table must have  a field CONFIGDEPRECATIONCODE with data element CONFIG_DEPRECATION_CODE  

Data Consistency Check : select this option to include a validation for the prepare draft action in the generated app which checks the consistency  of the input fields with  

Domain with fixed values 

Foreign keys where @abapCatalog.foreign key.screenCheck : true 

 If we have foreign key relationship / data element / domain 

( suppose we have gender field in the employee table –> we need to maintain it in the 

Domain level ) 

We can make use of  this field. 

<< 

Enable Transport Selection selected  

The transport request selection action displayed in edit mode The transport request information is displayed in the header toolbar If the save action is executed without  selected transport request and the transport is mandatory , the action to select a transport request is triggered automatically . After  selecting  a transport request , the save action continues  

 

If the action is not selected , a transport selection is made possible using an action button . This option is selected by default    

 

< Manual :   

 Select manual to include the action select transport  in the generated app 

With this action , you can select an existing customizing transport request before saving the configuration changes  

< Manual with pre selection  

To include the action select transport in the generated app .  

With this action , you can select an existing customizing transport request before saving the configuration changes. When the edit action is performed , a customizing transport request is determined automatically 

< No transport : Select No transport for an app without a transport suitable  fro configurations that are to be adjusted in the productive system 

 

 T1. take a custom table say employee 

@EndUserText.label : ’employee table’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #C
@AbapCatalog.dataMaintenance : #ALLOWED
define table zpd_dt_em {

key client : abap.clnt not null;
key employee_id : zpd_de_emp not null;
@EndUserText.label : ‘First Name’
first_name : abap.char(50);
@EndUserText.label : ‘Last Name’
last_name : abap.char(50);
@EndUserText.label : ‘Department’
department : abap.char(20);
@EndUserText.label : ‘Joining Date’
joining_date : abap.dats;
@EndUserText.label : ‘Active Employee’
is_active : abap.char(1);
@EndUserText.label : ‘Changed By’
changed_by : abap.char(12);
configdeprecationcode : config_deprecation_code;
local_last_changed_at : abp_locinst_lastchange_tstmpl;
changed_at : abp_lastchange_tstmpl;

}

2. Create a LANGUAGE TABLE : 

Here we need one  entry – so we have selected I_Language and put into the  

Where condition so that we get one entry to achieve singleton  

3.  select table and click on select generate repository objects

4. 

5.Delivery class must be C 

6. Data maintenance should be allowed

7.Also we need to have a data element( domain )

8. Provide package

9. We have business service definition and binding 

10. 

.Also, we have singleton entity details 

Singleton entity name ( EmployeeTableAll) Interface view ( Zi_EmployeeTable_S) Projection view  ( Zi_EmployeeTable_S) Key field (SinlgetonID) Draft table (zpd_dt_empl_d_s) 

11. Table details 

12. we can be able to see the classes 

 

13. we have scenario option 

 

 

 

14 Transport selection : 

 

 

15. activate the service binding

16. All the atrifacts will be generated 

intreface view:

@EndUserText.label: ’employee table’
@AccessControl.authorizationCheck: #MANDATORY
@Metadata.allowExtensions: true

define view entity ZI_EmployeeTable1
as select from ZPD_DT_EM
association to parent ZI_EmployeeTable_S1 as _EmployeeTableAll on $projection.SingletonID = _EmployeeTableAll.SingletonID
association [0..*] to I_ConfignDeprecationCodeText as _ConfignDeprecationCodeText on $projection.ConfigDeprecationCode = _ConfignDeprecationCodeText.ConfigurationDeprecationCode

{

key EMPLOYEE_ID as EmployeeId,
FIRST_NAME as FirstName,
LAST_NAME as LastName,
DEPARTMENT as Department,
JOINING_DATE as JoiningDate,
IS_ACTIVE as IsActive,
CHANGED_BY as ChangedBy,
@ObjectModel.text.association: ‘_ConfignDeprecationCodeText’
@Consumption.valueHelpDefinition: [ {

entity: {

name: ‘I_ConfignDeprecationCode’,
element: ‘ConfigurationDeprecationCode’

}

} ]

CONFIGDEPRECATIONCODE as ConfigDeprecationCode,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
@Consumption.hidden: true
LOCAL_LAST_CHANGED_AT as LocalLastChangedAt,
@Semantics.systemDateTime.lastChangedAt: true
CHANGED_AT as ChangedAt,
@Consumption.hidden: true
1 as SingletonID,
_EmployeeTableAll,
case when CONFIGDEPRECATIONCODE = ‘W’ then 2 when CONFIGDEPRECATIONCODE = ‘E’ then 1 else 3 end as ConfigDeprecationCode_Critlty,
_ConfignDeprecationCodeText

}

17.

parent entity for interface  

Here we have made joined I_language  with employee table because  

We want Etag field from the table so that we can get lastchangedat field 

<< one more association is created related to transport – because we need to capture the entries in transport ( I_abap_transportrequesttext) 

@EndUserText.label: ’employee table Singleton’
@AccessControl.authorizationCheck: #NOT_REQUIRED
@ObjectModel.semanticKey: [ ‘SingletonID’ ]
@UI: {

headerInfo: {
typeName: ‘EmployeeTableAll’

}

}

define root view entity ZI_EmployeeTable_S1

as select from I_Language
left outer join ZPD_DT_EM on 0 = 0
association [0..*] to I_ABAPTransportRequestText as _ABAPTransportRequestText on $projection.TransportRequestID = _ABAPTransportRequestText.TransportRequestID
composition [0..*] of ZI_EmployeeTable1 as _EmployeeTable

{

@UI.facet: [ {

id: ‘ZI_EmployeeTable1’,
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: ’employee table’,
position: 1 ,
targetElement: ‘_EmployeeTable’

} ]

@UI.lineItem: [ {
position: 1

} ]

key 1 as SingletonID,
_EmployeeTable,
@UI.hidden: true
max( ZPD_DT_EM.CHANGED_AT ) as LastChangedAtMax,
@ObjectModel.text.association: ‘_ABAPTransportRequestText’
@UI.identification: [ {

position: 2 ,
type: #WITH_INTENT_BASED_NAVIGATION,
semanticObjectAction: ‘manage’

} ]

@Consumption.semanticObject: ‘CustomizingTransport’
cast( ” as SXCO_TRANSPORT) as TransportRequestID,
_ABAPTransportRequestText

}

where I_Language.Language = $session.system_language

18. child entity holds the singleton filed including  all other fields

@EndUserText.label: ’employee table’
@AccessControl.authorizationCheck: #MANDATORY
@Metadata.allowExtensions: true
define view entity ZI_EmployeeTable1

as select from ZPD_DT_EM
association to parent ZI_EmployeeTable_S1 as _EmployeeTableAll on $projection.SingletonID = _EmployeeTableAll.SingletonID
association [0..*] to I_ConfignDeprecationCodeText as _ConfignDeprecationCodeText on $projection.ConfigDeprecationCode = _ConfignDeprecationCodeText.ConfigurationDeprecationCode

{

key EMPLOYEE_ID as EmployeeId,
FIRST_NAME as FirstName,
LAST_NAME as LastName,
DEPARTMENT as Department,
JOINING_DATE as JoiningDate,
IS_ACTIVE as IsActive,
CHANGED_BY as ChangedBy,
@ObjectModel.text.association: ‘_ConfignDeprecationCodeText’
@Consumption.valueHelpDefinition: [ {

entity: {
name: ‘I_ConfignDeprecationCode’,
element: ‘ConfigurationDeprecationCode’

}

} ]

CONFIGDEPRECATIONCODE as ConfigDeprecationCode,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
@Consumption.hidden: true
LOCAL_LAST_CHANGED_AT as LocalLastChangedAt,
@Semantics.systemDateTime.lastChangedAt: true
CHANGED_AT as ChangedAt,
@Consumption.hidden: true
1 as SingletonID,
_EmployeeTableAll,

case when CONFIGDEPRECATIONCODE = ‘W’ then 2 when CONFIGDEPRECATIONCODE = ‘E’ then 1 else 3 end as ConfigDeprecationCode_Critlty,

_ConfignDeprecationCodeText

}

19.behaviour definition

managed with additional save implementation in class ZBP_I_EMPLOYEETABLE_S1 unique;
strict;
with draft;
define behavior for ZI_EmployeeTable_S1 alias EmployeeTableAll

draft table ZPD_DT_EM_D_S1
with unmanaged save
lock master total etag LastChangedAtMax
authorization master( global )

{

field ( readonly )
SingletonID;
field ( features : instance )
TransportRequestID;
field ( notrigger )
SingletonID,
LastChangedAtMax;

update;
internal create;
internal delete;

draft action ( features : instance ) Edit with additional implementation;
draft action Activate optimized;
draft action Discard;
draft action Resume;
draft determine action Prepare;

action ( features : instance ) SelectCustomizingTransptReq parameter D_SelectCustomizingTransptReqP result [1] $self;

association _EmployeeTable { create ( features : instance ); with draft; }
validation ValidateTransportRequest on save ##NOT_ASSIGNED_TO_DETACT { create; update; }

side effects {
action SelectCustomizingTransptReq affects $self;

}

}

define behavior for ZI_EmployeeTable1 alias EmployeeTable ##UNMAPPED_FIELD

persistent table ZPD_DT_EM
draft table ZPD_DT_EM_D
etag master LocalLastChangedAt
lock dependent by _EmployeeTableAll
authorization dependent by _EmployeeTableAll

{

field ( mandatory : create )

EmployeeId;
field ( readonly )
SingletonID,
LocalLastChangedAt,
ChangedAt,
ConfigDeprecationCode,
ConfigDeprecationCode_Critlty;
field ( readonly : update )
EmployeeId;

field ( notrigger )
SingletonID,
LocalLastChangedAt,
ChangedAt;

update( features : global );
delete( features : instance );

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

mapping for ZPD_DT_EM

{

EmployeeId = EMPLOYEE_ID;
FirstName = FIRST_NAME;
LastName = LAST_NAME;
Department = DEPARTMENT;
JoiningDate = JOINING_DATE;
IsActive = IS_ACTIVE;
ChangedBy = CHANGED_BY;
ConfigDeprecationCode = CONFIGDEPRECATIONCODE;
LocalLastChangedAt = LOCAL_LAST_CHANGED_AT;
ChangedAt = CHANGED_AT;

}
association _EmployeeTableAll { with draft; }

validation ValidateTransportRequest on save ##NOT_ASSIGNED_TO_DETACT { create; update; delete; }

}

20. service definition

@ObjectModel.leadingEntity.name: ‘ZI_EmployeeTable_S’
define service ZUI_EMPLOYEETABLE {
expose ZI_EMPLOYEETABLE_S as EmployeeTableAll;
expose ZI_EMPLOYEETABLE as EmployeeTable;

}

21. service binding

22. OUTPUT

23. 

24. Double click on singleton entity 

25. here the edit button  is disabled –because no access to the tbale

26.  

27. Click on edit 

We will get the option to create 

28.

29.Entry will be created

30. As soon as you click on save we will get an error 

As we have not specified any  customizing transport 

31. 

32. now we data is getting stored but not displaying because of CDS authorization  

Select not allowed 

33.  now we can be able to see the data

34. 

<< Copy Action 

I  will select the entry ( id 3) 

And click on copy action ..entry will be copied and we  need to assign new Id 

And also we can make changes in the perticular entry 

35. deprecate selected entry and invalidate entry  

36. Deprcate action 

<< now  I shall select an entry and click on deprecate button 

37. 

38. 

39. Again if you select the depricated entries . The action buttons will not be shown 

 << Invalidate entry

 

 40. Click on save and open the entry 

 

Thnaks and Regards —

Pradeep Ishwar devadiga

 

 

 

​ In  this blog I have explained creating of an RAP Singleton entity  with multiline edit  functionalities , and also coevered below topics 1.copy action2. Depricate actions3.invalidating entries4.transport organizer Add Copy Action : Helps to create new entries in object page In case we have  lot of fields and we  need to refer existing entries –> and based on that if we want to create new entries we can make use of this action Add Deprecate Action :  If we want to deprecate some entries ( make it invalid )  We can make use of this action  Select to include a deprecate and invalidate action in the generated app Pre requisite : the table must have  a field CONFIGDEPRECATIONCODE with data element CONFIG_DEPRECATION_CODE  Data Consistency Check : select this option to include a validation for the prepare draft action in the generated app which checks the consistency  of the input fields with  Domain with fixed values Foreign keys where @abapCatalog.foreign key.screenCheck : true  If we have foreign key relationship / data element / domain ( suppose we have gender field in the employee table –> we need to maintain it in the Domain level ) We can make use of  this field. << Enable Transport Selection selected  The transport request selection action displayed in edit mode The transport request information is displayed in the header toolbar If the save action is executed without  selected transport request and the transport is mandatory , the action to select a transport request is triggered automatically . After  selecting  a transport request , the save action continues   If the action is not selected , a transport selection is made possible using an action button . This option is selected by default     < Manual :    Select manual to include the action select transport  in the generated app With this action , you can select an existing customizing transport request before saving the configuration changes  < Manual with pre selection  To include the action select transport in the generated app .  With this action , you can select an existing customizing transport request before saving the configuration changes. When the edit action is performed , a customizing transport request is determined automatically < No transport : Select No transport for an app without a transport suitable  fro configurations that are to be adjusted in the productive system   T1. take a custom table say employee @EndUserText.label : ’employee table’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #C
@AbapCatalog.dataMaintenance : #ALLOWED
define table zpd_dt_em {

key client : abap.clnt not null;
key employee_id : zpd_de_emp not null;
@EndUserText.label : ‘First Name’
first_name : abap.char(50);
@EndUserText.label : ‘Last Name’
last_name : abap.char(50);
@EndUserText.label : ‘Department’
department : abap.char(20);
@EndUserText.label : ‘Joining Date’
joining_date : abap.dats;
@EndUserText.label : ‘Active Employee’
is_active : abap.char(1);
@EndUserText.label : ‘Changed By’
changed_by : abap.char(12);
configdeprecationcode : config_deprecation_code;
local_last_changed_at : abp_locinst_lastchange_tstmpl;
changed_at : abp_lastchange_tstmpl;

} 2. Create a LANGUAGE TABLE : Here we need one  entry – so we have selected I_Language and put into the  Where condition so that we get one entry to achieve singleton  3.  select table and click on select generate repository objects4. 5.Delivery class must be C 6. Data maintenance should be allowed7.Also we need to have a data element( domain )8. Provide package9. We have business service definition and binding 10. .Also, we have singleton entity details Singleton entity name ( EmployeeTableAll) Interface view ( Zi_EmployeeTable_S) Projection view  ( Zi_EmployeeTable_S) Key field (SinlgetonID) Draft table (zpd_dt_empl_d_s) 11. Table details 12. we can be able to see the classes  13. we have scenario option    14 Transport selection :   15. activate the service binding16. All the atrifacts will be generated intreface view:@EndUserText.label: ’employee table’
@AccessControl.authorizationCheck: #MANDATORY
@Metadata.allowExtensions: true

define view entity ZI_EmployeeTable1
as select from ZPD_DT_EM
association to parent ZI_EmployeeTable_S1 as _EmployeeTableAll on $projection.SingletonID = _EmployeeTableAll.SingletonID
association [0..*] to I_ConfignDeprecationCodeText as _ConfignDeprecationCodeText on $projection.ConfigDeprecationCode = _ConfignDeprecationCodeText.ConfigurationDeprecationCode

{

key EMPLOYEE_ID as EmployeeId,
FIRST_NAME as FirstName,
LAST_NAME as LastName,
DEPARTMENT as Department,
JOINING_DATE as JoiningDate,
IS_ACTIVE as IsActive,
CHANGED_BY as ChangedBy,
@ObjectModel.text.association: ‘_ConfignDeprecationCodeText’
@Consumption.valueHelpDefinition: [ {

entity: {

name: ‘I_ConfignDeprecationCode’,
element: ‘ConfigurationDeprecationCode’

}

} ]

CONFIGDEPRECATIONCODE as ConfigDeprecationCode,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
@Consumption.hidden: true
LOCAL_LAST_CHANGED_AT as LocalLastChangedAt,
@Semantics.systemDateTime.lastChangedAt: true
CHANGED_AT as ChangedAt,
@Consumption.hidden: true
1 as SingletonID,
_EmployeeTableAll,
case when CONFIGDEPRECATIONCODE = ‘W’ then 2 when CONFIGDEPRECATIONCODE = ‘E’ then 1 else 3 end as ConfigDeprecationCode_Critlty,
_ConfignDeprecationCodeText

} 17.parent entity for interface  Here we have made joined I_language  with employee table because  We want Etag field from the table so that we can get lastchangedat field << one more association is created related to transport – because we need to capture the entries in transport ( I_abap_transportrequesttext) @EndUserText.label: ’employee table Singleton’
@AccessControl.authorizationCheck: #NOT_REQUIRED
@ObjectModel.semanticKey: [ ‘SingletonID’ ]
@UI: {

headerInfo: {
typeName: ‘EmployeeTableAll’

}

}

define root view entity ZI_EmployeeTable_S1

as select from I_Language
left outer join ZPD_DT_EM on 0 = 0
association [0..*] to I_ABAPTransportRequestText as _ABAPTransportRequestText on $projection.TransportRequestID = _ABAPTransportRequestText.TransportRequestID
composition [0..*] of ZI_EmployeeTable1 as _EmployeeTable

{

@UI.facet: [ {

id: ‘ZI_EmployeeTable1’,
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: ’employee table’,
position: 1 ,
targetElement: ‘_EmployeeTable’

} ]

@UI.lineItem: [ {
position: 1

} ]

key 1 as SingletonID,
_EmployeeTable,
@UI.hidden: true
max( ZPD_DT_EM.CHANGED_AT ) as LastChangedAtMax,
@ObjectModel.text.association: ‘_ABAPTransportRequestText’
@UI.identification: [ {

position: 2 ,
type: #WITH_INTENT_BASED_NAVIGATION,
semanticObjectAction: ‘manage’

} ]

@Consumption.semanticObject: ‘CustomizingTransport’
cast( ” as SXCO_TRANSPORT) as TransportRequestID,
_ABAPTransportRequestText

}

where I_Language.Language = $session.system_language 18. child entity holds the singleton filed including  all other fields@EndUserText.label: ’employee table’
@AccessControl.authorizationCheck: #MANDATORY
@Metadata.allowExtensions: true
define view entity ZI_EmployeeTable1

as select from ZPD_DT_EM
association to parent ZI_EmployeeTable_S1 as _EmployeeTableAll on $projection.SingletonID = _EmployeeTableAll.SingletonID
association [0..*] to I_ConfignDeprecationCodeText as _ConfignDeprecationCodeText on $projection.ConfigDeprecationCode = _ConfignDeprecationCodeText.ConfigurationDeprecationCode

{

key EMPLOYEE_ID as EmployeeId,
FIRST_NAME as FirstName,
LAST_NAME as LastName,
DEPARTMENT as Department,
JOINING_DATE as JoiningDate,
IS_ACTIVE as IsActive,
CHANGED_BY as ChangedBy,
@ObjectModel.text.association: ‘_ConfignDeprecationCodeText’
@Consumption.valueHelpDefinition: [ {

entity: {
name: ‘I_ConfignDeprecationCode’,
element: ‘ConfigurationDeprecationCode’

}

} ]

CONFIGDEPRECATIONCODE as ConfigDeprecationCode,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
@Consumption.hidden: true
LOCAL_LAST_CHANGED_AT as LocalLastChangedAt,
@Semantics.systemDateTime.lastChangedAt: true
CHANGED_AT as ChangedAt,
@Consumption.hidden: true
1 as SingletonID,
_EmployeeTableAll,

case when CONFIGDEPRECATIONCODE = ‘W’ then 2 when CONFIGDEPRECATIONCODE = ‘E’ then 1 else 3 end as ConfigDeprecationCode_Critlty,

_ConfignDeprecationCodeText

} 19.behaviour definitionmanaged with additional save implementation in class ZBP_I_EMPLOYEETABLE_S1 unique;
strict;
with draft;
define behavior for ZI_EmployeeTable_S1 alias EmployeeTableAll

draft table ZPD_DT_EM_D_S1
with unmanaged save
lock master total etag LastChangedAtMax
authorization master( global )

{

field ( readonly )
SingletonID;
field ( features : instance )
TransportRequestID;
field ( notrigger )
SingletonID,
LastChangedAtMax;

update;
internal create;
internal delete;

draft action ( features : instance ) Edit with additional implementation;
draft action Activate optimized;
draft action Discard;
draft action Resume;
draft determine action Prepare;

action ( features : instance ) SelectCustomizingTransptReq parameter D_SelectCustomizingTransptReqP result [1] $self;

association _EmployeeTable { create ( features : instance ); with draft; }
validation ValidateTransportRequest on save ##NOT_ASSIGNED_TO_DETACT { create; update; }

side effects {
action SelectCustomizingTransptReq affects $self;

}

}

define behavior for ZI_EmployeeTable1 alias EmployeeTable ##UNMAPPED_FIELD

persistent table ZPD_DT_EM
draft table ZPD_DT_EM_D
etag master LocalLastChangedAt
lock dependent by _EmployeeTableAll
authorization dependent by _EmployeeTableAll

{

field ( mandatory : create )

EmployeeId;
field ( readonly )
SingletonID,
LocalLastChangedAt,
ChangedAt,
ConfigDeprecationCode,
ConfigDeprecationCode_Critlty;
field ( readonly : update )
EmployeeId;

field ( notrigger )
SingletonID,
LocalLastChangedAt,
ChangedAt;

update( features : global );
delete( features : instance );

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

mapping for ZPD_DT_EM

{

EmployeeId = EMPLOYEE_ID;
FirstName = FIRST_NAME;
LastName = LAST_NAME;
Department = DEPARTMENT;
JoiningDate = JOINING_DATE;
IsActive = IS_ACTIVE;
ChangedBy = CHANGED_BY;
ConfigDeprecationCode = CONFIGDEPRECATIONCODE;
LocalLastChangedAt = LOCAL_LAST_CHANGED_AT;
ChangedAt = CHANGED_AT;

}
association _EmployeeTableAll { with draft; }

validation ValidateTransportRequest on save ##NOT_ASSIGNED_TO_DETACT { create; update; delete; }

} 20. service definition@ObjectModel.leadingEntity.name: ‘ZI_EmployeeTable_S’
define service ZUI_EMPLOYEETABLE {
expose ZI_EMPLOYEETABLE_S as EmployeeTableAll;
expose ZI_EMPLOYEETABLE as EmployeeTable;

} 21. service binding22. OUTPUT23. 24. Double click on singleton entity 25. here the edit button  is disabled –because no access to the tbale26.  27. Click on edit We will get the option to create 28.29.Entry will be created30. As soon as you click on save we will get an error As we have not specified any  customizing transport 31. 32. now we data is getting stored but not displaying because of CDS authorization  Select not allowed 33.  now we can be able to see the data34. << Copy Action I  will select the entry ( id 3) And click on copy action ..entry will be copied and we  need to assign new Id And also we can make changes in the perticular entry 35. deprecate selected entry and invalidate entry  36. Deprcate action << now  I shall select an entry and click on deprecate button 37. 38. 39. Again if you select the depricated entries . The action buttons will not be shown  << Invalidate entry  40. Click on save and open the entry  Thnaks and Regards –Pradeep Ishwar devadiga     Read More Technology Blog Posts by Members articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author