Intégrer un planificateur glisser-déposer dans une page Business Central
Par Fred Morin ·
Aussi en anglais : Embedding a drag-and-drop scheduler in a Business Central page
Business Central est très bon pour bien des choses. Un planificateur visuel, en glisser-déposer, n'en fait pas partie. Il n'y a aucun écran dans l'application de base où on voit ses gens et son équipement côte à côte, où on prend un rendez-vous avec la souris pour le déposer sur une autre ressource ou à une autre heure.
Dans une extension verticale que j'ai construite pour une clinique vétérinaire, cet écran était le cœur du projet. La clinique devait répartir vétérinaires, assistants et équipement sur les rendez-vous toute la journée, et le faire à partir de listes et de fiches, ça ne marche tout simplement pas. Alors j'en ai construit un. Il roulait dans une page Business Central, avait l'air natif, et les utilisateurs n'avaient jamais à sortir de BC pour planifier leur journée.
Cet article explique comment il était monté. Rien n'y est propre au vétérinaire : la même approche convient à toute entreprise qui répartit des ressources, du service sur la route à la construction en passant par la location d'équipement.
Illustration refaite pour cet article. L'original était un travail pour un client, donc pas de captures d'écran.
Une démo autonome que j'ai refaite pour montrer l'idée. Ce n'est pas le système du client : ici, le tableau vit dans l'extension, alors que l'original utilisait une application web à part.
Ce que faisait le planificateur
Un calendrier de jour et de semaine, avec les ressources en haut (vétérinaires, assistants, équipement) et les heures sur le côté. Les utilisateurs pouvaient :
- glisser un rendez-vous à une nouvelle heure,
- le déposer sur une autre ressource,
- le supprimer,
tout ça visuellement, à la souris. Chaque changement était renvoyé dans Business Central.
L'architecture, en quatre morceaux
1. L'extension : tables, pages et champs ajoutés
Le côté AL était une extension ordinaire. De nouvelles tables pour les données de planification, de nouvelles pages pour travailler avec, et des champs ajoutés aux tables standard par des extensions de table, pour que l'information de planification vive juste à côté des enregistrements auxquels elle appartient. Chaque réservation était liée à une ligne de commande de vente portant une ressource, ce qui gardait le travail planifié et la ligne facturable ensemble.
Rien d'exotique, mais ça compte : le planificateur n'avait pas sa propre copie de « qui est une ressource » ou « qu'est-ce qu'un rendez-vous ». Business Central restait le système de référence pour ces données d'affaires.
2. Des pages API pour les nouveaux champs
Le planificateur devait lire et écrire ces champs ajoutés. Les API standard de Business Central ne connaissent pas les champs qu'ajoute votre extension, alors je les ai exposés par mes propres pages API. Un exemple simplifié, pas le code d'origine :
page 50101 "Schedule Entries API"
{
PageType = API;
APIPublisher = 'contoso';
APIGroup = 'scheduling';
APIVersion = 'v1.0';
EntityName = 'scheduleEntry';
EntitySetName = 'scheduleEntries';
SourceTable = "Schedule Entry";
DelayedInsert = true;
ODataKeyFields = SystemId;
layout
{
area(Content)
{
repeater(Records)
{
field(id; Rec.SystemId) { Editable = false; }
field(resourceNo; Rec."Resource No.") { }
field(startDateTime; Rec."Start DateTime") { }
field(endDateTime; Rec."End DateTime") { }
}
}
}
}
Cette page apparaît sous votre propre éditeur, groupe et version dans l'adresse de l'API, à côté des points d'accès standard :
https://api.businesscentral.dynamics.com/v2.0/{tenant}/{environment}/api/contoso/scheduling/v1.0/companies({companyId})/scheduleEntries
Un glisser-déposer dans le planificateur devenait un PATCH sur une entrée. Une nouvelle réservation, un POST ; une suppression, un DELETE.
3. Le planificateur lui-même : une application web sur Azure
Le planificateur était une application web Blazor hébergée sur Azure. C'était un morceau d'un système plus large qui comprenait aussi une application mobile Xamarin, celle que les vétérinaires utilisaient sur la route. Le côté Azure utilisait Azure SQL et Azure Storage pour les données temporaires, entre les deux : la synchronisation avec l'application mobile, la copie de travail de l'horaire, etc. Business Central restait le système de référence, et tout passait par ces API pour l'atteindre.
Garder le planificateur hors de Business Central était voulu. Une interface riche et interactive comme celle-là se construit beaucoup plus facilement comme une vraie application web que dans les contraintes d'une page BC. Et comme le seul contrat entre les deux côtés était l'API, chacun pouvait évoluer de son bord.
4. Le mettre dans une page Business Central
C'est ce qui le rendait natif. Business Central permet à une extension d'afficher du contenu web dans une page grâce à un contrôle intégré (control add-in), un petit composant JavaScript qui roule dans la page. Le mien chargeait l'adresse du planificateur et prenait tout l'espace disponible : pour l'utilisateur, c'était juste une autre page de Business Central.
Encore une fois simplifié. Le contrôle intégré :
controladdin "Scheduler Host"
{
StartupScript = 'scripts/startup.js';
Scripts = 'scripts/host.js';
RequestedHeight = 700;
MinimumHeight = 400;
VerticalStretch = true;
HorizontalStretch = true;
event ControlReady();
procedure LoadUrl(Url: Text);
}
Son JavaScript, qui crée un cadre (iframe) dans le conteneur du contrôle :
// startup.js
Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('ControlReady', []);
// host.js
function LoadUrl(url) {
var frame = document.createElement('iframe');
frame.src = url;
frame.style.border = 'none';
frame.style.width = '100%';
frame.style.height = '100%';
document.getElementById('controlAddIn').appendChild(frame);
}
Et la page qui l'héberge :
page 50100 "Resource Scheduler"
{
PageType = Card;
ApplicationArea = All;
UsageCategory = Tasks;
Caption = 'Resource Scheduler';
layout
{
area(Content)
{
usercontrol(Scheduler; "Scheduler Host")
{
ApplicationArea = All;
trigger ControlReady()
var
Setup: Record "Scheduler Setup";
begin
if not Setup.Get() or not Setup.Enabled then
exit;
CurrPage.Scheduler.LoadUrl(Setup."Scheduler URL");
end;
}
}
}
}
Une page de configuration : un interrupteur et une adresse
L'adresse du planificateur n'était pas codée en dur. L'extension avait sa propre page de paramètres avec deux choses : un interrupteur pour activer le planificateur, et l'adresse de l'application.
table 50100 "Scheduler Setup"
{
fields
{
field(1; "Primary Key"; Code[10]) { }
field(2; Enabled; Boolean) { Caption = 'Enabled'; }
field(3; "Scheduler URL"; Text[250])
{
Caption = 'Scheduler URL';
ExtendedDatatype = URL;
}
}
keys
{
key(PK; "Primary Key") { Clustered = true; }
}
}
C'est un petit détail, mais il rendait le tout beaucoup plus facile à vivre. Pointer un sandbox vers une instance de test du planificateur, fermer la fonction pour une compagnie qui ne s'en servait pas, déménager l'application à une nouvelle adresse : tout ça devenait un changement de paramètre, pas une nouvelle version de l'extension.
Le résultat
Ça a fonctionné sans faute. Pour être franc, j'ai été un peu surpris que ça marche aussi bien : le planificateur avait l'air et se comportait comme une partie native de Business Central, et les utilisateurs le traitaient comme tel.
Si vous construisez quelque chose du genre aujourd'hui
Quelques points à penser d'avance. Ce sont des conseils généraux, pas une liste de problèmes que j'ai eus :
- L'authentification. Prévoyez comment l'application web s'authentifie auprès de l'API de Business Central (une inscription d'application Microsoft Entra avec accès de service à service est la voie habituelle pour Business Central en ligne), et comment l'utilisateur dans le cadre est identifié.
- Les règles d'affichage en cadre. L'application web doit accepter d'être affichée dans la page de Business Central. Vérifiez ses en-têtes
Content-Security-Policy(frame-ancestors) etX-Frame-Options. - Le contexte. Décidez ce que le contrôle passe à l'application : la compagnie, l'utilisateur, peut-être l'enregistrement d'où la page a été ouverte. Des paramètres dans l'adresse, c'est le plus simple.
- Une seule source de vérité. Soyez clair sur les données qui vivent dans Business Central et celles qui vivent dans la base de l'application, pour que les deux ne se contredisent jamais.
- La taille.
VerticalStretchetHorizontalStretchlaissent le contrôle remplir la page. Sans eux, un planificateur coincé dans une petite boîte perd l'essentiel de sa valeur.
Pas juste pour les vétérinaires
L'écran a été construit pour une clinique vétérinaire, mais rien n'y est vétérinaire : les ressources en haut, les heures sur le côté, des rendez-vous qu'on déplace à la souris. Toute entreprise qui envoie des gens et de l'équipement sur des travaux a le même besoin, et le même trou dans Business Central.
Vous avez un écran Business Central que votre équipe contourne au lieu de s'en servir? Parlons-en.