Comment Développer dans Smart Processing (Documentation Développeur)
Statut : 01.08.2026 • Temps de lecture : ~8 minutes
Smart Processing fournit des points d'extension où vous pouvez implémenter une logique personnalisée pour démarrer vos propres processus ou adapter le traitement aux exigences des clients. Ce document explique le concept clé du développement : travailler à travers la session active et déclencher des codeunits personnalisés dans le processus correspondant en utilisant les données de session.
1. Concept de Base : Travailler à Travers la Session de Traitement Active
Smart Processing exécute une “session de brouillon active” pendant qu'un document est traité. Pendant ce temps, l'état pertinent est maintenu de manière centrale (contexte du document, contexte du modèle, lignes correspondantes, écarts et données intermédiaires utilisées par l'interface utilisateur et le pipeline de traitement).
En tant que développeur, l'approche recommandée est :
- Lire le contexte de traitement actuel à partir de la session.
- Effectuer votre logique en utilisant le contexte et les références fournis.
- Écrire les résultats/l'état dans la session lorsque le flux de travail en a besoin.
- Compter sur la plateforme pour maintenir l'interface utilisateur et le traitement alignés grâce à l'architecture de session.
Cela rend les personnalisations stables et évite un état incohérent entre ce que les utilisateurs voient et ce que votre logique a changé.
2. L'Objet Central : “SIM_DI Process Session SI”
Le codeunit “SIM_DI Process Session SI” est le point d'entrée central pour accéder et manipuler la session Smart Processing actuellement active. Il agit comme un hub pour obtenir des données d'exécution pertinentes (document entrant, modèle de processus, tampons de ligne, métadonnées d'écart) et pour communiquer les résultats au pipeline (par exemple, quel en-tête a été traité).
Vue d'ensemble des fonctions publiques (à partir des exemples fournis)
Le tableau suivant répertorie les fonctions publiques qui sont réellement utilisées dans votre code d'exemple. Si une fonction existe dans plusieurs variantes (surcharges), les paramètres qui peuvent ne pas être présents dans chaque variante sont marqués comme optionnels.
| Fonction | Ce qu'elle fait | Paramètres (optionnels marqués) | Retourne |
|---|---|---|---|
InitSession |
Initialise la session avec un document entrant et résout le modèle de processus associé. | InboundDocument |
aucun |
InitSession |
Initialise la session avec un document entrant et un modèle de processus explicitement fourni (et efface d'abord les données de session précédentes). | InboundDocument, ProcessTemplate |
aucun |
GetInboundDocument |
Récupère le document entrant stocké dans la session actuelle. | var InboundDocument |
aucun |
GetProcessTemplate |
Récupère le modèle de processus stocké dans la session actuelle. | var ProcessTemplate |
aucun |
ClearProcessSession |
Efface l'intégralité du contexte de session, y compris le document entrant, le modèle de processus, les clés de mappage et les données de processus. | - | aucun |
ClearProcessData |
Efface uniquement les données de traitement (en-têtes/lignes/observations temporaires, enregistrements de recherche mappés, id d'en-tête traité) et efface les données correspondantes. | - | aucun |
SetProcessedHeaderRecordId |
Stocke la référence d'enregistrement de l'en-tête de document cible créé/correspondant dans la session pour un traitement ultérieur (par exemple, achèvement/archivage). | RecordId |
aucun |
GetProcessedHeaderRecordId |
Retourne la référence d'enregistrement d'en-tête traité stockée et l'efface de la session par la suite. | - | RecordId |
SetDocumentHeaderVariableRef |
Stocke une copie de la variable d'en-tête temporaire fournie dans la session (utilisée comme état d'en-tête de session de style référence). | var TempDocumentHeader (temporaire) |
aucun |
GetDocumentHeaderVariable |
Copie les données d'en-tête temporaire actuelles de la session dans la variable d'enregistrement temporaire fournie. | var TempDocumentHeader (temporaire) |
aucun |
SetDocumentLineVariableRef |
Stocke une copie de la variable de ligne temporaire fournie dans la session (utilisée comme état de ligne de session de style référence). | var TempDocumentLine (temporaire) |
aucun |
GetDocumentLineVariable |
Copie les données de ligne temporaires actuelles de la session dans la variable d'enregistrement temporaire fournie. | var TempDocumentLine (temporaire) |
aucun |
SetRemarkVariableRef |
Stocke une copie de la variable d'observation temporaire fournie dans la session (utilisée comme état d'observation de session de style référence). | var DocumentRemark (temporaire) |
aucun |
RemoveDocumentRemark |
Supprime une observation de la session en fonction du numéro de ligne, du numéro de champ et de la zone de validation. | LineNo, FieldNo, FieldValidationArea |
aucun |
GetRemarks |
Copie toutes les observations de session dans la variable d'enregistrement d'observation temporaire fournie. | var DocumentRemark (temporaire) |
aucun |
RemoveMappedLookupRecord |
Supprime la référence d'enregistrement de recherche mappée pour un champ de modèle donné. | TemplateFieldRecordId |
aucun |
GetMappedLookupRecord |
Récupère la référence d'enregistrement de recherche mappée pour un champ de modèle donné (si disponible). | TemplateFieldRecordId, var MappedRecordId |
Boolean |
GetDevitatingLinesJsonObject |
Retourne l'objet JSON décrivant les lignes déviantes et leurs champs non correspondants (y compris le nom de configuration de correspondance et les détails de non correspondance). | - | JsonObject |
GetExecuteCodeunitRefRecordIds |
Récupère les références d'enregistrement assignées pour exécution par un codeunit spécifique (typiquement des lignes correspondantes à traiter). | ExecuteCodeunitNo, var RecordIds |
Boolean |
GetMatchedDocumentHeader |
Récupère la référence d'enregistrement d'en-tête de document correspondant pour un nom de configuration de correspondance donné (si disponible). | MatchingSetupName, var MatchedDocumentHeaderRecordId |
Boolean |
Remarque : Ce sont toutes les fonctions publiques qui peuvent être confirmées à partir de vos exemples. Si vous fournissez le code source complet du codeunit, j'élargirai ce tableau pour qu'il inclue toutes les fonctions publiques.
3. Personnalisations dans le Processus de Correspondance
Un scénario d'extension courant consiste à déclencher des codeunits personnalisés lors de la correspondance, en particulier lorsque l'utilisateur termine le brouillon et que l'enregistrement d'en-tête cible a été créé.
Un détail clé de ce mécanisme est que le codeunit personnalisé est exécuté avec l'enregistrement d'en-tête du type de document correspondant (par exemple, un en-tête d'achat). Cet enregistrement devient votre ancre stable : vous pouvez en dériver le contexte, créer ou importer des lignes, mettre à jour des enregistrements connexes, puis renvoyer le résultat à la session afin que la plateforme puisse continuer son flux de travail.
4. Exemples de Codeunits de Processus Personnalisés (Code Inchangé)
Exemple 1 : Importer les lignes de réception correspondantes après la création de l'en-tête
codeunit 5673320 "SIM_DI Purch.-Get Receipts"
{
Description = 'Ce codeunit est utilisé comme codeunit d\'importation de correspondance pour recevoir des lignes de réception d\'achat et créer des lignes de facture d\'achat basées sur les lignes de document correspondantes.';
TableNo = "Purchase Header"; // <-- Ceci est l'en-tête créé après avoir terminé le traitement
Access = Internal;
var
GlobalCodeunitSIMDIProcessSessionSI: Codeunit "SIM_DI Process Session SI";
GlobalCodeunitGetReceipts: Codeunit "Purch.-Get Receipt";
GlobalNoPurchaseReceiptLinesFoundLbl: Label 'Il n\'y a pas de lignes de réception d\'achat pouvant être traitées. Veuillez vérifier les lignes de réception d\'achat et vérifier si elles ont déjà été facturées.';
trigger OnRun()
var
LocalRecordPurchRcptLine: Record "Purch. Rcpt. Line";
LocalMatchedLineRecordId: RecordId;
LocalToExecuteRecordIds: List of [RecordId];
begin
// Recevoir les lignes correspondantes qui doivent être exécutées
GlobalCodeunitSIMDIProcessSessionSI.GetExecuteCodeunitRefRecordIds(Codeunit::"SIM_DI Purch.-Get Receipts", LocalToExecuteRecordIds);
// Filtrer les lignes de réception d'achat en fonction des lignes correspondantes entrantes
foreach LocalMatchedLineRecordId in LocalToExecuteRecordIds do
if LocalRecordPurchRcptLine.Get(LocalMatchedLineRecordId) then
LocalRecordPurchRcptLine.Mark(true);
// Obtenir les lignes de réception d'achat qui n'ont pas été facturées
LocalRecordPurchRcptLine.MarkedOnly(true);
LocalRecordPurchRcptLine.SetFilter("Qty. Rcd. Not Invoiced", '<>0');
// Si aucune ligne de réception d'achat n'est trouvée, lancer une erreur
if LocalRecordPurchRcptLine.IsEmpty() then
Error(GlobalNoPurchaseReceiptLinesFoundLbl);
// Exécuter le processus pour obtenir les lignes de réception
GlobalCodeunitGetReceipts.SetPurchHeader(Rec);
GlobalCodeunitGetReceipts.CreateInvLines(LocalRecordPurchRcptLine);
Commit();
end;
}
Cet exemple montre un processus typique piloté par la correspondance : la session fournit la liste des “enregistrements à exécuter”, et votre codeunit effectue un import basé exactement sur ces références correspondantes.
Exemple 2 : Mettre à jour les champs déviants lorsque la déviation est acceptée
codeunit 5673350 "SIM_DI Update Deviating Fields"
{
Description = 'Ce codeunit peut être utilisé pour mettre à jour les lignes de document liées avec les valeurs entrantes pour les champs qui ont été déviants pendant le processus de correspondance et où la déviation est acceptée.';
Access = Public;
var
GlobalCodeunitSIMDIProcessSessionSI: Codeunit "SIM_DI Process Session SI";
GlobalCodeunitSIMCOREEvaluate: Codeunit "SIM_CORE Evaluate";
GlobalCodeunitSIMCOREJSON: Codeunit "SIM_CORE JSON";
trigger OnRun()
var
TempLocalRecordSIMDITempDocumentLine: Record "SIM_DI Temp. Document Line" temporary;
LocalRecordSIMDIProcessTemplate: Record "SIM_DI Process Template";
LocalRecordSIMDIInboundDocument: Record "SIM_DI Inbound Document";
LocalCodeunitSIMDILogManagement: Codeunit "SIM_DI Log Management";
LocalDocumentHeaderRecordId: RecordId;
LocalRecordId: RecordId;
LocalRecordRef: RecordRef;
LocalFieldRef: FieldRef;
LocalRecordIdText: Text;
LocalDevitatingLinesJsonObject: JsonObject;
LocalLineMatchingValidationFieldsJsonObject: JsonObject;
LocalJsonToken: JsonToken;
LocalFieldNoInteger: Integer;
LocalFieldNoText: Text;
LocalCellIndexInteger: Integer;
LocalMatchingSetupNameText: Text;
LocalOldValueText: Text;
LocalNewValueText: Text;
LocalValueVariant: Variant;
LocalDevitatingFieldUpdateLbl: Label 'La ligne %1 a été mise à jour.\Le champ "%2" a été changé de la valeur "%3" à la valeur "%4".', Comment = '%1 = Numéro de ligne, %2 = Nom du champ, %3 = Ancienne valeur, %4 = Nouvelle valeur';
begin
/// Recevoir les données de la session de processus
LocalDevitatingLinesJsonObject := GlobalCodeunitSIMDIProcessSessionSI.GetDevitatingLinesJsonObject();
GlobalCodeunitSIMDIProcessSessionSI.GetDocumentLineVariable(TempLocalRecordSIMDITempDocumentLine);
GlobalCodeunitSIMDIProcessSessionSI.GetInboundDocument(LocalRecordSIMDIInboundDocument);
GlobalCodeunitSIMDIProcessSessionSI.GetProcessTemplate(LocalRecordSIMDIProcessTemplate);
// Itérer sur les
foreach LocalRecordIdText in LocalDevitatingLinesJsonObject.keys() do begin
if not Evaluate(LocalRecordId, LocalRecordIdText) then continue;
if not LocalRecordRef.Get(LocalRecordId) then continue;
// Obtenir l'enregistrement de ligne actuel
LocalDevitatingLinesJsonObject.Get(LocalRecordIdText, LocalJsonToken);
// Obtenir le nom de configuration de correspondance
LocalMatchingSetupNameText := GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'MatchingSetupName');
// Obtenir les champs non correspondants sous forme d'objet json
if not LocalJsonToken.AsObject().Get('MissMatchedFields', LocalJsonToken) then continue;
LocalLineMatchingValidationFieldsJsonObject := LocalJsonToken.AsObject();
// Itérer sur les champs non correspondants et mettre à jour la ligne de document liée avec la valeur entrante si la déviation est acceptée
foreach LocalFieldNoText in LocalLineMatchingValidationFieldsJsonObject.keys() do begin
if not Evaluate(LocalFieldNoInteger, LocalFieldNoText) then continue;
LocalLineMatchingValidationFieldsJsonObject.Get(LocalFieldNoText, LocalJsonToken);
if not GlobalCodeunitSIMCOREJSON.GetJsonPathValueBoolean(LocalJsonToken.AsObject(), 'AcceptDeviation') then
continue;
if not Evaluate(LocalCellIndexInteger, GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'CellIndex')) then
continue;
LocalFieldRef := LocalRecordRef.Field(LocalFieldNoInteger);
LocalNewValueText := GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'IncomingValue');
LocalOldValueText := LocalFieldRef.Value;
// Mettre à jour le champ avec la valeur entrante
GlobalCodeunitSIMCOREEvaluate.EvaluateVariable(
LocalNewValueText,
Format(LocalFieldRef.Type),
LocalValueVariant,
false
);
LocalFieldRef.Validate(LocalValueVariant);
LocalRecordRef.Modify();
// Ajouter une entrée de journal
LocalCodeunitSIMDILogManagement.AddInformationInTheLog(
StrSubstNo(LocalDevitatingFieldUpdateLbl, Format(LocalRecordIdText), LocalFieldRef.Caption, LocalOldValueText, LocalNewValueText),
'SIM_DI Update Deviating Fields',
LocalRecordSIMDIInboundDocument."Entry No."
);
end;
// Se souvenir de l'id d'enregistrement d'en-tête de document pour archiver le document plus tard
GlobalCodeunitSIMDIProcessSessionSI.GetMatchedDocumentHeader(LocalMatchingSetupNameText, LocalDocumentHeaderRecordId);
end;
// Définir l'id d'enregistrement d'en-tête de document correspondant dans la session
GlobalCodeunitSIMDIProcessSessionSI.SetProcessedHeaderRecordId(LocalDocumentHeaderRecordId);
Commit();
end;
}
Cet exemple illustre le “traitement des déviations piloté par la session” : la session fournit un ensemble de données de déviation structurées ; votre code applique les déviations acceptées, enregistre ce qui a changé et rapporte l'en-tête traité dans la session.
5. Points de Personnalisation Supplémentaires
Il existe d'autres endroits où des codeunits personnalisés peuvent être intégrés, mais le principe directeur reste constant : utilisez la session pour récupérer le contexte de traitement actif et pour écrire les résultats pertinents au flux de travail. Cela maintient vos extensions cohérentes avec le flux de Smart Processing et empêche l'écart entre l'interface utilisateur et le processus.