Implémentation pas à pas (exemple complet)
Objectif : créer une API personnalisée CalculateOpportunityMargin qui calcule la marge d’une opportunité à partir du montant, du coût et d’options de remise.
Pré requis
- Rôle avec droits de personnalisation Dataverse.
- Solution non gérée dédiée (ex.
Marlk_SalesCore). - SDK et Plugin Registration Tool installés.
- Projet C# .NET 6+ avec
Microsoft.CrmSdk.CoreAssemblies.
Définir l’API dans la solution
- Dans Power Apps > Solutions, ouvrir
Marlk_SalesCore. - Nouvel élément > API personnalisée.
- Paramètres principaux :
- Nom logique:
mar_custom_CalculateOpportunityMargin - Display name:
Calculate Opportunity Margin - Binding type:
Global - Execution mode:
Synchronous - Allowed custom processing step type:
SyncOnly - Is Function:
false(action)
- Nom logique:
- Paramètres d’entrée :
opportunityid(Guid, Required)applydiscount(Boolean, Optional)
- Paramètres de sortie :
marginamount(Money)marginrate(Decimal)
- Enregistrer.
Implémenter la logique métier (plug-in handler)
Créer une classe C# qui lit l’opportunité, calcule la marge, et alimente la réponse.
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
public class CalculateOpportunityMarginHandler : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var service = factory.CreateOrganizationService(context.UserId);
// Lecture des paramètres d’entrée
var input = context.InputParameters;
if (!input.Contains("opportunityid"))
throw new InvalidPluginExecutionException("opportunityid is required");
var oppId = (Guid)input["opportunityid"];
var applyDiscount = input.Contains("applydiscount") && (bool)input["applydiscount"];
// Récupère l’opportunité
var cols = new ColumnSet("totalamount", "new_totalcost", "discountpercentage");
var opp = service.Retrieve("opportunity", oppId, cols);
var revenue = opp.GetAttributeValue<Money>("totalamount")?.Value ?? 0m;
var cost = opp.GetAttributeValue<Money>("new_totalcost")?.Value ?? 0m;
var discountPct = opp.Contains("discountpercentage") ? (decimal)opp["discountpercentage"] : 0m;
if (applyDiscount && discountPct > 0)
revenue = revenue * (1 - (discountPct / 100m));
if (revenue < 0 || cost < 0)
throw new InvalidPluginExecutionException("Invalid revenue/cost values");
var margin = revenue - cost;
var rate = revenue == 0 ? 0 : margin / revenue;
// Sorties
context.OutputParameters["marginamount"] = new Money(margin);
context.OutputParameters["marginrate"] = rate;
}
}
Bonnes pratiques implémentées : validation stricte des entrées, sélection minimale de colonnes, erreurs explicites.
Enregistrer le handler sur l’API
- Compiler l’assembly. Versionner (ex.
1.0.0.0). - Ouvrir Plugin Registration Tool > se connecter.
- Register > Register New Assembly > sélectionner le DLL.
- Add Step > cibler le Message =
mar_custom_CalculateOpportunityMargin(Custom API) > Stage =PostOperation> Synchronous. - Enregistrer.
Tester avec Postman
- Authentification OAuth 2.0 sur l’API Dataverse.
- Requête POST :
POST https://<org>.crm.dynamics.com/api/data/v9.2/mar_custom_CalculateOpportunityMargin
Content-Type: application/json
{
"opportunityid": "GUID-OPP",
"applydiscount": true
}
- Réponse attendue :
{
"marginamount": 1250.0,
"marginrate": 0.22
}
Tester avec Power Automate
- Créer un cloud flow instantané.
- Étape HTTP with Dataverse ou Action personnalisée > sélectionner
Calculate Opportunity Margin. - Fournir
opportunityid,applydiscount. - Ajouter un Compose pour afficher
marginamount,marginrate.
Sécurité et gouvernance
- Créer un rôle de sécurité spécifique autorisant l’exécution de l’API.
- Journaliser via Plug-in Trace Log et exporter vers Application Insights si nécessaire.
- Noms et schéma : préfixe d’éditeur
Marlk_, noms logiques en anglais, affichage localisé.
Check-list de validation
- L’API est dans une solution avec dépendances résolues.
- Les paramètres sont typés et documentés.
- Le handler gère les erreurs et retourne des messages clairs.
- Les tests Postman et Power Automate passent.
- Les droits de sécurité sont en place.
- Traçabilité activée et contrôlée.
Industrialisation (extrait pipeline)
pac solution packpour générer les artefacts.- Power Platform Build Tools:
Export Solution(unmanaged) > tests >Import Solution(managed) en recette. - Gate qualité : règles statiques (StyleCop), tests unitaires sur la logique C# (calcul marge), tests d’intégration via flows.
Ce parcours donne un flux bout en bout reproductible, du design à l’appel API, prêt pour l’ALM et l’audit.
