Exécution d'un script

Réagir à une opération en exécutant un script JavaScript

Principe

Ce gestionnaire d’opérations permet de réagir à l’exécution d’une opération en exécutant un script JavaScript. Le script JavaScript est exécuté à l’aide du moteur Graal embarqué dans la JVM de FlowerDocs Core.

Le script est stocké comme contenu (fichier .js attaché) du document OperationHandlerRegistration utilisé pour configurer l’abonnement.

Variables

Liées au contexte

Les variables suivantes sont injectées dans le script en fonction du type d’opération :

Variable Type Disponibilité Description
context

OperationContext | Toujours | Contexte d’exécution de l’opération (scope, utilisateur, phase, etc.) | | component |

Component | Opérations sur composants (CREATE, UPDATE, DELETE, READ, ANSWER, ASSIGN) | Le composant concerné par l’opération | | request |

SearchRequest | Opérations de recherche | La requête de recherche exécutée | | response |

SearchResponse | Opérations de recherche (phase AFTER uniquement) | La réponse de recherche | | logger | SLF4J Logger | Toujours | Logger pour écrire dans le journal de FlowerDocs Core | | util | ContextUtil | Toujours | Objet utilitaire pour l’accès aux services et la persistance des composants |

En phase BEFORE, les modifications apportées à l’objet component sont appliquées à l’opération avant son exécution. En phase AFTER, le composant reflète son état après l’exécution de l’opération.

Utilitaires

Deux objets utilitaires sont disponibles — ils ont des rôles distincts :

  • util est une instance de RuleContextUtil. Utilisez-le pour toute opération qui appelle les services de FlowerDocs Core : accès aux services (util.getDocumentService(), util.getFolderService(), …), persistance de composants (util.create(), util.update()), changement de classe (util.changeClass()), et création de faits (util.createFact()).

  • RuleUtil est une référence de classe statique. Utilisez-le pour les opérations en mémoire sur l’objet component sans appel de service : lecture et écriture de tags (RuleUtil.getTagValue(), RuleUtil.setTagValue()), identifiants de classe (RuleUtil.getClassId(), RuleUtil.setClassId()), et statuts (RuleUtil.getStatus(), RuleUtil.setStatus()).

La référence complète des méthodes des deux objets est documentée ici.


Comme util est une instance d’une classe qui hérite de RuleUtil, appeler les méthodes statiques via util (ex : util.setTagValue(...)) fonctionne également. Toutefois, la convention recommandée est RuleUtil.method() pour les helpers statiques et util.method() pour les appels de services.

Classes pré-importées

Pour simplifier le développement des scripts, les classes suivantes sont disponibles par leur nom court sans nécessiter de référence complète au package :

Classe Description
ComponentBuilder Construction d’instances Document, Folder, Task ou VirtualFolder
TagBuilder Construction d’instances Tag
CriterionBuilder Construction de critères de recherche
FilterClauseBuilder Construction de clauses de filtre AND/OR
SearchRequestBuilder Construction de requêtes de recherche
SearchBuilder Construction de recherches pour dossiers virtuels
FactBuilder Construction de faits d’audit
ReferenceBuilder Construction de références de composants
ExceptionBuilder Construction d’exceptions fonctionnelles ou techniques
Classe Description
Document, Folder, Task, VirtualFolder Types de composants
DocumentFile Fichier attaché
Id, Ids Types d’identifiants
Tag, Tags Tag et collection de tags
Status Statut du composant
Category Enum de catégorie du composant
ComponentData Métadonnées du composant
ComponentReference Référence vers un composant
Attachment Pièce jointe d’une tâche
Answer, ReasonedAnswer Types de réponse de tâche
User, Group Principaux de sécurité
Fact, Action, ObjectType Types de faits d’audit
Classe Description
SearchRequest Objet de requête de recherche
Criterion, Criteria Critère de recherche
AndClause, OrClause Combinateurs de clauses logiques
Operators Opérateurs de recherche (EQUALS_TO, CONTAINS, etc.)
Types Types de données des critères (STRING, DATE, etc.)
OrderClause Ordre de tri
FlowerFields Noms de champs standards
SearchContexts Types de contexte de recherche
FieldAggregation Configuration d’agrégation
ResultField Sélection de champs de résultat
Classe Description
RuleUtil Méthodes statiques pour les tags, classes et statuts
ComponentHelper Méthodes utilitaires pour les composants
TagHelper Méthodes utilitaires pour les tags
Lists, Sets, Maps Fabriques de collections Guava
String, Boolean, Float, Integer, Date Types standards Java
IdentifiableString Wrapper de chaîne identifiable
InternalFeatures Fonctionnalités internes
Classe Description
ExceptionBuilder Création d’exceptions fonctionnelles ou techniques
FunctionalErrorCode Enum de codes d’erreur fonctionnels
TechnicalErrorCode Enum de codes d’erreur techniques

Exemples

Création d’un dossier lors de la création d’un document


AFTER / CREATE / DOCUMENT

var folder = ComponentBuilder.folder().classId('Folder').build();
folder.setName("Dossier " + component.getName());
util.getFolderService().create(Lists.newArrayList(folder));
util.getFolderService().addChildren(folder.getId(), Lists.newArrayList(ReferenceBuilder.from(component)), false);

Création d’un fait d’audit


AFTER / CREATE / DOCUMENT — Enregistrer un fait métier pour la traçabilité

logger.info("[CreateFactOnCreation] Build facts for " + component.getId());

var builder = FactBuilder.objectId(component.getId()).type(ObjectType.DOCUMENT);
builder.action("CREATE").description("Le traitement a été lancé.");
util.createFact(builder.build());

Machine à états de workflow (transitions de classe de tâche)


BEFORE / ANSWER / TASK — Changer la classe de la tâche en fonction de la réponse donnée

logger.info("[Workflow_OH] START");
var classId = RuleUtil.getClassId(component);
var answerId = component.getAnswer().getId().getValue();
logger.info("[Workflow_OH] classId=" + classId + ", answerId=" + answerId);

var start = "WKF_Step0_Creation";
var step1 = "WKF_Step1_Treatment";
var end = "WKF_Step2_End";

switch (classId) {
  case start:
    changeClassOnAnswer(answerId, "Initiate", step1);
    break;
  case step1:
    if (!changeClassOnAnswer(answerId, "Validate", end)) {
      changeClassOnAnswer(answerId, "Adjourn", start);
    }
    break;
  default:
}
logger.info("[Workflow_OH] END");

function changeClassOnAnswer(appliedAnswer, expectedAnswer, classToApply) {
  if (appliedAnswer === expectedAnswer) {
    util.changeClass(component, classToApply);
    logger.info("[Workflow_OH] Class changed to " + classToApply);
    return true;
  }
  return false;
}

Recherche et création conditionnelle d’un dossier virtuel


AFTER / CREATE / DOCUMENT — Créer un dossier métier s'il n'existe pas déjà

let refValue = RuleUtil.getTagValue(component, "NumReference");
let clientValue = RuleUtil.getTagValue(component, "NomClient");

let refCriterion = CriterionBuilder.name("NumReference").operator(Operators.EQUALS_TO)
    .type(Types.STRING).value(refValue).build();
let clientCriterion = CriterionBuilder.name("NomClient").operator(Operators.EQUALS_TO)
    .type(Types.STRING).value(clientValue).build();
let request = SearchRequestBuilder.init()
    .filter(FilterClauseBuilder.and().criterion(refCriterion).criterion(clientCriterion).build())
    .build();

let response = util.getVirtualFolderService().search(request);
if (response.getFound() == 0) {
  let vf = ComponentBuilder.virtualFolder().classId("DossierMetier")
      .name(refValue + " - " + clientValue).build();
  vf.getTags().getTags().add(TagBuilder.name("NomClient").value(clientValue).build());
  vf.getTags().getTags().add(TagBuilder.name("NumReference").value(refValue).build());
  util.getVirtualFolderService().create([vf]);
}

Modification de tags et d’ACLs


BEFORE / ANSWER / TASK — Mettre à jour les tags et l'ACL lors d'une transition de workflow

var classId = RuleUtil.getClassId(component);
var answerId = component.getAnswer().getId().getValue();

if (classId == "Step0_Creation" && answerId === "Initiate") {
  util.changeClass(component, "Step1_Treatment");
  component.setAssignee("");
  component.getData().setACL("acl-treatment");
  util.setTagValue(component, "Status", "In Progress");
}

Lever des exceptions

Un script en phase BEFORE peut empêcher l’exécution de l’opération en levant une exception. Cela ne fonctionne que lorsque StopOnException est défini à true dans l’abonnement.


BEFORE — Rejeter l'opération avec une erreur fonctionnelle

// Lever une exception fonctionnelle (violation de règle métier)
throw ExceptionBuilder.createFunctionalException(FunctionalErrorCode.F00008);

// Lever une exception technique (erreur système)
throw ExceptionBuilder.createTechnicalException(TechnicalErrorCode.T00008);


Pour définir manuellement ce gestionnaire d’opérations, l’identifiant com.flower.docs.core.tsp.operation.script.ScriptOperationHandler peut être utilisé comme valeur du tag OperationHandler.


L’identifiant legacy com.flower.docs.bpm.core.operation.ScriptOperationHandler est déprécié depuis la version 2025.0. Utilisez l’identifiant ci-dessus à la place. Lors de la migration des scripts depuis l’ancien gestionnaire, remplacez equals() par == pour les comparaisons de chaînes et Date.now() par String.valueOf(new Date().getTime()).