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

Hi @Ulrich,

It looks like you’ve already addressed the earlier errors, which is good:

  • the 33 errors about the mandatory cascading select field 10140

  • and the 14 errors about the missing Re-ouvrir transition

From what you’ve shared, those do not seem to be the current blocker anymore.

At this point, the only error present is actually your own: debug.error(...), which means the condition you added is now being matched and the script is intentionally stopping the sync.

Could you confirm what custom field type CM Composant is on the source side? If it is a cascading select, then this access pattern may be correct, but we should confirm that the value is actually being accessed here:

def cmParent = replica.customFields.'CM Composant'?.value?.parent?.value
def cmChild = replica.customFields.'CM Composant'?.value?.child?.value

Also, is cmChild being null an invalid situation in this sync? Or was this added just for troubleshooting. If a null child can be expected in some cases, I would remove the debug.error(...) so it does not block the sync.

Hello Javier,

Thanks for confirming.

CM Composant is a cascading select field on the source side and it is mandatory.
Therefore, cmChild must not be null in this sync.

The debug.error(...) was added intentionally to enforce this rule and to stop the synchronization when the cascading field is not fully populated (parent + child).

Also the CM parent is valid only when the value is Comutitres - Exploitation - SI.