Developer Extensibility : integrer une logique personnalisee dans les applications standard

Catégorie
ABAP Cloud
Publié
Auteur
Johannes

La Developer Extensibility permet aux developpeurs ABAP d’etendre les applications SAP standard avec leur propre code. Cet article presente les differents concepts d’extensibilite et leur application pratique.

Qu’est-ce que la Developer Extensibility ?

La Developer Extensibility est le modele d’extension pour les developpeurs ABAP dans ABAP Cloud :

AspectDeveloper Extensibility
Public cibleDeveloppeurs ABAP
OutilsADT (Eclipse), Business Application Studio
LangageABAP Cloud (Restricted Language Version)
FlexibiliteLiberte de programmation complete
DisponibiliteS/4HANA Cloud, BTP ABAP Environment

Developer vs. Key User Extensibility

CritereDeveloper ExtensibilityKey User Extensibility
ComplexiteArbitrairement complexeLogique simple
CodeABAP CloudLow-Code/No-Code
TestsTests unitaires possiblesManuel
VersionnementGit/gCTSAutomatique
PerformanceOptimisablePredeterminee
APIsToutes les APIs ReleasedTriggers predefinis

Extension Include Structures

Les Extension Includes permettent d’ajouter ses propres champs aux tables SAP standard.

Concept

SAP definit des structures d’extension dans les tables de base de donnees standard :

-- Table standard avec Extension Include
define table I_SALESORDER {
key SalesOrder : vbeln;
SalesOrderType : auart;
SoldToParty : kunnr;
...
-- Extension Include Point
include structure ZZ1_SALESORDER_EXT;
}

Creer sa propre Extension

Etape 1 : Creer une Extension Structure

@EndUserText.label: 'Sales Order Extension Fields"
@AbapCatalog.enhancement.category: #EXTENSIBLE_ANY
define structure zz1_salesorder_ext {
zz1_project_id : char20;
zz1_priority : /dmo/priority;
zz1_external_ref : char35;
}

Etape 2 : Activer dans ADT

  1. Clic droit sur la structure → “Include in Extension Include”
  2. Selectionner la table cible (par ex. I_SALESORDER)
  3. Activer

Exemple : Champs supplementaires pour commande client

@EndUserText.label: 'Sales Order Customer Extensions"
@AbapCatalog.enhancement.category: #EXTENSIBLE_ANY
define structure zz1_sd_order_ext {
" Reference projet pour le suivi des commandes
zz1_project_id : char20;
" Indicateur de priorite
zz1_priority_code : char1;
" Reference systeme externe
zz1_external_ref : char35;
" Date specifique au client
zz1_customer_date : dats;
}

Acces aux champs d’extension

" Dans une CDS View
define view entity ZI_SalesOrderExt
as select from I_SalesOrder
{
key SalesOrder,
SoldToParty,
" Champs d'extension directement disponibles
zz1_project_id as ProjectId,
zz1_priority_code as PriorityCode,
zz1_external_ref as ExternalReference,
zz1_customer_date as CustomerDate
}

Implementations BAdI

Les BAdIs (Business Add-Ins) sont le mecanisme d’extension central dans ABAP Cloud.

Trouver un BAdI

Rechercher les BAdIs disponibles dans ADT :

  1. Open Development Object (Ctrl+Shift+A)
  2. Rechercher *BADI* dans le domaine souhaite
  3. Ou dans la documentation : SAP API Business Hub

Categories BAdI importantes

CategorieExempleUtilisation
ValidationBADI_SD_SALES_DOC_CHECKVerification des entrees
DeterminationBADI_SD_PRICINGDerivation de valeurs
ModificationBADI_SD_ITEM_DATAModification de donnees
AuthorizationBADI_AUTH_CHECKAutorisations

Implementer un BAdI

Etape 1 : Creer une Enhancement Implementation

" Enhancement Implementation : ZEI_SALES_ORDER_CHECK
@ObjectModel.leadingEntity.name: 'I_SALESORDER"

Etape 2 : Creer la classe BAdI

CLASS zcl_sales_order_validation DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_sd_sales_doc_check.
ENDCLASS.
CLASS zcl_sales_order_validation IMPLEMENTATION.
METHOD if_sd_sales_doc_check~check.
" Validation du numero de projet
LOOP AT it_order_items ASSIGNING FIELD-SYMBOL(<item>).
" Le projet doit etre renseigne pour certains types de commande
IF <item>-order_type = 'ZPR"
AND <item>-zz1_project_id IS INITIAL.
" Ajouter une erreur a la table de messages
APPEND VALUE #(
msgty = 'E"
msgid = 'ZSD"
msgno = '001"
msgv1 = <item>-sales_order_item
) TO ct_messages.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Exemple : BAdI de determination de prix

CLASS zcl_custom_pricing DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_sd_pricing_exit.
ENDCLASS.
CLASS zcl_custom_pricing IMPLEMENTATION.
METHOD if_sd_pricing_exit~calculate_price.
" Remise specifique au client basee sur la priorite
DATA(lv_priority) = is_item_data-zz1_priority_code.
CASE lv_priority.
WHEN 'A'.
" Clients VIP : 5% de remise supplementaire
cv_discount_percent = cv_discount_percent + 5.
WHEN 'B'.
" Clients importants : 2.5% de remise supplementaire
cv_discount_percent = cv_discount_percent + '2.5'.
WHEN OTHERS.
" Pas de modification
ENDCASE.
ENDMETHOD.
ENDCLASS.

Implementer un BAdI avec filtre

Les filtres permettent une activation ciblee :

CLASS zcl_sales_check_fr DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_sd_sales_doc_check.
PRIVATE SECTION.
" Actif uniquement pour certains pays
CONSTANTS: c_country_filter TYPE land1 VALUE 'FR'.
ENDCLASS.
CLASS zcl_sales_check_fr IMPLEMENTATION.
METHOD if_sd_sales_doc_check~check.
" Verification uniquement pour les commandes francaises
IF is_header-country <> c_country_filter.
RETURN.
ENDIF.
" Verification numero TVA francais
IF is_header-vat_registration IS INITIAL
AND is_header-customer_type = 'B2B'.
APPEND VALUE #(
msgty = 'E"
msgid = 'ZSD"
msgno = '002"
msgv1 = 'Numero de TVA requis pour les clients B2B"
) TO ct_messages.
ENDIF.
ENDMETHOD.
ENDCLASS.

CDS View Extensions

Les CDS View Extensions etendent les CDS Views standard avec ses propres champs et logiques.

Extend View Entity

extend view entity I_SalesOrder with ZE_SalesOrder_Ext {
" Ajouter des champs d'extension
zz1_project_id as ProjectId,
zz1_priority_code as PriorityCode,
zz1_customer_date as CustomerDate,
" Champ calcule
case zz1_priority_code
when 'A' then 'Priorite haute"
when 'B' then 'Priorite moyenne"
when 'C' then 'Priorite basse"
else 'Non classifie"
end as PriorityText,
" Association vers Custom Entity
_CustomProject
}

Etendre avec des associations

extend view entity I_SalesOrder with ZE_SalesOrder_Project {
" Association vers les donnees projet
association [0..1] to ZI_Project as _CustomProject
on $projection.zz1_project_id = _CustomProject.ProjectId,
" Exposer des champs de l'association
_CustomProject.ProjectName,
_CustomProject.ProjectManager,
_CustomProject.PlannedEndDate
}

Exemple : Extension de notation client

@EndUserText.label: 'Business Partner Extension - Customer Rating"
extend view entity I_BusinessPartner with ZE_BP_CustomerRating {
" Champs d'extension pour la notation client
zz1_customer_rating as CustomerRating,
zz1_last_rating_date as LastRatingDate,
" Statut de notation calcule
case
when zz1_customer_rating >= 8 then 'Or"
when zz1_customer_rating >= 5 then 'Argent"
when zz1_customer_rating >= 3 then 'Bronze"
else 'Standard"
end as RatingCategory,
" Association vers l'historique de notation
association [0..*] to ZI_CustomerRatingHistory as _RatingHistory
on $projection.BusinessPartner = _RatingHistory.BusinessPartner,
_RatingHistory
}

Access Control pour les Extensions

@EndUserText.label: 'Access Control for Sales Order Extension"
@MappingRole: true
define role ZE_SalesOrder_Auth {
grant select on ZE_SalesOrder_Ext
where ( SalesOrganization ) =
aspect pfcg_auth( V_VBAK_VKO, VKORG, ACTVT = '03' )
and ( zz1_priority_code ) =
aspect pfcg_auth( Z_PRIORITY, PRIORITY, ACTVT = '03' );
}

Behavior Extensions

Les Behavior Extensions etendent le comportement RAP des entites standard.

Definir une Behavior Extension

extension using interface zif_rap_sales_order
implementation in class zbp_sales_order_ext unique;
extend behavior for I_SalesOrderTP alias SalesOrder {
// Ajouter ses propres validations
validation validateProjectId on save {
field zz1_project_id;
}
// Ses propres Determinations
determination setDefaultPriority on modify {
field SalesOrderType;
}
// Ses propres Actions
action ( features : instance ) escalatePriority result [1] $self;
// Side Effects pour les champs d'extension
side effects {
field zz1_project_id affects field ProjectName;
}
}

Implementer la Behavior Extension

CLASS zbp_sales_order_ext DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES zif_rap_sales_order.
ENDCLASS.
CLASS zbp_sales_order_ext IMPLEMENTATION.
METHOD zif_rap_sales_order~validateProjectId.
" Lire les IDs projet
READ ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
FIELDS ( zz1_project_id SalesOrderType )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>).
" Pour les commandes projet, l'ID projet doit etre renseigne
IF <order>-SalesOrderType = 'ZPR"
AND <order>-zz1_project_id IS INITIAL.
APPEND VALUE #(
%tky = <order>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Une commande projet necessite un ID projet' )
%element-zz1_project_id = if_abap_behv=>mk-on
) TO reported-salesorder.
APPEND VALUE #( %tky = <order>-%tky ) TO failed-salesorder.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD zif_rap_sales_order~setDefaultPriority.
" Lire le type de commande
READ ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
FIELDS ( SalesOrderType zz1_priority_code )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
" Priorite par defaut basee sur le type de commande
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>)
WHERE zz1_priority_code IS INITIAL.
DATA(lv_priority) = COND char1(
WHEN <order>-SalesOrderType = 'ZEXP' THEN 'A' " Express
WHEN <order>-SalesOrderType = 'ZSTD' THEN 'C' " Standard
ELSE 'B' " Defaut Medium
).
MODIFY ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
UPDATE FIELDS ( zz1_priority_code )
WITH VALUE #( (
%tky = <order>-%tky
zz1_priority_code = lv_priority
) ).
ENDLOOP.
ENDMETHOD.
METHOD zif_rap_sales_order~escalatePriority.
" Lire les donnees actuelles
READ ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
FIELDS ( zz1_priority_code )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>).
" Augmenter la priorite d'un niveau
DATA(lv_new_priority) = COND char1(
WHEN <order>-zz1_priority_code = 'C' THEN 'B"
WHEN <order>-zz1_priority_code = 'B' THEN 'A"
ELSE 'A' " Deja priorite maximale
).
MODIFY ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
UPDATE FIELDS ( zz1_priority_code )
WITH VALUE #( (
%tky = <order>-%tky
zz1_priority_code = lv_new_priority
) ).
" Retourner le resultat
APPEND VALUE #(
%tky = <order>-%tky
%param = CORRESPONDING #( <order> )
) TO result.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Feature Control pour les Actions d’Extension

METHOD zif_rap_sales_order~get_instance_features.
READ ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
FIELDS ( zz1_priority_code OverallStatus )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>).
" Escalade uniquement si pas deja Priorite A
" et commande non terminee
DATA(lv_escalate_enabled) = COND #(
WHEN <order>-zz1_priority_code = 'A"
THEN if_abap_behv=>fc-o-disabled
WHEN <order>-OverallStatus = 'C"
THEN if_abap_behv=>fc-o-disabled
ELSE if_abap_behv=>fc-o-enabled
).
APPEND VALUE #(
%tky = <order>-%tky
%action-escalatePriority = lv_escalate_enabled
) TO result.
ENDLOOP.
ENDMETHOD.

Exemple pratique : Extension complete

Un exemple complet pour l’extension des commandes clients :

1. Extension Include Structure

@EndUserText.label: 'Sales Order Extension Structure"
@AbapCatalog.enhancement.category: #EXTENSIBLE_ANY
define structure zz1_salesorder_ext {
zz1_project_id : char20;
zz1_priority : char1;
zz1_approval_status : char2;
zz1_approved_by : syuname;
zz1_approved_at : timestampl;
}

2. CDS View Extension

extend view entity I_SalesOrderTP with ZE_SalesOrderTP_Ext {
zz1_project_id as ProjectId,
zz1_priority as Priority,
zz1_approval_status as ApprovalStatus,
zz1_approved_by as ApprovedBy,
zz1_approved_at as ApprovedAt,
case zz1_approval_status
when 'AP' then 'Approuve"
when 'RJ' then 'Rejete"
when 'PN' then 'En attente"
else 'Non requis"
end as ApprovalStatusText
}

3. Behavior Extension

extension implementation in class zbp_so_approval_ext unique;
extend behavior for I_SalesOrderTP alias SalesOrder {
validation validateApprovalRequired on save {
field NetAmount;
}
determination setApprovalPending on modify {
create;
}
action ( features : instance ) approve;
action ( features : instance ) reject;
}

4. Classe d’implementation

CLASS zbp_so_approval_ext IMPLEMENTATION.
METHOD validateApprovalRequired.
" Les commandes de plus de 10 000 EUR doivent etre approuvees
READ ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
FIELDS ( NetAmount zz1_approval_status TransactionCurrency )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>).
IF <order>-NetAmount > 10000
AND <order>-zz1_approval_status <> 'AP'.
APPEND VALUE #(
%tky = <order>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Les commandes de plus de 10 000 { <order>-TransactionCurrency
} necessitent une approbation| )
) TO reported-salesorder.
APPEND VALUE #( %tky = <order>-%tky ) TO failed-salesorder.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD setApprovalPending.
" Mettre les nouvelles commandes au-dessus du seuil sur 'En attente"
READ ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
FIELDS ( NetAmount )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>)
WHERE NetAmount > 10000.
MODIFY ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
UPDATE FIELDS ( zz1_approval_status )
WITH VALUE #( (
%tky = <order>-%tky
zz1_approval_status = 'PN' " En attente
) ).
ENDLOOP.
ENDMETHOD.
METHOD approve.
MODIFY ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
UPDATE FIELDS ( zz1_approval_status zz1_approved_by zz1_approved_at )
WITH VALUE #( FOR key IN keys (
%tky = key-%tky
zz1_approval_status = 'AP"
zz1_approved_by = cl_abap_context_info=>get_user_technical_name( )
zz1_approved_at = cl_abap_context_info=>get_system_time( )
) ).
" Retourner le resultat
READ ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
result = CORRESPONDING #( lt_orders ).
ENDMETHOD.
METHOD reject.
MODIFY ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
UPDATE FIELDS ( zz1_approval_status zz1_approved_by zz1_approved_at )
WITH VALUE #( FOR key IN keys (
%tky = key-%tky
zz1_approval_status = 'RJ"
zz1_approved_by = cl_abap_context_info=>get_user_technical_name( )
zz1_approved_at = cl_abap_context_info=>get_system_time( )
) ).
READ ENTITIES OF i_salesordertp IN LOCAL MODE
ENTITY SalesOrder
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
result = CORRESPONDING #( lt_orders ).
ENDMETHOD.
ENDCLASS.

Bonnes pratiques

Developpement d’extensions

RecommandationJustification
Espaces de noms propresZZ1_, YY1_ pour la compatibilite SAP Cloud
Base sur interfacesTestabilite et decouplage
DocumentationDocumenter chaque extension
Tests unitairesTests automatises pour les BAdIs

A eviter

❌ Modifications directes de tables sans Extension Include
❌ Valeurs codees en dur dans les BAdIs
❌ Trop d'extensions sur une entite
❌ Extensions sans gestion d'erreurs
❌ Logique lourde en performance dans les Determinations

Versionnement

Structure de commit recommandee :
├── feat: Add project tracking extension fields
├── feat: Implement validation BAdI for projects
├── feat: Add CDS view extension with project assoc
└── feat: Implement approval workflow behavior ext

Migration et mise a niveau

De Key User a Developer Extensibility

  1. Conserver les Custom Fields : les structures de donnees sont conservees
  2. Remplacer la Custom Logic : BAdI ABAP au lieu de Key User Logic
  3. Tester : s’assurer que les deux implementations ne sont pas en conflit
  4. Desactiver : desactiver la Key User Logic apres migration reussie

Securite des mises a niveau

  • Les Extension Includes sont stables aux mises a niveau
  • Les interfaces BAdI peuvent changer → consulter les Release Notes
  • Verifier les CDS View Extensions lors de modifications de la vue de base

Sujets connexes