Imported from personamanagmentlayer/pcl (
stdlib/domains/sap-expert/SKILL.md). Install upstream withnpx skills add personamanagmentlayer/pcl --skill sap-expert. Copyright stays with the author.
SAP Expert
Core Concepts
SAP Ecosystem
- SAP ERP - Enterprise Resource Planning suite (ECC, S/4HANA)
- SAP HANA - In-memory database platform
- SAP Fiori - Modern UX layer with responsive apps
- SAP Business Suite - Finance, HR, Supply Chain, Manufacturing modules
- SAP BTP - Business Technology Platform (cloud services)
ABAP Development
- ABAP Objects - Object-oriented programming in ABAP
- CDS Views - Core Data Services for data modeling
- AMDP - ABAP Managed Database Procedures
- ALV - ABAP List Viewer for reports
- BAPIs - Business Application Programming Interfaces
- RFCs - Remote Function Calls for integration
Integration Technologies
- OData Services - RESTful APIs for SAP data
- IDoc - Intermediate Documents for data exchange
- SOAP/RFC - Web services and remote function calls
- SAP PI/PO - Process Integration/Orchestration
- SAP Gateway - OData and REST API framework
Implementation Examples
ABAP Report with ALV Grid
*&---------------------------------------------------------------------*
*& Report Z_EMPLOYEE_REPORT
*&---------------------------------------------------------------------*
REPORT z_employee_report.
TABLES: pa0001. " Organizational Assignment
TYPES: BEGIN OF ty_employee,
pernr TYPE pa0001-pernr,
ename TYPE pa0001-ename,
orgeh TYPE pa0001-orgeh,
plans TYPE pa0001-plans,
stell TYPE pa0001-stell,
END OF ty_employee.
DATA: gt_employee TYPE TABLE OF ty_employee,
gs_employee TYPE ty_employee,
go_alv TYPE REF TO cl_salv_table.
SELECT-OPTIONS: s_pernr FOR pa0001-pernr.
START-OF-SELECTION.
" Fetch employee data
SELECT pernr ename orgeh plans stell
FROM pa0001
INTO TABLE gt_employee
WHERE pernr IN s_pernr
AND endda = '99991231'
AND begda <= sy-datum.
IF sy-subrc = 0.
" Display ALV
TRY.
cl_salv_table=>factory(
IMPORTING
r_salv_table = go_alv
CHANGING
t_table = gt_employee ).
" Enable all standard functions
go_alv->get_functions( )->set_all( abap_true ).
" Optimize column width
go_alv->get_columns( )->set_optimize( abap_true ).
" Display
go_alv->display( ).
CATCH cx_salv_msg INTO DATA(lx_msg).
MESSAGE lx_msg TYPE 'E'.
ENDTRY.
ELSE.
MESSAGE 'No data found' TYPE 'I'.
ENDIF.
CDS View with Associations
@AbapCatalog.sqlViewName: 'ZSALESORDERV'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order with Customer'
define view Z_SalesOrder
as select from vbak as SalesOrder
association [1..1] to kna1 as _Customer
on $projection.CustomerId = _Customer.kunnr
{
key SalesOrder.vbeln as SalesOrderId,
SalesOrder.erdat as CreatedDate,
SalesOrder.erzet as CreatedTime,
SalesOrder.ernam as CreatedBy,
SalesOrder.kunnr as CustomerId,
SalesOrder.netwr as NetValue,
SalesOrder.waerk as Currency,
// Associations
_Customer
}
where SalesOrder.vbtyp = 'C' // Sales order
OData Service Implementation
CLASS zcl_odata_employee_dpc_ext DEFINITION
PUBLIC
INHERITING FROM zcl_odata_employee_dpc
CREATE PUBLIC.
PUBLIC SECTION.
METHODS /iwbep/if_mgw_appl_srv_runtime~get_entityset
REDEFINITION.
METHODS /iwbep/if_mgw_appl_srv_runtime~get_entity
REDEFINITION.
METHODS /iwbep/if_mgw_appl_srv_runtime~create_entity
REDEFINITION.
ENDCLASS.
CLASS zcl_odata_employee_dpc_ext IMPLEMENTATION.
METHOD /iwbep/if_mgw_appl_srv_runtime~get_entityset.
DATA: lt_employees TYPE TABLE OF zcl_odata_employee_mpc=>ts_employee,
ls_employee TYPE zcl_odata_employee_mpc=>ts_employee.
CASE iv_entity_name.
WHEN 'EmployeeSet'.
" Apply filters from URI
DATA(lt_filter) = io_tech_request_context->get_filter( )->get_filter_select_options( ).
" Fetch data
SELECT pernr, ename, orgeh, plans
FROM pa0001
INTO CORRESPONDING FIELDS OF TABLE lt_employees
WHERE endda = '99991231'
AND begda <= sy-datum.
" Return data
copy_data_to_ref(
EXPORTING
is_data = lt_employees
CHANGING
cr_data = er_entityset ).
ENDCASE.
ENDMETHOD.
METHOD /iwbep/if_mgw_appl_srv_runtime~create_entity.
DATA: ls_employee TYPE zcl_odata_employee_mpc=>ts_employee,
ls_pa0001 TYPE pa0001.
" Get payload
io_data_provider->read_entry_data(
IMPORTING
es_data = ls_employee ).
" Create employee record
ls_pa0001-pernr = ls_employee-pernr.
ls_pa0001-ename = ls_employee-ename.
ls_pa0001-begda = sy-datum.
ls_pa0001-endda = '99991231'.
" Call BAPI to create employee
CALL FUNCTION 'BAPI_EMPLOYEE_ENQUEUE'
EXPORTING
number = ls_pa0001-pernr
EXCEPTIONS
OTHERS = 1.
IF sy-subrc = 0.
INSERT pa0001 FROM ls_pa0001.
COMMIT WORK.
ENDIF.
" Return created entity
er_entity = ls_employee.
ENDMETHOD.
ENDCLASS.
SAP Fiori App (SAPUI5)
sap.ui.define(
[
'sap/ui/core/mvc/Controller',
'sap/ui/model/json/JSONModel',
'sap/m/MessageToast',
],
function (Controller, JSONModel, MessageToast) {
'use strict';
return Controller.extend('com.example.employee.controller.Main', {
onInit: function () {
// Initialize model
var oModel = new JSONModel();
this.getView().setModel(oModel);
// Load employee data
this._loadEmployees();
},
_loadEmployees: function () {
var that = this;
var oDataModel = this.getView().getModel('odata');
oDataModel.read('/EmployeeSet', {
success: function (oData) {
that.getView().getModel().setProperty('/employees', oData.results);
},
error: function (oError) {
MessageToast.show('Failed to load employees');
},
});
},
onEmployeeSelect: function (oEvent) {
var oItem = oEvent.getParameter('listItem');
var oContext = oItem.getBindingContext();
var sEmployeeId = oContext.getProperty('Pernr');
// Navigate to detail view
this.getOwnerComponent().getRouter().navTo('detail', {
employeeId: sEmployeeId,
});
},
onCreateEmployee: function () {
var oDialog = this.byId('createDialog');
oDialog.open();
},
onSaveEmployee: function () {
var oView = this.getView();
var oModel = oView.getModel('odata');
var oEntry = {
Pernr: oView.byId('pernrInput').getValue(),
Ename: oView.byId('enameInput').getValue(),
Orgeh: oView.byId('orgehInput').getValue(),
};
oModel.create('/EmployeeSet', oEntry, {
success: function () {
MessageToast.show('Employee created successfully');
this._loadEmployees();
this.byId('createDialog').close();
}.bind(this),
error: function () {
MessageToast.show('Failed to create employee');
},
});
},
});
}
);
RFC Function Module
FUNCTION z_get_employee_details.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(IV_PERNR) TYPE PERNR_D
*" EXPORTING
*" VALUE(ES_EMPLOYEE) TYPE ZS_EMPLOYEE
*" EXCEPTIONS
*" EMPLOYEE_NOT_FOUND
*"----------------------------------------------------------------------
SELECT SINGLE pernr ename orgeh plans stell
FROM pa0001
INTO CORRESPONDING FIELDS OF es_employee
WHERE pernr = iv_pernr
AND endda = '99991231'
AND begda <= sy-datum.
IF sy-subrc <> 0.
RAISE employee_not_found.
ENDIF.
" Enrich with additional data
SELECT SINGLE persg persk
FROM pa0001
INTO (es_employee-persg, es_employee-persk)
WHERE pernr = iv_pernr
AND endda = '99991231'.
ENDFUNCTION.
Best Practices
Development Standards
- Use naming conventions (Z*/Y* for custom objects)
- Implement proper error handling with exceptions
- Follow ABAP coding guidelines (clean code)
- Use CDS views for data modeling in S/4HANA
- Leverage ABAP unit tests for quality assurance
- Document code with proper comments
Performance Optimization
- Use database views instead of nested SELECTs
- Implement buffering for frequently accessed tables
- Use SAP HANA-specific features (AMDP, CDS)
- Optimize ALV displays with field catalogs
- Implement lazy loading for large datasets
- Use parallel processing for batch jobs
Integration Patterns
- Prefer OData services for modern integrations
- Use IDoc for asynchronous batch processing
- Implement RFC for synchronous real-time calls
- Apply proper authorization checks
- Implement retry logic and error handling
- Use SAP Gateway for REST APIs
Security Best Practices
- Implement authorization objects properly
- Use secure network communication (SNC)
- Encrypt sensitive data at rest and in transit
- Apply principle of least privilege
- Regular security patches and updates
- Implement audit logging
Anti-Patterns
Code Smells
- Hard-coded values instead of customizing
- Missing error handling and exceptions
- SELECT * statements without field list
- Nested loops with database access
- Missing authorization checks
- Modifications to standard SAP objects
Design Issues
- Tight coupling between modules
- God objects with too many responsibilities
- Direct table access instead of BAPIs
- Synchronous processing for long-running tasks
- Missing transaction management
- No separation of concerns
Integration Mistakes
- Point-to-point integrations without middleware
- Missing idempotency in service calls
- No versioning for APIs
- Inadequate error handling in RFC calls
- Synchronous calls where async would be better
- Missing retry mechanisms
Resources
Official Documentation
- SAP Help Portal - Comprehensive documentation
- SAP API Business Hub - API references
- ABAP Keyword Documentation - Language reference
- SAP Fiori Design Guidelines - UX patterns
Learning Platforms
- SAP Learning Hub - Official training
- openSAP - Free online courses
- SAP Community - Forums and blogs
- SAP Press - Technical books
Tools & Extensions
- ABAP Development Tools (ADT) - Eclipse-based IDE
- SAP GUI - Classic interface
- SAP Business Application Studio - Cloud IDE
- SAP Cloud Connector - On-premise connectivity
Community Resources
- ABAP Forums - Q&A
- SAP Blogs - Technical articles
- GitHub SAP Samples - Code examples
- SAP CodeJam - Hands-on events