Le besoin métier est d’ajouter trois nouveaux champs dans les Installations Techniques. Une installation technique est un Équipement avec un type particulier. C’est donc au niveau Équipement PM qu’il va falloir travailler.
Dans cet article, nous aborderons la plupart des étapes requises pour répondre à ce besoin, avant même l’ajout proprement dit des champs sur les écrans standard.
Pour cela, les notes OSS https://launchpad.support.sap.com/#/notes/103855 et https://launchpad.support.sap.com/#/notes/92970 vont nous être utiles.
Ainsi pour commencer, nous allons créer les tables de valeurs et de textes relatives aux nouvelles zones.
On va donc créer un domaine générique que nous utiliserons pour les descriptions des valeurs (aka les textes). Ensuite, on crée un domaine et un élément de données pour chaque nouvelle zone et son texte.
Ici on ne réutilise pas d’éventuels existants car nous parlons d’un nouveau besoin client bien spécifique à ce projet.
Une fois toutes ces briques prêtes, il ne reste plus qu’à créer les trois couples [table principale / table de textes]. Les principales ne contiendront que le mandant ainsi que l’identifiant du champ relatif ; les tables de textes contiendront en plus : dans la clé une zone de langue, et hors de la clé une zone de texte descriptif (c’est là que notre domaine générique créé plus haut intervient).
Remarque : il faut bien gérer les clés externes des zones des tables afin que les tables de textes soient bien considérées en tant que telles.
Remarque 2 : penser aussi à créer une vue de gestion de chaque couple [table de valeurs / table de textes] et à en générer les écrans de maintenance SM30
.
Maintenant que les objets existent dans le Dictionnaire de Données, on va pouvoir les utiliser pour étendre le standard existant. En l’occurrence, c’est dans les données de l’Équipement que nous allons ajouter trois nouveaux champs.
Pour cela, direction SE11
→ EQUI
. On défile et on voit qu’il y a un customer-include CI_EQUI
– à noter que ce CI est également présent dans la structure ISTRU_EQ
. Si le CI n’existe pas, il faut le créer (attention, il doit être mentionné dans la liste des champs de EQUI
, même s’il n’existe pas ; je ne parle pas ici de modifier EQUI
pour l’y ajouter). Penser aussi à indiquer la clé externe pour automatiser l’aide à la recherche sur les écrans.
Par convention, on préfixe les champs spécifiques par ZZ
, pour être sûr de les distinguer des champs standard (se référer à vos éventuelles conventions de nommage, sur ce point). En effet, il est possible d’avoir des champs standard commençant par un simple Z
, alors autant le doubler pour éviter les soucis.
Renseigner le type de données des champs supplémentaires selon ce que nous avons créé à l’étape précédente, et activer (penser à saisir la catégorie d’extention, en gardant en tête que nous étendons une table, donc il y a des restrictions).
Il faut à présent répéter cette opération sur le customer-include CI_EQUI_U
. Comme souvent avec SAP, cette structure supplémentaire contient les mêmes champs que la précédente, mais sous forme de flag permettant de les mettre à jour ou non (par exemple, ceci permettra au système de savoir si une valeur envoyée vide correspond à une suppression de valeur ou à un champ à ignorer).
L’étape suivante consiste à gérer l’affichage de ces champs dans les transactions standard de manipulation des Équipements, comme indiqué dans les notes OSS susmentionnées.
Pour cela, SAP met à disposition des user-exits avec un exit d’écran. Dans mon cas, pas besoin de gérer quoi que ce soit dans les transactions CMOD
/ SMOD
car il y avait déjà un premier projet pour ces exits.
On retrouve donc directement les modules fonction EXIT_SAPLITO0_001
et EXIT_SAPLITO0_002
, utilisés respectivement pour transférer les données de la base vers l’écran et inversement.
Le premier module fonction retournera le numéro du sous-écran qui sera utilisé au sein de l’écran standard. Par défaut, le standard utiliser le numéro 1000
. Inutile pour nous de changer cette valeur.
Dans ce module fonction, nous allons nous contenter d’instancier un objet de classe locale (voir ci-dessous pour l’utilisation de cette classe), en lui transmettant les données de l’équipement concerné et le mode de traitement.
La classe locale sera utilisée pour gérer l’affichage des zones à l’écran, mais aussi le contrôle des saisies utilisateur. Avant d’instancier, nous vérifierons que le type de l’équipement correspond bien à ce que nous attendons.
Nous faisons une parenthèse pour décrire l’écran : celui-ci contient un cadre, dans lequel nous insérons 6 zones : pour chaque champ (donc 3), un label, une zone de saisie et le texte descriptif relatif à la valeur saisie. Ces 6 zones et le cadre auront une valeur identique dans leur champ group1 afin de pouvoir les manipuler ensemble.
On attaque à présent le PBO
de cet écran. Comme il s’agit d’un sous-écran, il est inutile de gérer le moindre PF-STATUS
. C’est dans le PBO
que nous allons masquer toutes les zones d’écran qui ont le group1 donné si et seulement si l’équipement n’a pas le bon type.
Et si le type est le bon, la classe locale sera responsable d’afficher ces champs en lecture ou modification selon le mode de traitement. Une autre méthode est appelée pour charger le texte descriptif de chacune des valeurs des zones ajoutées à l’écran.
Avant de parler du PAI
, attardons-nous un moment sur les aides à la recherche. Si tout a été fait correctement au niveau Dictionnaire de Données, les zones écrans ajoutées auront automatiquement une aide à la recherche basées sur leur table de valeurs. Cependant, lorsque l’utilisateur sélectionne une ligne depuis ces aides à la recherche, nous voulons que la zone du descriptif soit mise à jour en conséquence. Pour cela, il faut gérer l’aide à la recherche manuellement, par l’intermédiaire du MODULE ON VALUE-REQUEST
du dynpro. Nous en créons un par champ, sous la forme de :
field nom_de_la_zone_écran module nom_du_module.
Chacun de ces modules va appeler une méthode de la classe locale. Le principe sera le suivant : sélectionner en base les valeurs possibles (comme le ferait le standard automatiquement), puis appeler le module fonction F4IF_INT_TABLE_VALUE_REQUEST
en lui trasmettant :
- le champ de la table ciblée à récupérer
- le nom du programme et le numéro du dynpro concerné
- la zone écran ciblée
- la table des valeurs pré-sélectionnées
- la table de résultat du choix utilisateur.
Une fois que l’on a le choix spécifié par l’utilisateur, on met à jour les données de la classe locale – notamment en rechargeant le texte descriptif. Enfin, on utilise le module fonction DYNP_VALUES_UPDATE
pour que le texte descriptif soit envoyé à l’écran.
Nous ne pouvons pas attendre que le PBO le fasse car dans le processus d’aide à la recherche, celui-ci n’est pas appelé.
Nous pouvons maintenant nous préoccuper du PAI. Il se découpera en deux parties : contrôle des données saisies et conservation de ces dernières.
Pour le contrôle, nous allons créer un module par zones, en utilisant :
chain.
field nom_de_la_zone_écran module nom_du_module.
endchain.
Ceci permettra de bloquer toute saisie en cas d’erreur sauf sur la zone concernée, jusqu’à sa correction.
Chacun des modules de contrôle va appeler une nouvelle méthode de la classe locale, dont le principe est simple : vérifier en base de données l’existence de la valeur saisie, et en cas d’erreur : mettre le focus sur la zone écran correspondante puis afficher un message d’erreur.
La deuxième partie du PAI est elle aussi encapsulée dans une méthode de la classe locale. Elle se contente de stocker dans un attribut d’instance les valeurs saisies à l’écran.
L’ultime étape sera la transmission des données au standard SAP.
Pour cela, le user-exit EXIT_SAPLITO0_002
sera appelé automatiquement aux moments adéquats (par exemple après le PAI
, ou lors de la sauvegarde, …).
Il ne nous reste plus qu’à utiliser les données stockées dans la classe locale pour les transmettre aux paramètres de sortie de cet exit, sans oublier les flags de mise à jour. Nous mettrons abap_true
pour les trois zones écrans que nous avons ajoutées.
Conclusion
Voilà, ceci clôt ce long article un peu fouilli qui part de la création de tables de textes, en passant par l’extension de structures standard, pour finir sur des user-exits, tout en abordant un screen-exit.
J’espère avoir été suffisamment clair, même si je sais n’être pas 100% exhaustif. Compte-tenu du nombre de sujets survolés, j’ai choisi de ne pas illustrer le texte par des captures d’écran ou des extraits de code. Il ressort que chaque étape prise séparément est relativement simple.
Il est à noter que 99% de la logique est gérée à travers la classe locale – que j’appelle locale, mais qui est globale au groupe de fonctions ; c’est pourquoi elle est utilisable par les deux modules fonctions user-exits.