Floorplan Manager – FPM pour les intimes

Bonjour à tous,

Avant-propos :
Cet article se base sur mon expérience limitée et autodidacte sur le sujet. Les exemples proposés ne respectent donc pas forcément les bonnes pratiques telles que préconisées par SAP et n’utilisent probablement pas toute la puissance du framework.

Depuis quelques mois maintenant, pour les besoins d’un client, j’ai pu découvrir un framework SAP nommé Floorplan Manager (FPM). Je ne connaissais pas du tout, et j’ai donc dû monter en compétences aussi vite que possible.
Alors, FPM, qu’est-ce donc que cela, à quoi cela sert-il, et surtout… Comment. Ça. Marche ?

FPM, qu’est-ce que c’est ?
Comme dit en introduction, c’est un framework proposé par SAP. C’est bien joli, mais dit comme ça, ça n’aide absolument pas. Alors je vais tenter de préciser quelque peu.
Historiquement, les interfaces utilisateurs chez SAP, c’était les dynpros. Des écrans moches, avec des inputs un peu partout et peu de possibilités de mise en page ou de personnalisation.

Dynpro SAP classique
Dynpro SAP classique - et encore, j'aurais pu trouver pire

Puis vinrent les BSP (Business Server Pages) qui permettaient de générer des pages Web (donc du code HTML) à partir de l’ABAP. Le tout avec une base de pattern MVC. Et un framework maison pour abstraire l’HTML (appelé HTMLB). J’ai eu la chance de travailler dessus il y a quelques années, et c’était assez intéressant pour moi qui ai une base de développeur Web. Je me serais presque cru en train de faire du PHP côté serveur, et de l’HTML, JS et CSS côté client (je n’ai pas touché à l’HTMLB).
Cette technologie fut assez rapidement abandonnée, au profit des Web Dynpros.

Web Dynpro ABAP
Web Dynpro ABAP

Un peu plus de sexitude (toute relative, on parle de SAP quand même), un peu plus de possibilité de mise en page, mais pas d’accès au code source HTML généré (je me trompe peut-être sur ce point, mon expérience là-dessus étant très limitée).

Et le FPM arriva. Les quelques mois passés à travailler dessus me permettent d’en faire la définition suivante. Le framework FPM est un framework qui propose un ensemble de blocs plus ou moins génériques à intégrer dans des pages web. C’est assez simpliste, et ne permet toujours pas de s’en faire une idée. Mais visuellement, le résultat et le suivant.

Extrait d'un écran FPM
Extrait d'un écran FPM

Sur cet exemple, l’amélioration graphique ne saute pas forcément aux yeux, mais revenons à FPM à proprement parler.

FPM repose sur des composants Web Dynpro (WD) génériques et réutilisables. Par exemple, un composant WD pour afficher une liste (c’est-à-dire un tableau, c’est-à-dire l’équivalent d’un ALV par exemple) ; ou encore un formulaire (c’est-à-dire une liste de champs, saisissables ou non) ; ou encore un gestionnaire d’onglets ; ou encore etc.
Basiquement, on crée une application FPM de type OIF (object instance Floorplan, c’est le seul que j’ai utilisé parmi les trois types disponibles que sont OIF, OVP [overview page] et GAF [guided activity floorplan]). Puis on configure l’ensemble des blocs qui feront partie de cette application.
Là encore, c’est une définition basique.

Plus en détails, mais sans être exhaustif, une telle application possèdera une zone d’en-tête permettant d’afficher des informations, des boutons (exemple : sauvegarder), puis une succession des blocs configurés. Mais encore, l’application peut afficher des popups, gérer des évènements, basculer vers d’autres applications, etc.
Et tout cela est configurable sans code particulier. Bien évidemment, la logique métier derrière est à implémenter, mais la mise en forme est configurable en ligne.
De plus, chaque utilisateur peut personnaliser son interface en masquant ou cachant des champs (sans pour autant pouvoir déplacer des blocs).

J’apporte un court pas-à-pas pour créer une application FPM OIF un peu plus bas dans cet article.

Les ressources sur le net
Avant de passer aux choses sérieuses et concrètes, ci-dessous une liste de liens vers des ressources concernant SAP FPM.

Pas à pas basique de création d’une application FPM
La première étape est de définir l’apparence souhaitée de notre application. Je vais me baser sur un exemple réel que j’ai mis en place pour un client.
Cette application a pour but d’afficher sur une seule page web trois listes différentes, dont la structure de données est cependant identique. Avec des actions dans certaines cellules. C’est listes sont des listes de commandes passées ou reçues.
En haut de la page, une zone de saisie dite « recherche rapide » qui permet de saisir directement une numéro de commande et de naviguer vers celle-ci si elle existe.
Le résultat attendu ressemble à quelque chose comme :

FPM - Listes résultat
FPM - Listes résultat

Ici seule la première liste est alimentée, environnement de Développement oblige. Mais les trois fonctionnent de la même façon.

Nous avons donc la zone de titre « Factures émises ».
La zone de recherche rapide.
Et trois listes. Ces listes étant de structures identiques, elles reposeront sur le même composant générique que nous allons voir plus tard…

Création de l’application Web Dynpro
Tout d’abord il faut créer une application Web Dynpro.
Pour cela, direction la transaction SE80 puis charger le composant WD « FPM_OIF_COMPONENT ». En effet, notre application sera un OIF (object instance floorplan). Clic-droit sur le composant chargé puis Créer > Application WD. Saisir un petit nom ainsi qu’un descriptif, et valider. Puis sauvegarder. L’application est créée et dispose d’une URL.

Configuration de l’application WD
Avec le framework FPM, les deux mot-clés sont réutilisabilité et généricité. Aussi, une même application WD peut être utilisée de différentes façons. Chacun de ses « modes d’accès » est une configuration. Il va donc falloir créer une nouvelle configuration pour notre application. Pour cela, toujours dans SE80, avec le composant WD « FPM_OIF_COMPONENT » chargé, rechercher l’application créée plus haut, puis clic-droit « Afficher / modifier configuration ». Une page Web s’ouvre permettant de saisir l’application et sa configuration. Laisser l’application pré-remplie et renseigner une configuration à créer. Puis cliquer sur le bouton de création.
On arrive alors sur la première étape de configuration de notre application. Une simili arborescence devrait apparaître avec une première ligne reprenant l’application, et une seconde « IDR_USAGE ». Comme dit précédemment, avec FPM, tout est affaire de configuration.
Il y a donc une configuration pour l’application. Puis une configuration pour le composant WD sur lequel repose notre application. Il va donc falloir renseigner une configuration pour le composant FPM_OIF_COMPONENT. De même pour le composant FPM_IDR_COMPONENT (qui correspond à une des zones de l’écran que nous détaillerons plus tard).
Sous la liste, un ensemble de paramètre est à saisir pour personnaliser le comportement et l’affichage de l’application de façon générale. Par exemple, l’auto-suggestion des zones de saisie, le FAVICON, l’alignement des labels… Il est à noter que la plupart des paramètres peuvent être surdéfinis pour chacun des blocs qui composeront l’application.

Configuration de FPM_OIF_COMPONENT
Renseigner un nom de configuration pour le composant puis cliquer sur le bouton de création. Nous entrons dans le vif du sujet, car c’est là que nous disposons les composants de l’application. Du moins la première couche.
Là encore, des paramètres globaux peuvent être saisis, tels que la façon d’afficher la zone de messages (qui sera ici affichée uniquement si au moins un message est enregistré).

FPM - Application - Composant OIF - Options générales
FPM - Application - Composant OIF - Options générales

La zone « Schéma instance d’objet » permet de lister les différents blocs que nous voulons en première couche (première couche car certains peuvent en contenir d’autres).
Nous trouverons donc ici quatre blocs FPM :

  • FPM_COMPOSITE_UIBB : ce bloc permet d’en contenir plusieurs, nous verrons par la suite ce que nous allons y mettre
  • trois fois le bloc FPM_LIST_UIBB_ATS : un pour chaque liste à créer.
FPM - Application - Composant OIF - Liste des UIBB
FPM - Application - Composant OIF - Liste des UIBB

On comprend ici qu’un bloc FPM est aussi appelé UIBB pour User Interface Building Block. Et on voit bien qu’un même composant, ou bloc, ou UIBB peut apparaître plusieurs fois. La différence tiendra dans… sa configuration bien sûr. Chaque bloc se voit affecter une configuration, qu’il faudra créer, de la même manière que pour l’application ou le composant OIF.
On en reste là pour le moment sur cet écran de configuration, mais nous y reviendrons par la suite.
Occupons-nous du premier bloc, le composite UIBB.

Configuration du bloc composite
On renseigne donc une configuration pour ce bloc puis il suffit de cliquer sur « Configurer UIBB » pour basculer vers l’écran de configuration du bloc (après avoir validé la création de la configuration).

FPM - Application - Composite
FPM - Application - Composite

On le voit sur la capture d’écran, un seul bloc compose notre élément composite. Mais on pourrait en mettre plusieurs. Le bloc composite permet d’ajuster la mise en forme des différents blocs. Ici j’ai utilisé le composite uniquement pour centrer le bloc de la recherche rapide (si quelqu’un a une méthode plus simple pour le faire…). L’élément utilisé dans le composite est un bloc de type FORM. Un formulaire donc, car nous voulons que l’utilisateur puisse saisir un numéro de commande.

Configuration du bloc FORM
On commence à savoir comment faire : une fois que l’on a déterminé où afficher le bloc dans le composite, il suffit de saisir le composant WD UIBB à utiliser puis de renseigner une configuration. Créer cette configuration amènera à l’écran de paramétrage de cette dernière.
Rassurez-vous, nous allons bientôt devoir coder un peu. En effet, le composant FPM_FORM_UIBB est, comme les autres, un composant générique. C’est-à-dire qu’il permet d’afficher des éléments sous formes de zones de formulaires, mais évidemment, il ne connaît pas ces zones. Sinon, il perdrait tout son caractère générique.
C’est là que nous introduisons le concept de Classe de Feeder (ou Feeder Class). Une feeder class, comme son nom l’indique, est une classe dont le rôle sera d’alimenter le bloc FORM (ou autre bloc générique). Une même classe peut être utilisée par plusieurs blocs. Mais par soucis de clareté, de simplicité et de maintenance, je préconise d’en créer une par bloc – une telle classe est en effet très liée au bloc, il serait donc risqué de mélanger tout ça.

Feeder class
Comme dit ci-dessus, la feeder class alimente le bloc générique. Mais cela fonctionne dans les deux sens. Elle va envoyer au bloc la liste des champs disponibles pour générer un formulaire, elle va envoyer les données à afficher, mais elle va également récupérer les données saisies et gérer les évènements sur le bloc auquel elle est rattachée.
Pour que le système FPM comprenne qu’une classe est une feeder class, elle doit implémenter l’interface associée au bloc ciblé, ici IF_FPM_GUIBB_FORM. Ceci fait, la classe gagne des méthodes, dont les plus utilisées (du moins par moi) sont :

  • FLUSH : c’est la première méthode appelée lors du traitement du bloc. Elle permet de récupérer les saisies utilisateurs (avant traitement de l’action proprement dite). Généralement je l’utilise pour stocker en attribut privée d’instance les données saisies, afin d’en disposer dans les méthodes exécutées après.
  • PROCESS_EVENT : son nom l’indique, elle permet de traiter l’évènement déclenché dans l’écran de l’application. Vous avez bien lu, dans l’écran de l’application. Cela signifie que cette méthode sera appelée même si l’action utilisateur concerne un autre bloc de l’application. Dans notre cas, si l’utilisateur clique sur un bouton de la deuxième liste, eh bien le PROCESS_EVENT du feeder de la zone de recherche rapide sera déclenché. Pour éviter qu’une classe traite un évènement qui devrait être traité par une autre feeder class, la méthode PROCESS_EVENT dispose d’un paramètre d’entrée, IV_RAISED_BY_OWN_UI, qui permet de savoir si c’est le bloc de cette feeder qui est concerné ou pas. Cela permet notamment de ne pas exécuter plusieurs fois le même traitement. On évite ainsi des erreurs et on gagne en vitesse d’exécution.
    Dans le contexte du FORM de recherche rapide, un seul évènement est traité : l’exécution de la recherche après saisie d’un numéro de commande. Notre code ABAP va donc consister à contrôler que la saisie utilisateur correspond à une commande existante. Le cas échéant, celle-ci sera affichée dans une nouvelle fenêtre. Sinon, un message d’erreur est affiché.
  • GET_DATA : il s’agit de la dernière méthode appelée avant l’affichage du bloc. C’est ici que l’on détermine les valeurs qui seront affichées à l’écran. Il peut s’agir de valeurs par défauts par exemple. Ou simplement du résultat d’une recherche opérée. Le GET_DATA permet également de rendre des champs saisissables ou au contraire de les bloquer en lecture seule.
    Dans le contexte du FORM de recherche rapide, aucune valeur par défaut n’est à renseigner.
  • GET_DEFINITION : cette méthode n’est appelée que lors de la génération de l’écran. Donc typiquement une seule fois pour l’application. Elle est aussi appelée lors de la configuration du composant UIBB associée. Elle charge la dénition du bloc. C’est-à-dire qu’elle se charge de transmettre au système FPM la liste des champs disponibles pour le bloc (ainsi que leur match-code et autres caractéristiques de base). Il s’agit, plus ou moins, du Field Catalog. Cette méthode permet également de définir les évènements possibles pour ce bloc.
    Dans le contexte du FORM de recherche rapide, la liste des champs possibles sera issue de la structure VBAK (même si réellement un seul champ sera utilisé) et l’on définit un évènement pour déclencher la recherche, auquel on associe une icone (pour les cas où cet évènement serait affecté à un bouton, ce qui n’est pas le cas pour notre application).

On le comprend aisément, ce premier bloc UIBB FORM est simple dans son utilisation et dans son fonctionnement.

Nous revenons à présent à sa configuration. Il nous faut définir les champs de formulaire qui seront saisissables par l’utilisateur. La classe de feeder nous fournit une liste de champs, et nous sélectionnons dans cette liste ceux qui nous intéressent.

FPM - Application - FORM - éléments
FPM - Application - FORM - éléments

Une fois ceci fait, nous pouvons passer au bloc suivant, la première liste. Je n’en détaillerai qu’une dans cette article, car les trois sont identiques, sauf dans la sélection des données.

Configuration du bloc liste ATS
Il s’agit d’un composant LIST_ATS. C’est la dernière évolution (à ma connaissance) des GUIBB de liste, la précédente étant notée obsolète.
Comme précédemment, on lui saisit une configuration, et c’est parti pour la… configuration du bloc.
Le processus est proche de celui du FORM, à ceci près que l’on traite une liste, donc une table. Celle-ci peut avoir un titre, mais la configuration permet surtout de définir une taille minimale, le type de pagination, le comportement en cas de liste vide, la sélection (multiple ou non, ou non sélectionnable), l’activation d’évènements ou non, la possibilité de personnaliser ou non l’affichage (exemple : variante d’affichage et/ou d’ordre des colonnes par utilisateur), etc.
Ensuite, on définit la liste, l’ordre, le libellé et le type d’affichage des données de la liste, colonne par colonne.
Dans le contexte de notre application, la liste est en lecture seule, mais certaines colonnes sont affichées sous forme de bouton, l’une est même affichée en tant que lien vers une URL (mailto:).
Bien sûr, comme pour le UIBB FORM, c’est une classe de feeder qui va permettre tout ceci.

Feeder Class de liste
Notre application ayant trois listes quasi identiques, j’ai créé une classe abstraite mère dont chaque feeder class de liste va hériter pour redéfinir certaines méthodes clés.
Ici, notre feeder class doit implémenter l’interface IF_FPM_GUIBB_LIST. Mais les méthodes seront les mêmes que pour la feeder class de formulaire.

  • FLUSH : simple stockage en attribut de classe des données de l’écran. Commune à toutes les listes de notre application.
  • PROCESS_EVENT : ici on traite tous les évènements possibles de la liste. Ces évènements sont :
    • le rafraîchissement des données,
    • l’affichage de la commande sélectionnée
    • l’affichage des pièces jointes à une commande
    • l’afffichage de l’historique des modifications
    • la copie d’une commande
    • la modification du statut d’une commande
  • GET_DATA : c’est ici que les trois listes vont diverger les unes des autres. En effet, leur fonctionnement est le même, mais ce n’est pas le cas de leur contenu. La méthode GET_DATA, comme son nom l’indique, va se charger de récupérer les informations à afficher dans la liste. Il est donc normal que la logique de sélection de ces données varie d’une liste à l’autre.
  • GET_DEFINITION : cette méthode a le même fonctionnement que celui décrit pour la feeder class du bloc formulaire. À savoir : déclarer la structure de données de la liste (les colonnes mises à disposition, quelles soient affichées ou non à l’écran), ainsi que les actions possibles. Dans notre cas de liste, on va utiliser un mécanisme intéressant.
    On l’a vu, cette méthode permet de communiquer au moteur FPM la structure de la liste. Son fieldcatalog en d’autres termes. Cela veut dire, l’affichage de toutes les colonnes, identique à chaque ligne. Cependant, le besoin est ici de pouvoir afficher des boutons selon le contenu de la ligne. Par exemple, la ligne 1 aura un bouton d’action, la ligne 2 ne l’aura pas. La méthode GET_DEFINITION permet de faire ceci, assez facilement. Il suffit pour cela d’alimenter la table interne de sortie ET_FIELD_DESCRIPTION.
    Cette table interne prendra une entrée par colonne disponible de la liste. Nous pouvons lui préciser une aide à la recherche, une liste de valeurs possibles, définir le champ comme technique, et… préciser les noms des colonnes de référence pour ce champ.
    Je précise : une zone d’écran peut être en lecture seule et/ou obligatoire et/ou invisible. Il y a deux façons de procéder à cette définition. D’abord en utilisant les champs directs [READ_ONLY | MANDATORY | VISIBILITY]. Dans ce cas, il suffit de valoriser par exemple READ_ONLY = abap_true pour que le champ soit en lecture seule. L’autre méthode, est d’utiliser les champs référents [READ_ONLY | MANDATORY | VISIBILITY]_REF. Faire cela, c’est dire au moteur d’attendre l’exécution du bloc FPM pour déterminer l’état de la cellule, en fonction de la valeur du champ référent. Par exemple, si notre structure de liste est la suivante : [ID, NAME, NAME_RO]. Dans l’entrée de ET_FIELD_DESCRIPTION pour le champ NAME, on affectera la valeur NAME_RO au champ READ_ONLY_REF.
    À l’exécution, toutes les lignes dont NAME_RO = abap_true auront le champ NAME en lecture seule. Les autres seront saisissables par l’utilisateur (pour peu que la configuration du bloc le permette).

Ce mini tutorial en forme de présentation de FPM (ou l’inverse) touche à sa fin, car nous avons abordé ce que je crois être les notions essentielles du framework :
– les blocs UIBB réutilisables
– la configuration en ligne de ces blocs
– les feeder class qui permettent de lier la logique métier aux blocs génériques, via des configurations.

Si le temps me le permet, je tâcherai de revenir détailler certains points qui méritent plus de précision. N’hésitez pas à poser vos questions en commentaire, j’essaierai de répondre dans la limite de mes moyens.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *