Config Does not working

Hello,

I am currently facing an issue with my Exalate Config
I don’t know if I get it properly.

I have set a connection between two projet in my instance but it’s not working properly
Cab you perhaps help with this issue?

Outgoing Config :

replica.key = issue.key

replica.type = issue.type

replica.assignee = issue.assignee

replica.reporter = issue.reporter

replica.summary = issue.summary

replica.description = issue.description

replica.labels = issue.labels

replica.comments = issue.comments

replica.resolution = issue.resolution

replica.status = issue.status

replica.parentId = issue.parentId

replica.priority = issue.priority

replica.attachments = issue.attachments

replica.project = issue.project

replica.issueLinks = issue.issueLinks

//common fields

replica.project.versions = []

replica.project.components = []

replica.customFields.“Temps de résolution (en mn)” = issue.customFields.“Temps de résolution (en mn)”

replica.customFields.“Durée d’indisponibilité (en mn)” = issue.customFields.“Durée d’indisponibilité (en mn)”

replica.customFields.“Date de résolution” = issue.customFields.“Date de résolution”

replica.customFields.“Crise” = issue.customFields.“Crise”

if (issue.project?.key == “Proj1”) {

replica.customFields.“Temps d’intervention exploitant (en mn)” = issue.customFields.“Temps d’intervention exploitant (en mn)”

replica.customFields.“Domaine” = issue.customFields.“Domaine”

replica.customFields.“Root Cause Incident” = issue.customFields.“Root Cause Incident”

replica.customFields.“Sévérité” = issue.customFields.“Sévérité”

replica.customFields.“Motif Canceled” = issue.customFields.“Motif Canceled”

replica.customFields.“Date et heure de détection” = issue.customFields.“Date et heure de détection”

replica.customFields.“[Root] cause de l’incident” = issue.customFields.“[Root] cause de l’incident”

replica.customFields.‘Asset’ = issue.customFields.‘CM Composant’

replica.customFields.“Projet demandeur” = issue.customFields.“Projet demandeur”

}else if (issue.project?.key == “Proj2”) {

replica.customFields.“Temps d’intervention ( en mn)” = issue.customFields.“Temps d’intervention ( en mn)”

replica.customFields.“Domaine impacté” = issue.customFields.“Domaine impacté”

replica.customFields.“Date et heure de Détection” = issue.customFields.“Date et heure de Détection”

replica.customFields.“Type root cause” = issue.customFields.“Type root cause”

replica.customFields.“Séverité” = issue.customFields.“Séverité”

replica.customFields.“Motif annulation” = issue.customFields.“Motif annulation”

replica.customFields.“Description root cause” = issue.customFields.“Description root cause”

replica.customFields.‘Asset’ = issue.customFields.‘Asset’

replica.customFields.“Projet demandeur” = issue.customFields.“Projet demandeur”

replica.customFields.“Environnement(s) de détection” = issue.customFields.“Environnement(s) de détection”

}

Incoming Config

if(firstSync && replica.project.key == “proj2”){
issue.projectKey = “proj1”
issue.typeName = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: “Incident”
issue.customFields.“Projet demandeur”.value = ‘proj2’
syncHelper.syncBackAfterProcessing()
}
if(firstSync && replica.project.key == “proj1”){
issue.projectKey = “proj2”
issue.typeName = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: “Incident”
issue.customFields.“Projet demandeur”.value = ‘proj1’
syncHelper.syncBackAfterProcessing()
}
def Valid_Assets = [‘DEDO’, ‘SEL’, ‘SIG’, ‘SIMS NL+’, ‘VADCOM’, ‘TST’, ‘APP’, ‘Distributeurs’]
def Valid_Composant = ‘Comutitres - Exploitation - SI’
def ValidSousSomposants = [‘DEDO - BO’, ‘DEDO - FO’, ‘DEDO - Infra AWS’, ‘DEDO - Infra OVH’, ‘DEDO - Crowdhandler’, ‘SEL - BO’, ‘SEL - FO’, ‘SEL - Infra Contralia’, ‘SEL - Batch’, ‘SEL - Ordonnancement’, ‘SIG - Apache’, ‘SIG - BDD’, ‘SIG - Flux’, ‘SIG - Haproxy’, ‘SIG - IHMC’, ‘SIG - WEBLOGIC’, ‘SIG - Ordonnancement’, ‘SIMS NL+ - Facturation’, ‘SIMS NL+ - Flux de validations’, ‘VADCOM - Infra AWS’, ‘VADCOM - BDD’, ‘VADCOM - Flux’, ‘APP - Microservice’, ‘APP - File RabbitMQ’, ‘APP - Gateway API’, ‘APP - BDD’, ‘TST - Infra Worldline’, ‘TST - Certificats’, ‘TST - Flux’, ‘Distributeurs - Back’, ‘Distributeurs - BDD’, ‘Distributeurs - Front’, ‘Distributeurs - Application mobile’]
def baseurl = “https://tototata.atlasian.net/browse/”
issue.summary = replica.summary
issue.attachments = attachmentHelper.mergeAttachments(issue, replica)
issue.labels = replica.labels
issue.description = replica.description
issue.issueLinks = replica.issueLinks
/link//issue.comments = commentHelper.mergeComments(issue, replica)
/*issue.comments = commentHelper.mergeComments(issue, replica, {
it.executor = nodeHelper.getUserByEmail(it.author?.email)
})
*/

// On est proj1 on va copier vers
if (replica.project?.key == “proj1”) {
issue.customFields.“Temps d’intervention ( en mn)”.value = replica.customFields.“Temps d’intervention exploitant (en mn)”.value
issue.customFields.“Domaine impacté”.value = replica.customFields.“Domaine”?.value?.value
issue.customFields.“Séverité”.value = replica.customFields.“Sévérité”?.value?.value
issue.customFields.“Motif annulation”.value = replica.customFields.“Motif Canceled”?.value?.value
issue.customFields.“Date et heure de Détection”.value = replica.customFields.“Date et heure de détection”.value //10240 Date et heure de Détection

if(replica.status.name == “Résolu”){
issue.customFields.“Type root cause”.value = replica.customFields.“Root Cause Incident”?.value?.value
issue.customFields.“Description root cause”.value = replica.customFields.“[Root] cause de l’incident”?.value
issue.customFields.“Date de résolution” = replica.customFields.“Date de résolution”
issue.customFields.“Temps de résolution (en mn)” = replica.customFields.“Temps de résolution (en mn)”
issue.customFields.“Durée d’indisponibilité (en mn)” = replica.customFields.“Durée d’indisponibilité (en mn)”
issue.customFields.“Temps d’intervention ( en mn)” = replica.customFields.“Temps d’intervention ( en mn)”
issue.customFields.“Crise” = replica.customFields.“Crise”
}

/link//issue.customFields.“Ticket CEP”.value = replica.customFields.“Ticket CEP”?.value
//Define two fields that exists only in proj2 project : “Catégorisation”, “Environnement(s) de détection”
if (firstSync && replica.project?.key == “proj1”){
issue.customFields.“Environnement(s) de détection”.value = “PRODUCTION”
issue.customFields.“Catégorisation”.value = “A définir”
}

//Synchro de reporter et Assignee
def Reporterdefault = nodeHelper.getUser(“xxxxxxxxxxxxxxxx-xxxxx-xxxxx-xxxxxxx”)
def Rapporteur1 = nodeHelper.getUser(replica.reporter?.key)
if(Rapporteur1 != Reporterdefault){
issue.reporter = nodeHelper.getUser(replica.reporter?.key)
}

def assignee = nodeHelper.getUser(replica.assignee?.key)
if (nodeHelper.isUserAssignable(issue.projectKey, assignee)){
issue.assignee = assignee
}else{
if (!issue.assignee) { // Check if there’s no current assignee
issue.assignee = nodeHelper.getUserByEmail(“tata@tata”) // Assign to Exalate user
}
}

//commments merge
replica.addedComments.each { it.executor = nodeHelper.getUser(it.author?.key) }
replica.changedComments.each { it.executor = nodeHelper.getUser(it.updateAuthor?.key) }
issue.comments = commentHelper.mergeComments(issue, replica, { it })

//Synchronisation des etats et transitions de workflow pour premiere connexion
if(firstSync && replica.project?.key == “proj1” && replica.status.name == “Work in progress”){
workflowHelper.transition(issue, “Investiguer”)
}else if(firstSync && replica.project?.key == “proj1” && replica.status.name == “Pending”){
workflowHelper.transition(issue, “En attente”)
}else if(firstSync && replica.project?.key == “proj1” && replica.status.name == “Résolu”){
workflowHelper.transition(issue, “Résoudre”)
}
//Synchronisation des etats et transitions de workflow
// Replica = proj1
// Issue = proj2
// Issue = Workflow de proj2
if(!firstSync && replica.project?.key == “proj1”){
if(issue.status.name == “Ouvert” && replica.status.name == “Work in progress”){
workflowHelper.transition(issue, “Investiguer”)
/link//issue.setStatus(“En cours”)
}else if(issue.status.name == “Ouvert” && replica.status.name == “Canceled”){
workflowHelper.transition(issue, “Annuler”)
}else if(issue.status.name == “Ouvert” && replica.status.name == “Résolu”){
workflowHelper.transition(issue, “Résoudre”)
}else if(issue.status.name == “Ouvert” && replica.status.name == “Pending”){
workflowHelper.transition(issue, “En attente”)
// }else if(issue.status.name == “Completed” && replica.status.name == “Résolu”){
// workflowHelper.transition(issue, “Re-ouvrir”)
}else if(issue.status.name == “Completed” && replica.status.name == “Work in progress”){
workflowHelper.transition(issue, “Re-ouvrir”)
// Thread.sleep(400)
// workflowHelper.transition(issue, “Investiguer”)
} else if(issue.status.name == “En cours” && replica.status.name == “Résolu”){
workflowHelper.transition(issue, “Résoudre”)
}else if (issue.status.name == “En cours” && replica.status.name == “Pending”) {
workflowHelper.transition(issue, “En attente”)
/link//issue.setStatus(“En attente”)
}else if (issue.status.name == “En cours” && replica.status.name == “Canceled”) {
workflowHelper.transition(issue, “Annuler”)
}else if(issue.status.name == “Resolved” && replica.status.name == “Closed”){
workflowHelper.transition(issue, “Terminer”)
}else if(issue.status.name == “Resolved” && replica.status.name == “Work in progress”){
workflowHelper.transition(issue, “Retour au Travail en cours”)
}else if(issue.status.name == “En attente” && replica.status.name == “Work in progress”){
workflowHelper.transition(issue, “Investiguer”)
}else if(issue.status.name == “En attente” && replica.status.name == “Canceled”){
workflowHelper.transition(issue, “Annuler”)
}
}
if(issue.customFields.“Projet demandeur”.value == null){
issue.customFields.“Projet demandeur” = replica.customFields.“Projet demandeur”
}
//Annuler les tickets dont l’asset n’est plus une asset de l’equipe proj2
def sourceParent = replica.customFields.‘CM Composant’?.value?.parent?.value
def sourceChild = replica.customFields.‘CM Composant’?.value?.child?.value
def Ticket_master = issue.customFields.“Projet demandeur”?.value
// def childV = parentValue + “-” + childValue
// def parentOption_valid = nodeHelper.getOption(issue, 10140L, Valid_Composant)
// def childOption_valid = parentOption_valid?.childOptions.find{}
//if ((sourceParent != Valid_Composant || (childOption_valid != Valid_sous_composants))&& issue.status?.name != “Annulé”)
def remoteIssueUrl = baseurl + replica.key

// Si Ticket_Master = proj1 alors on est dans un incident proj2 et vice versa

if(Ticket_master)
{
if ((sourceParent != Valid_Composant || !ValidSousSomposants.contains(sourceChild)) && issue.status?.name != “Annulé” && Ticket_master == ‘proj1’)
{
issue.customFields.‘Motif annulation’.value = “Erreur de routage”
workflowHelper.transition(issue, “Annuler”)
issue.comments = commentHelper.addComment(“On annule l’incident car l’Asset de ticket CEP n’est pas compatible avec ce projet”, issue.comments)
issue.customFields.“Ticket CEP”.value = null
Thread.sleep(2000)
syncHelper.unExalateAfterProcessing()
}
else if ((sourceParent != Valid_Composant || !ValidSousSomposants.contains(sourceChild)) && issue.status?.name != “Canceled” && Ticket_master == ‘proj2’)
{
}
else
{
if (sourceChild) {
def (parentValue, childValue) = sourceChild.split(“-”).trim()
def parentOption = nodeHelper.getOption(issue, 10291L, parentValue)
def childOption = parentOption?.childOptions.find{it.value == childValue}
issue.customFields.‘Asset’.value = nodeHelper.getCascadingSelect(parentOption, childOption)
}
}
}
else
{
if ((sourceParent != Valid_Composant || !ValidSousSomposants.contains(sourceChild)) && issue.status?.name != “Annulé”)
{
issue.customFields.‘Motif annulation’.value = “Erreur de routage”
workflowHelper.transition(issue, “Annuler”)
issue.comments = commentHelper.addComment(“On annule l’incident car l’Asset de ticket CEP n’est pas compatible avec ce projet”, issue.comments)
issue.customFields.“Ticket CEP”.value = null
Thread.sleep(2000)
syncHelper.unExalateAfterProcessing()
}else
{
if (sourceChild) {
def (parentValue, childValue) = sourceChild.split(“-”)
.trim()
def parentOption = nodeHelper.getOption(issue, 10291L, parentValue)
def childOption = parentOption?.childOptions.find{it.value == childValue}
issue.customFields.‘Asset’.value = nodeHelper.getCascadingSelect(parentOption, childOption)
}
}
}
def AssetParent = issue.customFields.‘Asset’?.value?.parent?.value
if(!Valid_Assets.contains(AssetParent) && Ticket_master == ‘proj2’){
issue.customFields.“Ticket CEP”.value = null
}else{
issue.customFields.“Ticket CEP”.value = remoteIssueUrl
}
}else if (replica.project?.key == “proj2”) {
issue.customFields.“Temps d’intervention exploitant (en mn)”.value = replica.customFields.“Temps d’intervention ( en mn)”?.value
issue.customFields.“Domaine”.value = replica.customFields.“Domaine impacté”?.value?.value
issue.customFields.“10052”.value = replica.customFields.“Date et heure de Détection”?.value
issue.customFields.“Sévérité”.value = replica.customFields.“Séverité”?.value?.value
issue.customFields.“Motif Canceled”.value = replica.customFields.“Motif annulation”?.value?.value

if(replica.status.name == “Resolved”){
issue.customFields.“Root Cause Incident”.value = replica.customFields.“Type root cause”?.value?.value
issue.customFields.“[Root] cause de l’incident”.value = replica.customFields.“Description root cause”?.value
issue.customFields.“Date de résolution” = replica.customFields.“Date de résolution”
issue.customFields.“Temps de résolution (en mn)” = replica.customFields.“Temps de résolution (en mn)”
issue.customFields.“Durée d’indisponibilité (en mn)” = replica.customFields.“Durée d’indisponibilité (en mn)”
issue.customFields.“Temps d’intervention ( en mn)” = replica.customFields.“Temps d’intervention ( en mn)”
issue.customFields.“Crise” = replica.customFields.“Crise”
}
/* def sourceChild = replica.customFields.‘Asset’?.value?.child?.value
def sourceParent = replica.customFields.‘Asset’?.value?.parent?.value
if (sourceChild && sourceParent) {
def childValue = sourceParent + " - " + sourceChild
def parentValue = “Comutitres - Exploitation - SI”
def parentOption = nodeHelper.getOption(issue, 10140L, parentValue)
def childOption = parentOption?.childOptions.find{it.value == childValue}
issue.customFields.‘CM Composant’.value = nodeHelper.getCascadingSelect(parentOption, childOption)
}
*/
/link//issue.customFields.“Ticket ESI”.value = replica.customFields.“Ticket ESI”?.value
//Synchro de reporter et Assignee
def Reporterdefault = nodeHelper.getUser(“xxxxxxxxxxxxx-xxxx-xxxxxx-xxxxxxxx”)
def Rapporteur2 = nodeHelper.getUser(replica.reporter?.key)
if(Rapporteur2 != Reporterdefault){
issue.reporter = nodeHelper.getUser(replica.reporter?.key)
}

/* def Assignee = nodeHelper.getUser(replica.assignee?.key)
if(nodeHelper.isUserAssignable(issue.projectKey, Assignee)){
issue.assignee = Assignee
}else{
issue.assignee = nodeHelper.getUserByEmail(“toto@toto.fr”)
}
/
def assignee = nodeHelper.getUser(replica.assignee?.key)
if (nodeHelper.isUserAssignable(issue.projectKey, assignee)){
issue.assignee = assignee
}else{
if (!issue.assignee) { // Check if there’s no current assignee
issue.assignee = nodeHelper.getUserByEmail(“toto@toto”) // Assign to Exalate user
}
}
//commments merge
replica.addedComments.each { it.executor = nodeHelper.getUser(it.author?.key) }
replica.changedComments.each { it.executor = nodeHelper.getUser(it.updateAuthor?.key) }
issue.comments = commentHelper.mergeComments(issue, replica, { it })
/

issue.comments = commentHelper.mergeComments(issue, replica, { comment →
// Custom formatting if required
return “[${comment.executor?.displayName}] ${comment.body}”
})
*/

//Synchronisation des etats et transitions de workflow lors de la crétion si différent de “nouveau”
if(firstSync && replica.project?.key == “proj2” && replica.status.name == “En cours”){
issue.setStatus(“Work in progress”)
}else if(firstSync && replica.project?.key == “proj2” && replica.status.name == “En attente”){
workflowHelper.transition(issue, “En attente”)
}else if(firstSync && replica.project?.key == “proj2” && replica.status.name == “Resolved”){
workflowHelper.transition(issue, “Résoudre”)
}
////// Resolved → Résolu
//Synchronisation des etats et transitions de workflow aprés création
// Issue = proj1
// Replica = proj2
if(!firstSync && replica.project?.key == “proj2”){
if(issue.status.name == “Open” && replica.status.name == “En cours”){
issue.setStatus(“Work in progress”)
}else if(issue.status.name == “Open” && replica.status.name == “Annulé”){
workflowHelper.transition(issue, “Cancel”)
}else if(issue.status.name == “Open” && replica.status.name == “Resolved”){
workflowHelper.transition(issue, “Resolve”)
}else if(issue.status.name == “Open” && replica.status.name == “En attente”){
workflowHelper.transition(issue, “Pending”)
}else if(issue.status.name == “Closed” && replica.status.name == “Resolved”){
workflowHelper.transition(issue, “Reopen”)
}else if(issue.status.name == “Closed” && replica.status.name == “En cours”){
workflowHelper.transition(issue, “Reopen”)
Thread.sleep(400)
workflowHelper.transition(issue, “Back to work in progress”)
}else if(issue.status.name == “Work in progress” && replica.status.name == “En attente”){
workflowHelper.transition(issue, “Pending”)
}else if(issue.status.name == “Work in progress” && replica.status.name == “Resolved”){
workflowHelper.transition(issue, “Resolve”)
}else if(issue.status.name == “Work in progress” && replica.status.name == “Completed”){
workflowHelper.transition(issue, “Resolve”)
Thread.sleep(400)
workflowHelper.transition(issue, “Close”)
}else if(issue.status.name == “Work in progress” && replica.status.name == “Annulé”){
workflowHelper.transition(issue, “Cancel”)
}
if(issue.status.name == “Pending” && replica.status.name == “En cours”){
workflowHelper.transition(issue, “Investigate”)
}else if(issue.status.name == “Pending” && replica.status.name == “Annulé”){
workflowHelper.transition(issue, “Cancel”)
}
if(issue.status.name == “Résolu” && replica.status.name == “Completed”){
workflowHelper.transition(issue, “Close”)
}else if(issue.status.name == “Résolu” && replica.status.name == “En cours”){
workflowHelper.transition(issue, “Back to work in progress”)
}
}
if(issue.customFields.“Projet demandeur”.value == null){
issue.customFields.“Projet demandeur” = replica.customFields.“Projet demandeur”
}
//supprimer le lien de ticket sychronisé en cas de changement de composant declencheur
def remoteIssueUrl = baseurl + replica.key
def ticket_master = issue.customFields.“Projet demandeur”?.value
def sourceChild = replica.customFields.‘Asset’?.value?.child?.value
def sourceParent = replica.customFields.‘Asset’?.value?.parent?.value

// On est dans proj1

if(ticket_master)
{
//Annuler les tickets dont l’asset n’est plus une asset de l’equipe proj2
if (issue.status?.name != “Canceled” && !Valid_Assets.contains(sourceParent) && ticket_master == ‘proj2’ )
{
issue.customFields.‘Motif Canceled’.value = “Erreur de routage”
workflowHelper.transition(issue, “Cancel”)
issue.comments = commentHelper.addComment(“On annule l’incident car l’Asset de ticket ESI n’est pas compatible avec ce projet”, issue.comments)
issue.customFields.“Ticket ESI”.value = null
Thread.sleep(2000)
syncHelper.unExalateAfterProcessing()
}
else if(issue.status?.name != “Canceled” && replica.customFields.“Environnement(s) de détection”.value != “PRODUCTION” && ticket_master == ‘proj2’)
{
issue.customFields.‘Motif Canceled’.value = “Erreur de routage”
workflowHelper.transition(issue, “Cancel”)
issue.comments = commentHelper.addComment(“On annule l’incident car l’Asset de ticket ESI n’est pas compatible avec ce projet”, issue.comments)
issue.customFields.“Ticket ESI”.value = null
Thread.sleep(2000)
syncHelper.unExalateAfterProcessing()
}
else if(issue.status?.name != “Annlé” && !Valid_Assets.contains(sourceParent) && ticket_master == ‘proj1’ )
{

  }
  else
  {
     if (sourceParent) {
        def childValue = sourceParent + " - " + sourceChild
        def parentValue = "Comutitres - Exploitation - SI"
        def parentOption = nodeHelper.getOption(issue, 10140L, parentValue)
        def childOption = parentOption?.childOptions.find{it.value == childValue}
        issue.customFields.'10140'.value = nodeHelper.getCascadingSelect(parentOption, childOption)
		//issue.customFields.'CM Composant'.value = nodeHelper.getCascadingSelect(parentOption, childOption)
     }
  }

}
def CmChild = issue.customFields.‘CM Composant’?.value?.child?.value
def CmParent = issue.customFields.‘CM Composant’?.value?.parent?.value
if((CmParent != Valid_Composant || !ValidSousSomposants.contains(CmChild)) && issue.status?.name != “Annulé” && ticket_master == ‘proj1’)
{
issue.customFields.“Ticket ESI”.value = null
}else{
issue.customFields.“Ticket ESI”.value = remoteIssueUrl
}
/* String LocalChild = issue.customFields.‘CM composant’?.value?.child?.value
String LocalParent = issue.customFields.‘CM composant’?.value?.parent?.value
*/
}

Hi @Ulrich,

Welcome to the community!

I don’t see any error mentioned in your message, could you please share the exact error you’re getting from the Exalate console?

That said, I do notice a few syntax problems in the script that could be causing some issues:

I’m not entirely sure if this is due to how the script was pasted into the community (outside of a code block), but I can see that some parts use smart quotes (“ ” and ‘ ’) instead of standard/straight quotes (" or ') in some sections of the script. And also there are some Malformed comment blocks (. /* ... / instead of */).

Also sourceChild?.split("-")*.trim() returns a list, so you would need to trim each element of the list. split() will turn "A - B"["A", "B"]

Is the value from sourceChild coming from a cascading select? If that is the case it should already be a single value

Hello Javier,
Thank you for your reply.

So, I apparently had several different types of errors:

You have 33 errors of this type:

root_cause_detail_text     | Request type `Incident` requires a custom field 10140 of type `com.atlassian.jira.plugin.system.customfieldtypes:cascadingselect` but none was set through exalate config.
stack_trace                | com.exalate.api.exception.IssueTrackerException: Request type `Incident` requires a custom field 10140 of type `com.atlassian.jira.plugin.system.customfieldtypes:cascadingselect` but none was set through exalate config.

Plus 14 errors of the following type:

root_cause_error_type_name | Jira Cloud error
root_cause_detail_text     | Exalate could not find transition `Re-ouvrir` for work item `ESIC-4022`.
stack_trace                | jcloudnode.services.jcloud.exception.JiraCloudTrackerException: Exalate could not find transition `Re-ouvrir` for work item `ESIC-4022`.

And others that also seemed to be related to configuration issues.

I made several changes and fixed the errors, but the connection is still in error.

Here is the new script:
// === Mappings statuts et transitions ===
def statusMap_CEP_ESIC = [
‘Open’: ‘Ouvert’, ‘Work in progress’: ‘En cours’, ‘Pending’: ‘En attente’, ‘Résolu’: ‘Resolved’, ‘Closed’: ‘Completed’, ‘Canceled’: ‘Annulé’,
‘Ouvert’: ‘Open’, ‘En cours’: ‘Work in progress’, ‘En attente’: ‘Pending’, ‘Resolved’: ‘Résolu’, ‘Completed’: ‘Closed’, ‘Annulé’: ‘Canceled’
]
def transitionMap_CEP_ESIC = [
‘Create’: ‘Créer’, ‘Investigate’: ‘Investiguer’, ‘Pending’: ‘En attente’, ‘Resolve’: ‘Résoudre’, ‘Close’: ‘Terminer’, ‘Cancel’: ‘Annuler’, ‘Reopen’: ‘Re-ouvrir’,
‘Créer’: ‘Create’, ‘Investiguer’: ‘Investigate’, ‘En attente’: ‘Pending’, ‘Résoudre’: ‘Resolve’, ‘Terminer’: ‘Close’, ‘Annuler’: ‘Cancel’, ‘Re-ouvrir’: ‘Reopen’
]

// === Constantes métiers ===
def VALID_ASSET_PARENT = ‘Comutitres - Exploitation - SI’
def VALID_ASSET_CHILD = ‘DEDO - BO’
def VALID_ASSETS = [‘DEDO’, ‘SEL’, ‘SIG’, ‘SIMS NL+’, ‘VADCOM’, ‘TST’, ‘APP’, ‘Distributeurs’]
def VALID_SOUS_COMPOSANTS = [‘DEDO - BO’, ‘DEDO - FO’, ‘DEDO - Infra AWS’, ‘DEDO - Infra OVH’, ‘DEDO - Crowdhandler’, ‘SEL - BO’, ‘SEL - FO’, ‘SEL - Infra Contralia’, ‘SEL - Batch’, ‘SEL - Ordonnancement’, ‘SIG - Apache’, ‘SIG - BDD’, ‘SIG - Flux’, ‘SIG - Haproxy’, ‘SIG - IHMC’, ‘SIG - WEBLOGIC’, ‘SIG - Ordonnancement’, ‘SIMS NL+ - Facturation’, ‘SIMS NL+ - Flux de validations’, ‘VADCOM - Infra AWS’, ‘VADCOM - BDD’, ‘VADCOM - Flux’, ‘APP - Microservice’, ‘APP - File RabbitMQ’, ‘APP - Gateway API’, ‘APP - BDD’, ‘TST - Infra Worldline’, ‘TST - Certificats’, ‘TST - Flux’, ‘Distributeurs - Back’, ‘Distributeurs - BDD’, ‘Distributeurs - Front’, ‘Distributeurs - Application mobile’]
def BASE_URL = " Jira

// === Helpers ===
def getNowIso() { new Date().format(“yyyy-MM-dd’T’HH:mm:ss.SSSZ”, TimeZone.getTimeZone(‘Europe/Paris’)) }
def setCascadingField(issue, fieldId, parentValue, childValue) {
def parentOption = nodeHelper.getOption(issue, fieldId, parentValue)
def childOption = parentOption?.childOptions?.find { it.value == childValue }
if (parentOption && childOption) {
issue.customFields[“${fieldId}”].value = nodeHelper.getCascadingSelect(parentOption, childOption)
} else if (parentOption && !childOption && childValue == null) {
issue.customFields[“${fieldId}”].value = nodeHelper.getCascadingSelect(parentOption, null)
}
}
def getValidUserOrDefault(userKeyOrEmail, defaultUser) {
def user = userKeyOrEmail ? nodeHelper.getUser(userKeyOrEmail) : null
if (user && nodeHelper.isUserAssignable(issue.projectKey, user)) return user
return defaultUser
}
def setMandatoryAssetField(issue, fieldId, parentValue, childValue) {
parentValue = parentValue ?: VALID_ASSET_PARENT
childValue = childValue ?: VALID_ASSET_CHILD
setCascadingField(issue, fieldId, parentValue, childValue)
}
def setMandatoryDetectionDate(issue, fieldId, value) {
issue.customFields[“${fieldId}”].value = value ?: getNowIso()
}

// === Blocage de la création selon les conditions métiers ===
def remoteProject = replica.project?.key

// Récupération des valeurs des champs Ticket CEP et Ticket ESI (customfield ou nom)
def ticketCEP = issue.customFields.“customfield_10323”?.value ?: issue.customFields.“Ticket CEP”?.value
def ticketESI = issue.customFields.“customfield_10332”?.value ?: issue.customFields.“Ticket ESI”?.value

def alreadySynchronized = (ticketCEP || ticketESI)

// Nouvelle logique : effectiveFirstSync = true uniquement si firstSync ET ni Ticket ESI ni Ticket CEP ne sont renseignés
def effectiveFirstSync = firstSync && !alreadySynchronized

def defaultReporter = nodeHelper.getUserByEmail(“toto@toto.fr”)
def defaultReporterESIC = nodeHelper.getUserByEmail(“tata@toto.fr”)
def remoteStatus = replica.status?.name
def remoteKeyUrl = BASE_URL + replica.key

// === Blocage de toute tentative de création lors des synchronisations suivantes (ou si pas effectiveFirstSync) ===
if (!effectiveFirstSync) {
// On ne doit jamais tenter de créer un ticket lors des synchronisations suivantes
// Si le ticket n’existe pas côté local, on arrête la synchronisation
if (!issue.key) {
debug.error(“Blocage création : Tentative de création lors d’une synchronisation non initiale (effectiveFirstSync = false).”)
syncHelper.unExalateAfterProcessing()
return
}
}

// === Contrôles explicites CEP → ESIC ===
if (effectiveFirstSync && remoteProject == “CEP” && !alreadySynchronized) {
def cmParent = replica.customFields.‘CM Composant’?.value?.parent?.value
def cmChild = replica.customFields.‘CM Composant’?.value?.child?.value

if (!cmParent) {
    debug.error("Blocage création : Le champ parent du composant 'CM Composant' est vide ou non renseigné pour la synchronisation CEP -> ESIC. Parent: ${cmParent}, Enfant: ${cmChild}")
    syncHelper.unExalateAfterProcessing()
    return
}
if (!cmChild) {
    debug.error("Blocage création : Le champ enfant du composant 'CM Composant' est vide ou non renseigné pour la synchronisation CEP -> ESIC. Parent: ${cmParent}, Enfant: ${cmChild}")
    syncHelper.unExalateAfterProcessing()
    return
}
if (cmParent != VALID_ASSET_PARENT) {
    debug.error("Blocage création : Valeur du champ parent non autorisée pour la synchronisation.")
    syncHelper.unExalateAfterProcessing()
    return
}
if (!VALID_SOUS_COMPOSANTS.contains(cmChild)) {
    debug.error("Blocage création : Valeur du champ enfant non autorisée pour la synchronisation.")
    syncHelper.unExalateAfterProcessing()
    return
}

}

// === Contrôles explicites ESIC → CEP ===
if (effectiveFirstSync && remoteProject == “ESIC” && !alreadySynchronized) {
def assetParent = replica.customFields.‘Asset’?.value?.parent?.value
def assetChild = replica.customFields.‘Asset’?.value?.child?.value

if (!assetParent) {
    debug.error("Blocage création : Le champ parent du composant 'Asset' est vide ou non renseigné pour la synchronisation ESIC -> CEP. Parent: ${assetParent}, Enfant: ${assetChild}")
    syncHelper.unExalateAfterProcessing()
    return
}
if (!assetChild) {
    debug.error("Blocage création : Le champ enfant du composant 'Asset' est vide ou non renseigné pour la synchronisation ESIC -> CEP. Parent: ${assetParent}, Enfant: ${assetChild}")
    syncHelper.unExalateAfterProcessing()
    return
}
if (!VALID_ASSETS.contains(assetParent) || assetParent == 'Autre') {
    debug.error("Blocage création : Valeur du champ parent non autorisée pour la synchronisation.")
    syncHelper.unExalateAfterProcessing()
    return
}
if (assetChild == 'Reporting quotidien' || assetChild == 'Autre' || !VALID_SOUS_COMPOSANTS.contains(assetChild)) {
    debug.error("Blocage création : Valeur du champ enfant non autorisée pour la synchronisation.")
    syncHelper.unExalateAfterProcessing()
    return
}

}

// === Champs de base ===
issue.summary = replica.summary
issue.description = replica.description
issue.labels = replica.labels
issue.attachments = attachmentHelper.mergeAttachments(issue, replica)
issue.issueLinks = replica.issueLinks

// === Fusion des commentaires ===
replica.addedComments.each { it.executor = nodeHelper.getUser(it.author?.key) }
replica.changedComments.each { it.executor = nodeHelper.getUser(it.updateAuthor?.key) }
issue.comments = commentHelper.mergeComments(issue, replica, { it })

if (effectiveFirstSync && remoteProject == “ESIC”) {
issue.projectKey = “CEP”
issue.typeName = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: “Incident”
issue.customFields.“Projet demandeur”.value = ‘ESIC’
syncHelper.syncBackAfterProcessing()
}
if (effectiveFirstSync && remoteProject == “CEP”) {
issue.projectKey = “ESIC”
issue.typeName = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: “Incident”
issue.customFields.“Projet demandeur”.value = ‘CEP’
syncHelper.syncBackAfterProcessing()
}

if (remoteProject == “CEP”) {
// CEP → ESIC : CM Composant (CEP) → Asset (ESIC)
def cmParent = replica.customFields.‘CM Composant’?.value?.parent?.value
def cmChild = replica.customFields.‘CM Composant’?.value?.child?.value
if (cmParent && cmChild) {
// Map CM Composant (customfield_10140) to Asset (customfield_10291)
def parentOption = nodeHelper.getOption(issue, 10291L, cmParent)
def childOption = parentOption?.childOptions?.find { it.value == cmChild }
if (parentOption && childOption) {
issue.customFields[“10291”].value = nodeHelper.getCascadingSelect(parentOption, childOption)
} else {
issue.customFields[“10291”].value = null
}
} else {
issue.customFields[“10291”].value = null
}
issue.customFields[“10291”].value = issue.customFields.‘10291’?.value
issue.customFields.“Temps d’intervention ( en mn)”.value = replica.customFields.“Temps d’intervention exploitant (en mn)”?.value
issue.customFields.“Domaine impacté”.value = replica.customFields.“Domaine”?.value?.value
issue.customFields.“Séverité”.value = replica.customFields.“Sévérité”?.value?.value
issue.customFields.“Motif annulation”.value = replica.customFields.“Motif Canceled”?.value?.value
setMandatoryDetectionDate(issue, ‘10052’, replica.customFields.“Date et heure de détection”?.value)
if (remoteStatus == “Résolu”) {
issue.customFields.“Type root cause”.value = replica.customFields.“Root Cause Incident”?.value?.value
issue.customFields.“Description root cause”.value = replica.customFields.“[Root] cause de l’incident”?.value
issue.customFields.“Date de résolution” = replica.customFields.“Date de résolution”
issue.customFields.“Temps de résolution (en mn)” = replica.customFields.“Temps de résolution (en mn)”
issue.customFields.“Durée d’indisponibilité (en mn)” = replica.customFields.“Durée d’indisponibilité (en mn)”
issue.customFields.“Temps d’intervention ( en mn)” = replica.customFields.“Temps d’intervention ( en mn)”
issue.customFields.“Crise” = replica.customFields.“Crise”
}
if (effectiveFirstSync) {
issue.customFields.“Environnement(s) de détection”.value = “PRODUCTION”
issue.customFields.“Catégorisation”.value = “A définir”
}
issue.reporter = getValidUserOrDefault(replica.reporter?.key, defaultReporter)
issue.assignee = getValidUserOrDefault(replica.assignee?.key, defaultReporter)
def mappedTransition = transitionMap_CEP_ESIC[remoteStatus]
if (mappedTransition) {
try {
workflowHelper.transition(issue, mappedTransition)
} catch (Exception e) {
// Transition not available, ignore
}
}
def ticketMaster = issue.customFields.“Projet demandeur”?.value
if ((cmParent != VALID_ASSET_PARENT || !VALID_SOUS_COMPOSANTS.contains(cmChild)) && issue.status?.name != “Annulé” && ticketMaster == ‘CEP’) {
issue.customFields.‘Motif annulation’.value = “Erreur de routage”
try {
workflowHelper.transition(issue, ‘Annuler’)
} catch (Exception e) {
// Transition not available, ignore
}
issue.comments = commentHelper.addComment(“On annule l’incident car l’Asset de ticket CEP n’est pas compatible avec ce projet”, issue.comments)
issue.customFields.“Ticket CEP”.value = null
Thread.sleep(2000)
syncHelper.unExalateAfterProcessing()
} else {
issue.customFields.“Ticket CEP”.value = remoteKeyUrl
}
} else if (remoteProject == “ESIC”) {
// ESIC → CEP : Asset (ESIC) → CM Composant (CEP)
def assetParent = replica.customFields.‘Asset’?.value?.parent?.value
def assetChild = replica.customFields.‘Asset’?.value?.child?.value
if (assetParent && assetChild && VALID_SOUS_COMPOSANTS.contains(assetChild)) {
// Map Asset (customfield_10291) to CM Composant (customfield_10140)
def parentOption = nodeHelper.getOption(issue, 10140L, VALID_ASSET_PARENT)
def childOption = parentOption?.childOptions?.find { it.value == assetChild }
if (parentOption && childOption) {
issue.customFields[“10140”].value = nodeHelper.getCascadingSelect(parentOption, childOption)
} else {
issue.customFields[“10140”].value = null
}
} else {
issue.customFields[“10140”].value = null
}
issue.customFields[“10140”].value = issue.customFields.‘10140’?.value
issue.customFields.“Temps d’intervention exploitant (en mn)”.value = replica.customFields.“Temps d’intervention ( en mn)”?.value
issue.customFields.“Domaine”.value = replica.customFields.“Domaine impacté”?.value?.value
setMandatoryDetectionDate(issue, ‘10052’, replica.customFields.“Date et heure de Détection”?.value)
issue.customFields.“Sévérité”.value = replica.customFields.“Sévérité”?.value?.value
issue.customFields.“Motif Canceled”.value = replica.customFields.“Motif annulation”?.value?.value
if (remoteStatus == “Resolved”) {
issue.customFields.“Root Cause Incident”.value = replica.customFields.“Type root cause”?.value?.value
issue.customFields.“[Root] cause de l’incident”.value = replica.customFields.“Description root cause”?.value
issue.customFields.“Date de résolution” = replica.customFields.“Date de résolution”
issue.customFields.“Temps de résolution (en mn)” = replica.customFields.“Temps de résolution (en mn)”
issue.customFields.“Durée d’indisponibilité (en mn)” = replica.customFields.“Durée d’indisponibilité (en mn)”
issue.customFields.“Temps d’intervention ( en mn)” = replica.customFields.“Temps d’intervention ( en mn)”
issue.customFields.“Crise” = replica.customFields.“Crise”
}
issue.reporter = getValidUserOrDefault(replica.reporter?.key, defaultReporterESIC)
issue.assignee = getValidUserOrDefault(replica.assignee?.key, defaultReporterESIC)
def mappedTransition = transitionMap_CEP_ESIC[remoteStatus]
if (mappedTransition) {
try {
workflowHelper.transition(issue, mappedTransition)
} catch (Exception e) {
// Transition not available, ignore
}
}
def ticketMaster = issue.customFields.“Projet demandeur”?.value
if ((assetParent && !VALID_ASSETS.contains(assetParent)) && issue.status?.name != “Canceled” && ticketMaster == ‘ESIC’) {
issue.customFields.‘Motif Canceled’.value = “Erreur de routage”
try {
workflowHelper.transition(issue, ‘Cancel’)
} catch (Exception e) {
// Transition not available, ignore
}
issue.comments = commentHelper.addComment(“On annule l’incident car l’Asset de ticket ESI n’est pas compatible avec ce projet”, issue.comments)
issue.customFields.“Ticket ESI”.value = null
Thread.sleep(2000)
syncHelper.unExalateAfterProcessing()
} else {
issue.customFields.“Ticket ESI”.value = remoteKeyUrl
}
}
And here is the new error message:

com.exalate.api.exception.script.IssueTrackerScriptException:
Blocage création : The child field of component 'CM Composant' is empty or not set for CEP -> ESIC synchronization.
Parent: Comutitres - Exploitation - SI, Child: null

Caused by: com.exalate.api.exception.IssueTrackerException:
Blocage création : The child field of component 'CM Composant' is empty or not set for CEP -> ESIC synchronization.
Parent: Comutitres - Exploitation - SI, Child: null