Les hiérarchies CDS permettent la représentation efficace des structures parent-enfant directement dans les vues CDS. Avec la syntaxe DEFINE HIERARCHY, les relations récursives comme les structures organisationnelles, les nomenclatures ou les hiérarchies de centres de coûts peuvent être modélisées élégamment et visualisées dans Fiori Elements.
Concept de base
Les données hiérarchiques sont omniprésentes dans SAP. Les hiérarchies CDS offrent une solution native :
| Approche classique | Hiérarchies CDS |
|---|---|
| Logique ABAP récursive | Définition déclarative |
| Calcul manuel des niveaux | Attributs de hiérarchie automatiques |
| Intensif en performance | Optimisé par DB-Pushdown |
| Difficile à maintenir | Défini de manière centralisée |
| Pas d’intégration UI | TreeTable Fiori Elements |
Cas d’utilisation
- Structures organisationnelles : Hiérarchie d’entreprise, centres de coûts, centres de profit
- Nomenclatures (BOM) : Relations matériau-composants
- Hiérarchies de projet : Éléments OTP, lots de travaux
- Catégories : Catégories de produits, classes de documents
- Workflows d’approbation : Relations manager-employé
Modèle de données pour les hiérarchies
Table de base
Une table de hiérarchie typique contient une auto-référence :
@EndUserText.label: 'Cost Center"define table zcostcenter { key client : abap.clnt not null; key cost_center: abap.char(10) not null; parent_cc : abap.char(10); description : abap.char(40); responsible : abap.char(12); valid_from : abap.dats; valid_to : abap.dats;}Vue d’interface
@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Cost Center"define view entity ZI_CostCenter as select from zcostcenter{ key cost_center as CostCenter, parent_cc as ParentCostCenter, description as Description, responsible as Responsible, valid_from as ValidFrom, valid_to as ValidTo}Définition de hiérarchie
La hiérarchie proprement dite est définie avec DEFINE HIERARCHY :
@AccessControl.authorizationCheck: #NOT_REQUIREDdefine hierarchy ZI_CostCenterHierarchy as parent child hierarchy( source ZI_CostCenter child to parent association _Parent start where ParentCostCenter is initial siblings order by CostCenter ){ key CostCenter, ParentCostCenter, Description, Responsible, ValidFrom, ValidTo,
// Attributs de hiérarchie générés automatiquement $node.hierarchy_rank, $node.hierarchy_tree_size, $node.hierarchy_parent_rank, $node.hierarchy_level, $node.hierarchy_is_cycle, $node.hierarchy_is_orphan}Éléments de syntaxe
| Élément | Description |
|---|---|
source | Vue de base avec les données de hiérarchie |
child to parent association | Association vers le noeud parent |
start where | Condition pour les noeuds racine |
siblings order by | Tri des noeuds frères |
L’association _Parent
Dans la vue de base, une association vers le parent doit être définie :
@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Cost Center with Parent Association"define view entity ZI_CostCenter as select from zcostcenter association [0..1] to ZI_CostCenter as _Parent on $projection.ParentCostCenter = _Parent.CostCenter{ key cost_center as CostCenter, parent_cc as ParentCostCenter, description as Description, responsible as Responsible, valid_from as ValidFrom, valid_to as ValidTo,
_Parent}Attributs de hiérarchie ($node)
Les hiérarchies CDS fournissent des attributs calculés automatiquement :
| Attribut | Type | Description |
|---|---|---|
$node.hierarchy_rank | INT8 | Numéro de ligne unique dans l’arbre |
$node.hierarchy_tree_size | INT8 | Nombre de noeuds dans le sous-arbre |
$node.hierarchy_parent_rank | INT8 | Rang du noeud parent |
$node.hierarchy_level | INT4 | Profondeur dans l’arbre (Root = 1) |
$node.hierarchy_is_cycle | INT1 | 1 si le noeud fait partie d’un cycle |
$node.hierarchy_is_orphan | INT1 | 1 si le parent n’existe pas |
Utilisation des attributs
define hierarchy ZI_OrgUnitHierarchy as parent child hierarchy( source ZI_OrgUnit child to parent association _Parent start where ParentOrgUnit is initial siblings order by OrgUnitID ){ key OrgUnitID, ParentOrgUnit, OrgUnitName,
// Pour l'affichage UI $node.hierarchy_level as HierarchyLevel,
// Pour la vérification d'autorisation (tous les subordonnés) $node.hierarchy_tree_size as SubordinateCount,
// Vérifier la qualité des données $node.hierarchy_is_orphan as IsOrphan, $node.hierarchy_is_cycle as HasCycle}Hierarchy Directory
Pour les scénarios complexes avec plusieurs hiérarchies par source de données, un Hierarchy Directory est utilisé :
@AccessControl.authorizationCheck: #NOT_REQUIREDdefine hierarchy ZI_CostCenterHierDir with hierarchy directory ZI_HierarchyDirectory as parent child hierarchy( source ZI_CostCenter child to parent association _Parent start where ParentCostCenter is initial siblings order by CostCenter ){ key CostCenter, ParentCostCenter, Description, $node.hierarchy_level}Vue Hierarchy Directory
Le directory permet la sélection entre différentes versions de hiérarchie :
@AccessControl.authorizationCheck: #NOT_REQUIREDdefine view entity ZI_HierarchyDirectory as select from zhierdir{ key hierarchy_id as HierarchyID, key valid_from as ValidFrom, valid_to as ValidTo, hierarchy_name as HierarchyName, is_active as IsActive}where is_active = 'X"Plusieurs hiérarchies
-- Table pour plusieurs versions de hiérarchie@EndUserText.label: 'Cost Center Hierarchy Versions"define table zcc_hier_versions { key client : abap.clnt not null; key hierarchy_id : abap.char(10) not null; key cost_center : abap.char(10) not null; parent_cc : abap.char(10); valid_from : abap.dats; valid_to : abap.dats;}Alternative : Hiérarchie Parent-to-Child
En plus de child to parent, il existe aussi parent to child :
define hierarchy ZI_BOMHierarchy as parent child hierarchy( source ZI_BillOfMaterial parent to child association _Components start where MaterialType = 'FERT' -- Produits finis comme point de départ siblings order by ItemNumber ){ key Material, key BOMItem, ComponentMaterial, Quantity, Unit, $node.hierarchy_level}Vue de base avec association Child
define view entity ZI_BillOfMaterial as select from zbom association [0..*] to ZI_BillOfMaterial as _Components on $projection.Material = _Components.ParentMaterial{ key material as Material, key bom_item as BOMItem, parent_material as ParentMaterial, component as ComponentMaterial, quantity as Quantity, unit as Unit,
_Components}Consommation dans Fiori Elements
Annotation TreeTable
Pour l’affichage en TreeTable dans Fiori Elements :
@Metadata.layer: #CUSTOMERannotate view ZC_CostCenterHierarchy with{ @UI.lineItem: [{ position: 10, importance: #HIGH }] CostCenter;
@UI.lineItem: [{ position: 20 }] Description;
@UI.lineItem: [{ position: 30 }] Responsible;
@UI.lineItem: [{ position: 40 }] HierarchyLevel;}Projection pour Fiori
@AccessControl.authorizationCheck: #NOT_REQUIRED@Metadata.allowExtensions: true@UI.headerInfo: { typeName: 'Centre de coûts', typeNamePlural: 'Centres de coûts', title.value: 'CostCenter', description.value: 'Description"}define view entity ZC_CostCenterHierarchy as projection on ZI_CostCenterHierarchy{ @UI.facet: [{ id: 'General', type: #IDENTIFICATION_REFERENCE, position: 10 }]
@UI.identification: [{ position: 10 }] key CostCenter,
@UI.identification: [{ position: 20 }] ParentCostCenter,
@UI.identification: [{ position: 30 }] Description,
@UI.identification: [{ position: 40 }] Responsible,
@UI.hidden: true HierarchyLevel,
@UI.hidden: true SubordinateCount}Définition de service
@EndUserText.label: 'Cost Center Hierarchy Service"define service ZUI_COSTCENTER_HIER_O4 { expose ZC_CostCenterHierarchy;}Fonctions de hiérarchie en ABAP
Interroger la hiérarchie
" Lire toute la hiérarchieSELECT * FROM zi_costcenterhierarchy INTO TABLE @DATA(lt_hierarchy).
" Uniquement certains niveauxSELECT * FROM zi_costcenterhierarchy WHERE hierarchy_level <= 3 INTO TABLE @DATA(lt_top_levels).
" Trouver les subordonnés d'un noeudSELECT * FROM zi_costcenterhierarchy WHERE hierarchy_parent_rank = @lv_parent_rank INTO TABLE @DATA(lt_children).Déterminer le chemin vers la racine
METHOD get_path_to_root. DATA: lt_path TYPE TABLE OF zi_costcenterhierarchy.
" Lire le noeud actuel SELECT SINGLE * FROM zi_costcenterhierarchy WHERE costcenter = @iv_cost_center INTO @DATA(ls_current).
WHILE ls_current IS NOT INITIAL. APPEND ls_current TO lt_path.
IF ls_current-parentcostcenter IS INITIAL. EXIT. " Racine atteinte ENDIF.
SELECT SINGLE * FROM zi_costcenterhierarchy WHERE costcenter = @ls_current-parentcostcenter INTO @ls_current. ENDWHILE.
rt_path = lt_path.ENDMETHOD.Agrégation de sous-arbre
" Somme sur tous les centres de coûts subordonnésSELECT h~costcenter, SUM( b~amount ) AS total_budget FROM zi_costcenterhierarchy AS h INNER JOIN zbudget AS b ON h~costcenter = b~cost_center WHERE h~hierarchy_rank BETWEEN @lv_start_rank AND @lv_start_rank + @lv_tree_size - 1 GROUP BY h~costcenter INTO TABLE @DATA(lt_aggregated).Hiérarchie avec tranches temporelles
Pour les hiérarchies dépendantes du temps :
@AccessControl.authorizationCheck: #CHECKdefine view entity ZI_CostCenterTemporal as select from zcostcenter_temp association [0..1] to ZI_CostCenterTemporal as _Parent on $projection.ParentCostCenter = _Parent.CostCenter and $projection.ValidFrom <= _Parent.ValidTo and $projection.ValidTo >= _Parent.ValidFrom{ key cost_center as CostCenter, key valid_from as ValidFrom, valid_to as ValidTo, parent_cc as ParentCostCenter, description as Description,
_Parent}define hierarchy ZI_CostCenterHierTemporal as parent child hierarchy( source ZI_CostCenterTemporal child to parent association _Parent start where ParentCostCenter is initial siblings order by CostCenter ){ key CostCenter, key ValidFrom, ValidTo, ParentCostCenter, Description, $node.hierarchy_level}where ValidFrom <= $session.system_date and ValidTo >= $session.system_dateVérifier la qualité des données
Trouver les noeuds orphelins
@AccessControl.authorizationCheck: #NOT_REQUIREDdefine view entity ZI_OrphanCostCenters as select from ZI_CostCenterHierarchy{ CostCenter, ParentCostCenter, Description}where $node.hierarchy_is_orphan = 1Détecter les cycles
@AccessControl.authorizationCheck: #NOT_REQUIREDdefine view entity ZI_CyclicCostCenters as select from ZI_CostCenterHierarchy{ CostCenter, ParentCostCenter, Description}where $node.hierarchy_is_cycle = 1Conseils de performance
Index sur le champ parent
-- Index pour une navigation hiérarchique rapide@AbapCatalog.sqlViewAppendName: 'ZSQLV_CC"define table zcostcenter { key client : abap.clnt not null; key cost_center: abap.char(10) not null; @AbapCatalog.foreignKey.screenCheck: false parent_cc : abap.char(10); ...}-- Index secondaire sur parent_cc recommandéVues de hiérarchie matérialisées
Pour les très grandes hiérarchies, une matérialisation peut être pertinente :
" Écrire la hiérarchie dans une table tamponMETHOD materialize_hierarchy. DELETE FROM zhier_buffer WHERE hierarchy_id = @iv_hierarchy_id.
SELECT * FROM zi_costcenterhierarchy INTO TABLE @DATA(lt_hierarchy).
MODIFY zhier_buffer FROM TABLE @lt_hierarchy.ENDMETHOD.Limitations
| Limitation | Contournement |
|---|---|
| Max. 100 niveaux de hiérarchie | Généralement suffisant |
| Pas d’agrégation dans Hierarchy | Vue séparée pour l’agrégation |
| Performance avec >1 Mio. noeuds | Matérialisation ou filtre |
Bonnes pratiques
| Recommandation | Justification |
|---|---|
child to parent pour les hiérarchies standard | Navigation plus intuitive vers le haut |
parent to child pour les nomenclatures | Explosion de haut en bas |
| Implémenter la vérification d’orphelins | Assurer la qualité des données |
| Index sur le champ parent | Optimisation de la performance |
| Tranches temporelles pour les données temporelles | Permettre les vues historiques |
Utiliser les attributs $node pour l’UI | Visualiser niveau et taille |
| Hierarchy Directory pour les versions | Gérer plusieurs versions de hiérarchie |
Résumé
Les hiérarchies CDS offrent :
- Définition déclarative : Structure de hiérarchie définie directement dans CDS
- Attributs automatiques : Niveau, rang, taille sans calcul
- Qualité des données : Détection des orphelins et cycles intégrée
- Intégration Fiori : Support natif TreeTable
- Performance : Optimisation par DB-Pushdown
Avec les hiérarchies CDS, des structures parent-enfant complexes peuvent être modélisées élégamment et interrogées efficacement - des structures organisationnelles aux nomenclatures multiniveaux.
Sujets connexes
- Fondamentaux des vues CDS - Concepts de base de CDS
- CDS Access Control (DCL) - Autorisations pour les hiérarchies
- CDS Entity Buffer - Optimisation de la performance