1
0
-1

We have an integration between Zendesk and ServiceNow with custom fields on both sides for the corresponding ticket numbers from the other application. When we push a ticket from one application to the other the ticket is created in the target application as expected. However, the custom field in originating application is not populated with the associated ticket number until a user updates the ticket created in the target application. So if I push a ticket from Zendesk to SN and a ticket is created I can see the SN case number in the Exahalate applet but the custom field on the Zendesk ticket remains empty even though I can see the SN Case number in the Incoming Sync. Then when someone updates the ticket on the SN side the field populates in Zendesk as expected. The incoming sync data looks the same both times. The same behavior in reverse occurs when a ticket is pushed from ServiceNow from Zendesk the data is present in the first sync but the custom field in the originating application doesn't populate until the child ticket is "updated"  Note, the same Custom Field in the child application does populate with expected data.


Zendesk Incoming Sync. 

issue.customFields."SN Correlation Number".value = replica.number
issue.customFields."Customer Reference".value = replica.number


Sync Data present in both feeds.
    "location_street_address": "1050 SW Spokane Street",
    "impact": "2 - Medium",
    "zendesk_ticket_id": "16273",
    "location_confluence_page": "",
    "cause": "",
    "location_state": "WA",
    "product_name": "Mainsail",
    "close_notes": "",
    "account_is_affiliate": "true",
   ** "number": "CS0341977",**
    "caller": "Champ.Yarbrough@tideworks.com",
    "location_name": "Terminal 18 (t18)",
  1. Ariel Aguilar

    Hi Champ,

    Could you please be specific on what incoming, outgoing you are using on both sides concerning these custom fields?

    Kind regards,

    Ariel

  2. Champ Yarbrough

    I don't understand what you asking.


  3. Jose Lobo

    Hey Champ Yarbrough 

    Long time no see.

    Ariel Aguilar is asking about the scripts on each side.

CommentAdd your comment...

1 answer

  1.  
    1
    0
    -1

    Syncs between ServiceNow and Zendesk

    ServiceNow

    https://snownode-kurd-koft-ycie-fail.exalate.cloud/connections/3

     Outgoing Sync

    if(entityType == "customerCase") {

        replica.key            = customerCase.sys_id

        replica.zendesk_ticket_id = customerCase.correlation_id

        //debug.error("replica.zendesk_ticket_id = ${customerCase.correlation_id}")

        replica.number         = customerCase.number

        replica.summary        = customerCase.short_description

        replica.description    = customerCase.description

        replica.state          = customerCase.state

        replica.attachments    = customerCase.attachments

        replica.comments       = customerCase.comments

        replica.impact         = customerCase.impact

        replica.urgency        = customerCase.urgency

        replica.priority       = customerCase.priority

        replica.assignment_group = customerCase.assignment_group //?.display_value

        replica.close_code     = customerCase.resolution_code // resolution type, drop down

        replica.close_notes    = customerCase.close_notes  // Resolution notes, when resolved

        replica.cause          = customerCase.cause // Resolution Detail 

        //if ((customerCase.watch_list) != "")

        //    replica.watchers   = customerCase.watch_list.split();

       

        if (!(customerCase.u_caller instanceof String)) {

            replica.caller = customerCase.u_caller?.display_value

        }


        // Customer/Account

        if (!(customerCase.account instanceof String)) {

            def account = nodeHelper.getTableByLink(customerCase.account?.link)

            replica.account_friendly_name = account?.name //Defined by Bizdev, if a managed customer.  Otherwise, I used the org chart from legal.

            replica.account_number = account?.number //optional. reflects the customer/department number if assigned by accounting.  Or the auto-generated number if Accounting hasn't defined one

            replica.account_legal_name = account?.u_legal_name // optional. legal name for account, if known

            replica.account_is_affiliate = account?.u_affiliate // Is this an affiliate or external customer?

        }   

       

       

        // location

        if (!(customerCase.location instanceof String)) {

            def location = nodeHelper.getTableByLink(customerCase.location?.link)

            replica.location_name = location?.name //Defined by Bizdev, if a managed customer.  Otherwise, I used the org chart from legal.

            replica.location_street_address = location?.street //optional.

            replica.location_city = location?.city // optional.

            replica.location_state = location?.state // optional.

            replica.location_web_site = location?.web_site // optional.

            replica.location_confluence_page = location?.u_confluence // optional.

        }



        // Assigned to

        //throw new Exception("assigned_to = ${customerCase.assigned_to.link}")

        if (!(customerCase.assigned_to instanceof String)) {

            replica.assigned_to = nodeHelper.getTableByLink(customerCase.assigned_to?.link)?.email

        }

       

        // Product (Mainsail, iPro, etc)

        if (!(customerCase.product instanceof String)) {

            def product = nodeHelper.getTableByLink(customerCase.product?.link)

            replica.product_name = product?.name

            replica.product_version = product?.version // if known/tracked within SN

        }

        // Jira issue

       

        if (!(customerCase.u_jira_issue instanceof String)) {

            //throw new Exception("u_jira_issue = ${customerCase.u_jira_issue}")

            def u_jira_issue = nodeHelper.getTableByLink(customerCase.u_jira_issue?.link)

            replica.jira_issue     = u_jira_issue?.number

        }

       

        if (!(customerCase.incident instanceof String)) {

            replica.incident     = nodeHelper.getTableByLink(customerCase.incident?.link)?.number

        }

       

    }



    if(entityType == "incident") {

        replica.key            = incident.key

        replica.status         = incident.status

        replica.state          = incident.state

       

        if (!(incident.parent instanceof String)) {

            replica.number     = nodeHelper.getTableByLink(incident.parent?.link)?.number

        }


        replica.incident       = incident.number

        replica.summary        = incident.short_description

        replica.description    = incident.description

        replica.attachments    = incident.attachments

        replica.comments       = incident.comments

        replica.impact         = incident.impact

        replica.urgency        = incident.urgency

        replica.priority = incident.priority

       

        //replica.basepriority = incident.priority?: "null value" // results in: "null value"

        replica.priority = incident.priority //?: "null value" // results in: Cannot cast object 'null value' with class 'java.lang.String' to class 'com.exalate.api.domain.hubobject.v1_2.IHubPriority'

        //replica.im = incident.internalMap // results in: java.lang.NullPointerException.


        replica.assignment_group = incident.assignment_group //?.display_value

        replica.close_code = incident.resolution_code // resolution type, drop down

        replica.close_notes = incident.close_notes  // Resolution notes, when resolved

        if (!(incident.u_caller instanceof String)) {

            replica.caller = incident.u_caller?.display_value

        }



        // Prep some variables that we'll reuse for handling reference fields

        def link

        def parsedLink

        def record

       

        // Customer/Account

        if (!(incident.company instanceof String)) {

            def account = nodeHelper.getTableByLink(incident.company?.link)

            replica.account_friendly_name = account?.name //Defined by Bizdev, if a managed customer.  Otherwise, I used the org chart from legal.

            replica.account_number = account?.number //optional. reflects the customer/department number if assigned by accounting.  Or the auto-generated number if Accounting hasn't defined one

            //replica.account_parent = account?.account_parent // optional. account parent reflects hierarchy

            replica.account_legal_name = account?.u_legal_name // optional. legal name for account, if known

            replica.account_is_affiliate = account?.u_affiliate // Is this an affiliate or external customer?

        }   

       

        // location

        if (!(incident.location instanceof String)) {

            def location = nodeHelper.getTableByLink(incident.location?.link)

            replica.location_name = location?.name //Defined by Bizdev, if a managed customer.  Otherwise, I used the org chart from legal

        }



        // Assigned to

        //throw new Exception("assigned_to = ${incident.assigned_to.link}")

        if (!(incident.assigned_to instanceof String)) {

            def assigned_to = nodeHelper.getTableByLink(incident.assigned_to?.link)

            replica.assigned_to = assigned_to?.email

        }

       

        // Product (Mainsail, iPro, etc)

        if (!(incident.u_cmdb_model instanceof String)) {

            def cmdb_model = nodeHelper.getTableByLink(incident.u_cmdb_model?.link)

            replica.product_name = cmdb_model?.name

            replica.product_version = cmdb_model?.version // if known/tracked within SN

        }


    }


    if(entityType == "problem") {

        replica.key            = problem.key

        replica.summary        = problem.short_description

        replica.description    = problem.description

        replica.attachments    = problem.attachments

        replica.comments       = problem.comments

    }


    if(entityType == "changeRequest") {

        replica.key            = changeRequest.key

        replica.summary        = changeRequest.short_description

        replica.description    = changeRequest.description

        replica.attachments    = changeRequest.attachments

        replica.comments       = changeRequest.comments

    }



    if(entityType == "requestItem") {

        replica.key            = requestItem.key

        replica.summary        = requestItem.short_description

        replica.description    = requestItem.description

        replica.attachments    = requestItem.attachments

        replica.comments       = requestItem.comments


    Incoming Sync

    def defaultEntityType = "customerCase"

    if(firstSync) entityType = defaultEntityType else entityType


    if(entityType == "customerCase") {

        if (replica.reporter && !customerCase.u_caller){

            String strCaller = replica.reporter.email?.trim()

            //throw new Exception("strCaller = ${strCaller.class} ${strCaller}")

            // if (strCaller && strCaller.equalsIgnoreCase("champ.yarbrough@tideworks.com"))

            customerCase.u_caller = strCaller

        }


        customerCase.short_description = replica.summary

        if (!customerCase.description)

            customerCase.description = replica.description

        //customerCase.attachments += replica.addedAttachments

       

    // kcs test 1/27/21 from https://docs.idalko.com/exalate/display/ED/How+to+sync+comments+in+ServiceNow

        customerCase.correlation_id = replica.key

        customerCase.correlation_display = 'zendesk'

        /*

        // kcs 11/18/20 - Discussed with Champ and agreed we would NOT use assignee/assignment group to track ownership

        // reason being, IT Service Desk will be needed to liase between CS and IT

        if (!(replica.assignee instanceof String)) {

        customerCase.assigned_to = replica.assignee?.email

        }

        */


        def productMap = [

            "intermodal_pro" : "Tideworks Intermodal Pro (Ipro)",

            "mainsail" : "Tideworks Mainsail",

            "spinnaker" : "Tideworks Spinnaker",

            "traffic_control" : "Tideworks Traffic Control",

            "beacon" : "Tideworks Beacon",

            "edi_porter" : "Tideworks EDI Porter",

            "terminal_view" : "Tideworks Terminal View",

            "sim" : "Tideworks System Integration Module (SIM)",

            "buoy" : "Tideworks Buoy",

            "logbook" : "Tideworks Logbook",

            "forecast" : "Tideworks Forecast",

            "gatevision" : "Tideworks GateVision",

            "insight" : "Tideworks Insight",

            "mainsail_billing" : "Tideworks Mainsail Billing",

            "scale_weight_reader" : "Tideworks Scale Weight Reader",

            "digital_bridge" : "Tideworks Digital Bridge",

            "genoa" : "Tideworks Genoa",

            "timetracker" : "Tideworks TimeTracker",

            "enterprise_custom_solutions___docmar" : "Tideworks DOCMAR",

            "enterprise_custom_solutions___ediaap" : "Tideworks EDIaaP",

            "enterprise_custom_solutions___ediaas" : "Tideworks EDIaaS",

            "enterprise_custom_solutions___facts_state" : "Tideworks FACTS State",

            "enterprise_custom_solutions___incident_report" : "Tideworks Incident Report",

            "enterprise_custom_solutions___liba" : "Tideworks LIBA",

            "enterprise_custom_solutions___scap" : "Tideworks SCAP",

            "enterprise_custom_solutions___sicrefis" : "Tideworks SICREFIS",

            "enterprise_custom_solutions___snap" : "Tideworks SNAP",

            "zendesk" : "Zendesk"

        ]

        def productName = productMap[replica.tideworks_application?.value?.value]

        customerCase.product = productName


        def priorityMap = [

            // remote side priority <-> local side priority

            "urgent" : "Critical",

            "high" : "Important",

            "normal" : "Normal",

            "low" : "Informational"

        ]

        def priorityName = priorityMap[replica.priority?.name] ?: "Normal"

        customerCase.priority = nodeHelper.getPriority(priorityName)


        def locationMap = [

            "361082465291" : "Seattle - Main (sea)",

            "361142948111" : "Bethlehem",

            "361142948111" : "Buffalo",

            "361142948131" : "Chambersburg",

            "361142948151" : "Detroit",

            "361142948171" : "East St Louis",

            "361142948211" : "Evansville",

            "361142948211" : "Fairburn",

            "361142948231" : "Indianapolis",

            "361142948271" : "Louisville (LOU)",

            "361142948291" : "Memphis",

            "361142948311" : "Nashville",

            "361142948351" : "North Bergen",

            "361142948371" : "NWOH",

            "361142948391" : "Philadelphia",

            "361142948451" : "Savannah",

            "361142948471" : "South Kearny",

            "361142948491" : "Springfield",

            "361142948511" : "Syracuse",

            "361142948511" : "Tacoma Terminal (tac)",

            "361142948531" : "Tampa",

            "361142948591" : "Winterhaven",

            "361142948611" : "Worcester (csxw)",

            "361142948651" : "Port San Diego",

            "361142948671" : "Freeport (fpo)",

            "361142948691" : "Guayaquil (gye)",

            "361142948711" : "Gulfport (gpt)",

            "361142948751" : "Port Everglades (pef)",

            "361142948811" : "Westlake Village",

            "361142948831" : "Port of Wilmington (dwil)",

            "361142948851" : "Portroe",

            "361142948871" : "DRS - Philadelphia",

            "361142948891" : "DRS/Tioga - Philadelphia",

            "361142948911" : "Florida International Terminal (fit)",

            "361142948951" : "Freightliner - Birmingham Terminal (bift)",

            "361142948971" : "Freightliner - Bristol Railport",

            "361142949011" : "Freightliner - Cleveland Terminal",

            "361142949031" : "Freightliner - Coatbridge Railport",

            "361142949051" : "Freightliner - Corporate",

            "361142949071" : "Freightliner - Daventry",

            "361142949091" : "Freightliner - Ditton",

            "361142949111" : "Freightliner - Leeds Terminal",

            "361142949131" : "Freightliner - Liverpool Terminal",

            "361142949151" : "Freightliner - Manchester Terminal",

            "361142949171" : "Freightliner - Scunthorpe",

            "361142949191" : "Freightliner - Southampton",

            "361142949191" : "Freightliner - Thamesport Terminal",

            "361142949191" : "Southhampton",

            "361142949211" : "Freightliner - Thamesport",

            "361142949231" : "Freightliner - Tilbury Ramp",

            "361142949251" : "Kingston Wharves Ltd (kwl)",

            "361142949271" : "Koniambo Nickel SAS (kns)",

            "361142949291" : "Little Ferry",

            "361142949291" : "Luka Koper (kop)",

            "361142949311" : "Conley Terminal (mct)",

            "361142949331" : "Fergusson Container Terminal",

            "361142949351" : "New Orleans",

            "361142949351" : "New Orleans Terminal (nol)",

            "361142949371" : "POMTOC",

            "361142949391" : "Port Everglades Terminal",

            "361142949391" : "Port Everglades Terminal (pet)",

            "361142949411" : "Port Lafito Haiti (plh)",

            "361142949451" : "North Intermodal Rail Yard (nim)",

            "361142949491" : "PSA Halifax (hal)",

            "361142949511" : "Red Hook Container Terminal (red)",

            "361142949531" : "Santa Marta International Terminal (smit)",

            "361142949551" : "SCIP - Dillon (ipd)",

            "361142949571" : "SCPA - North Charleston Terminal (nit)",

            "361142949591" : "Wando Welch Terminal (wwt)",

            "361142949611" : "Jacksonville Blount Island  (bij)",

            "361142949611" : "SSA Atlantic - Jacksonville Blount Island  (bij)",

            "361142949631" : "SSA Marine - SLC",

            "361142949651" : "Manzanillo (zlo)",

            "361142949651" : "SSA Mexico - Manzanillo (zlo)",

            "361142949671" : "SSA Panama - Colon (mit)",

            "361142949691" : "SSA SV International Chile Limitada",

            "361142949711" : "B63",

            "361142949731" : "C60 ",

            "361142949751" : "Matson Tacoma (TAM)",

            "361142949751" : "SSAT - Matson Tacoma (TAM)",

            "361142949771" : "Oakland International Container Terminal (oict)",

            "361142949771" : "PCT",

            "361142949771" : "SSAT - Oakland International Container Terminal (oict)",

            "361142949791" : "SSAT - PCT",

            "361142949811" : "SSAT - Terminal 5 (T5)",

            "361142949831" : " Terminal 18 (t18)",

            "361142949851" : "Terminal 25/30 (t30)",

            "361142949871" : "Terminal A (pa)",

            "361142949891" : "SSIT - Vietnam",

            "361142949911" : "STE - Alameda (sta)",

            "361142949951" : "Carson (stl)",

            "361142949951" : "STE - Carson (stl)",

            "361142949971" : "French Camp (stf)",

            "361142949971" : "STE - French Camp (stf)",

            "361142949991" : "STE - Middle Road",

            "361142950011" : "Oakland (sto)",

            "361142950011" : "STE - Oakland (sto)",

            "361142950031" : "STE - Tukwila (sts)",

            "361142950031" : "Tukwila (sts)",

            "361142950031" : "Vale Nouvelle Caledonie",

            "361142950051" : "STE - Wilmington (stw)",

            "361142950051" : "Wilmington (stw)",

            "361142950071" : "STI - San Antonio Terminal Internacional (sai)",

            "361142950091" : "SVTI - San Vicente Terminal Internacional",

            "361142950111" : "Terminal 54/55 (b54)",

            "361142950171" : "Anchorage Office/Terminal (anc)",

            "361142950211" : "San Juan Administrative Office (ssl)",

            "361142950291" : "Trapac LA",

            "361142950311" : "Tuxpan Terminal (tpt)",

            "361142950351" : "YTI - Oakland",

            "361145089832" : "Advent Intermodal Solutions (emod)",

            "361145089852" : "Baltic Container Terminal (bct)",

            "361145089872" : "Barranquilla Container Terminal - (bitco)",

            "361145089932" : "BNSF (ssih) South Seattle Intermodal Facility",

            "361145089972" : "CN Headquarters",

            "361145090032" : "Crowley - San Juan (sjc)",

            "361145090052" : "Portsmouth",

            "361145090072" : "Main Office Jacksonville",

            "361159557252" : "BNSF - Logistics Park Chicago (lpc)",

            "361174665391" : "Freightliner - Doncaster Railport",

            "361174665931" : "Freightliner - Teesport",

            "361177035652" : "Felixstowe",

            "361177035772" : "Freightliner - Hams Hall",

            "361177578311" : "Bessemer Terminal",

            "361177583691" : "Bedford Park",

            "361177584291" : "Cincinnati",

            "361177585351" : "Cleveland",

            "361177586711" : "Columbus (colm)",

            "361177593511" : "Jacksonville",

            "361179834732" : "Charlotte",

            "361179835432" : "Charleston",

            "361179837652" : "Chicago 59th Street",

            "361179865332" : "BNSF (sig) Seattle International Gateway",

            "361179865332" : "BNSF Railway Main Office (BN01)",

            "361180142931" : "Groupe Noumea Port (gnp)",

            "361182010311" : "Gearbulk - Pasir Gudang (atm) ZD only",

            "361182016391" : "Gearbulk - Port Manatee (pmtf)",

            "361182037551" : "Batumi International Container Terminal (bict)",

            "361184195972" : "Gearbulk - Lake Charles (lcct)",

            "361145089992" : "Talleyrand (jac)",

            "361142950191" : "Blount Island Terminal",

            "361145089832" : "Advent Intermodal Solutions (emod)",

            "361234218192" : "Matson Navigation"

        ]

        String orgID = String.format ("%.0f", replica.organization);

        //throw new Exception("orgID = ${orgID.class} ${orgID}")

        def locationName = locationMap[orgID]

        if (locationName && !customerCase.location) {

            // update case ONLY if Zendesk has a location, and the Case does not

            // throw new Exception("locationName = ${locationName.class} ${locationName}")

            customerCase.location = locationName

        }

      

        if (!customerCase.account) {

            if (orgID == "361082465291") {

                // Tideworks Customer Success

                customerCase.account = "Tideworks Administration"

            }

            else if (orgID) {

                // use the primary "Account" for {$locationName}

                def accountRef = nodeHelper.getReference("cmn_location", "name", locationName)?.account

                //throw new Exception("accountRef = ${accountRef.class} ${accountRef}")

                if (accountRef){

                    def accountName = nodeHelper.getTableByLink(accountRef?.link)?.name

                    //throw new Exception("accountName = ${accountName}")

                    customerCase.account = accountName

                }

            }

        }

       


        // prefill variables from source/target

        String remoteStatus = replica?.status?.name

        String localStatus = customerCase?.state

       

        String whoIsMaster = "localMaster" // default to local master

        // Set remote master only if the case is assigned to Customer Success

        if (!(customerCase?.assignment_group instanceof String) && (customerCase?.assignment_group?.display_value == "Customer Success Support"))

            whoIsMaster = "remoteMaster"

           

        //throw new Exception("Assignment Group = ${customerCase?.assignment_group}")

       

        // validate existing ZD & SN statuses

        def validZdStatus = ["new", "open", "pending", "solved", "closed"]

        def validSnStatus = ["New", "Open", "Awaiting Info",  "Solution Proposed", "Closed"]

        if (!(remoteStatus in validZdStatus))

            remoteStatus = "default"

        if (!(localStatus in validSnStatus))

            localStatus = "default"

       

        // Build my conditional mapping rules

        def statusMap = [

            localMaster:[

                new:[

                    "New" :             [newStatus:"New",               routeCommentTo:"comments",  needsAttention:null],

                    "Open" :            [newStatus:"Open",              routeCommentTo:"comments",  needsAttention:null],

                    "Awaiting Info" :   [newStatus:"Open",              routeCommentTo:"comments",  needsAttention:true],

                    "Solution Proposed":[newStatus:"Solution Proposed", routeCommentTo:"comments",  needsAttention:null],

                    "Closed" :          [newStatus:"Closed",            routeCommentTo:null,        needsAttention:null],

                    "default" :         [newStatus:null,                routeCommentTo:"comments",  needsAttention:null] // default

                ],

                open:[

                    "New" :             [newStatus:null,                routeCommentTo:"comments",  needsAttention:true],

                    "Open" :            [newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Awaiting Info" :   [newStatus:"Open",              routeCommentTo:"comments",  needsAttention:true],

                    "Solution Proposed":[newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Closed" :          [newStatus:null,                routeCommentTo:null,        needsAttention:null],

                    "default" :         [newStatus:null,                routeCommentTo:"comments",  needsAttention:null] // default

                ],

                pending:[

                    "New" :             [newStatus:"Awaiting Info",     routeCommentTo:"comments",  needsAttention:true],

                    "Open" :            [newStatus:"Awaiting Info",     routeCommentTo:"comments",  needsAttention:null],

                    "Awaiting Info" :   [newStatus:null,                routeCommentTo:"comments",  needsAttention:true],

                    "Solution Proposed":[newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Closed" :          [newStatus:null,                routeCommentTo:null,        needsAttention:null],

                    "default" :         [newStatus:null,                routeCommentTo:"comments",  needsAttention:null] // default

                ],

                solved:[

                    "New" :             [newStatus:null,                routeCommentTo:"comments",  needsAttention:true],

                    "Open" :            [newStatus:null,                routeCommentTo:"comments",  needsAttention:true],

                    "Awaiting Info" :   [newStatus:"Open",              routeCommentTo:"comments",  needsAttention:true],

                    "Solution Proposed":[newStatus:null,                routeCommentTo:null,        needsAttention:null],

                    "Closed" :          [newStatus:null,                routeCommentTo:null,        needsAttention:null],

                    "default" :         [newStatus:null,                routeCommentTo:"comments",  needsAttention:null] // default

                ],

                closed:[

                    "New" :             [newStatus:null,                routeCommentTo:"comments",  needsAttention:true],

                    "Open" :            [newStatus:null,                routeCommentTo:"comments",  needsAttention:true],

                    "Awaiting Info" :   [newStatus:"Open",              routeCommentTo:"comments",  needsAttention:true],

                    "Solution Proposed":[newStatus:null,                routeCommentTo:null,        needsAttention:null],

                    "Closed" :          [newStatus:null,                routeCommentTo:null,        needsAttention:null],

                    "default" :         [newStatus:null,                routeCommentTo:"comments",  needsAttention:null] // default

                ],

                default:[

                    "New" :             [newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Open" :            [newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Awaiting Info" :   [newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Solution Proposed":[newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Closed" :          [newStatus:null,                routeCommentTo:null,        needsAttention:null],

                    "default" :         [newStatus:null,                routeCommentTo:"comments",  needsAttention:null] // default]

                ]

            ],

            remoteMaster:[

                new:[

                    "New" :             [newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Open" :            [newStatus:"New",               routeCommentTo:"comments",  needsAttention:null],

                    "Awaiting Info" :   [newStatus:"New",               routeCommentTo:"comments",  needsAttention:null],

                    "Solution Proposed":[newStatus:"New",               routeCommentTo:"comments",  needsAttention:null],

                    "Closed" :          [newStatus:"New",               routeCommentTo:"comments",  needsAttention:null],

                    "default" :         [newStatus:"New",               routeCommentTo:"comments",  needsAttention:null] // default

                ],

                open:[

                    "New" :             [newStatus:"Open",              routeCommentTo:"comments",  needsAttention:null],

                    "Open" :            [newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Awaiting Info" :   [newStatus:"Open",              routeCommentTo:"comments",  needsAttention:null],

                    "Solution Proposed":[newStatus:"Open",              routeCommentTo:"comments",  needsAttention:null],

                    "Closed" :          [newStatus:"Open",              routeCommentTo:"comments",  needsAttention:null],

                    "default" :         [newStatus:"Open",              routeCommentTo:"comments",  needsAttention:null] // default

                ],

                pending:[

                    "New" :             [newStatus:"Awaiting Info",     routeCommentTo:"comments",  needsAttention:null],

                    "Open" :            [newStatus:"Awaiting Info",     routeCommentTo:"comments",  needsAttention:null],

                    "Awaiting Info" :   [newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Solution Proposed":[newStatus:"Awaiting Info",     routeCommentTo:"comments",  needsAttention:null],

                    "Closed" :          [newStatus:"Awaiting Info",     routeCommentTo:"comments",  needsAttention:null],

                    "default" :         [newStatus:"Awaiting Info",     routeCommentTo:"comments",  needsAttention:null] // default

                ],

                solved:[

                    "New" :             [newStatus:"Solution Proposed", routeCommentTo:"close_notes",needsAttention:null],

                    "Open" :            [newStatus:"Solution Proposed", routeCommentTo:"close_notes",needsAttention:null],

                    "Awaiting Info" :   [newStatus:"Solution Proposed", routeCommentTo:"close_notes",needsAttention:null],

                    "Solution Proposed":[newStatus:null,                routeCommentTo:"comments",   needsAttention:null],

                    "Closed" :          [newStatus:"Solution Proposed", routeCommentTo:"comments",   needsAttention:null],

                    "default" :         [newStatus:"Solution Proposed", routeCommentTo:"comments",   needsAttention:null] // default

                ],

                closed:[

                    "New" :             [newStatus:"Closed",            routeCommentTo:"close_notes",needsAttention:null],

                    "Open" :            [newStatus:"Closed",            routeCommentTo:"close_notes",needsAttention:null],

                    "Awaiting Info" :   [newStatus:"Closed",            routeCommentTo:"close_notes",needsAttention:null],

                    "Solution Proposed":[newStatus:"Closed",            routeCommentTo:"comments",   needsAttention:null],

                    "Closed" :          [newStatus:null,                routeCommentTo:null,        needsAttention:null],

                    "default" :         [newStatus:"Closed",            routeCommentTo:"comments",  needsAttention:null] // default

                ],

                default:[

                    "New" :             [newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Open" :            [newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Awaiting Info" :   [newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Solution Proposed":[newStatus:null,                routeCommentTo:"comments",  needsAttention:null],

                    "Closed" :          [newStatus:null,                routeCommentTo:null,        needsAttention:null],

                    "default" :         [newStatus:null,                routeCommentTo:"comments",  needsAttention:null] // default]

                ]

            ]

        ]

        // Process the conditional mapping rules

        //throw new Exception("statusMap[${whoIsMaster}][${remoteStatus}][${localStatus}]: " + statusMap[whoIsMaster][remoteStatus][localStatus])

        def newStatus = statusMap[whoIsMaster][remoteStatus][localStatus].newStatus

        def routeCommentTo = statusMap[whoIsMaster][remoteStatus][localStatus].routeCommentTo

        def needsAttention = statusMap[whoIsMaster][remoteStatus][localStatus].needsAttention

       

        if (newStatus) { //new ticket state is indicated

            customerCase.state = newStatus

        }

        if (routeCommentTo != null) { //We have a new comment to process

            // Build the comment body

            String commentBuilder = "" // a temp variable to build the new comment text

           

            if (remoteStatus in ["solved", "closed"]){

                // If CS is done, note that fact in the comment text

                def resolutionTypeMap = [

                    "application_code_fix" : "Application",

                    "application___code_fix_release_related" : "Application >  Code Fix Release Related",

                    "application_-_configuration" : "Application > Code Fix",

                    "application__configuration" : "Application > Configuration",

                    "application_performance" : "Application > Performance",

                    "direct_database_change" : "Application > Direct Database Change/Update/Report Creation",

                    "hardware_issue" : "Hardware Issue",

                    "infrastructure_-_network" : "Infrastructure",

                    "infrastructure__database" : "Infrastructure > Database",

                    "infrastructure__firewall" : "Infrastructure > Firewall",

                    "infrastructure__hosted_enviroment" : "Infrastructure > Hosted Enviroment",

                    "infrastructure__server" : "Infrastructure > Server",

                    "third_party_system_camco__mijack__abb__advent__pierpass" : "Third Party Systems",

                    "training" : "Training",

                    "unable_to_replicate" : "Unable to Replicate/Cancelled by User",

                    "user_error" : "User Error"

                ]

               

                def resolutionType

                if (replica.resolution_type && replica.resolution_type.value && replica.resolution_type.value.value)

                    resolutionType = resolutionTypeMap[replica.resolution_type?.value?.value] ?: replica.resolution_type?.value?.value

               

                commentBuilder += "Tideworks Customer Success team has concluded work on your issue"

                if (resolutionType != null)

                    commentBuilder += ", under the category: ${resolutionType}"

                if (replica.resolution_detail != null)

                    commentBuilder += ", with the following resolution note: ${replica.resolution_detail}"

                    //if (replica.addedComments.size() > 0)

                    //commentBuilder += ', with the following comment:  '

                commentBuilder += "<br>"

            }     

           

            // Prep the comment(s) since last sync (usually just one, but it could be multiple)

            if (replica.addedComments.size() > 0) {

                replica.addedComments.collect { comment ->

                    // name author, if identified

                    if (comment.author != null)

                        comment.body = "Comment from " + comment.author.displayName + ": " + comment.body


                    // If ZD Ticket is closed, note that in the first comment. 

                    comment.body = commentBuilder + comment.body

                    commentBuilder = "" // Closure notice only on the first comment (in the event there are multiple)


                    // Cleanup the comment text

                    comment.body = "[code]" + comment.body.replaceAll("\\[code\\]", "").replaceAll("\\[/code\\]", "") + "[/code]"

                }

            }


            customerCase.comments += replica.addedComments


            if (routeCommentTo == "close_notes") {

                //commentBuilder += replica.resolution_detail

                replica.addedComments.collect { comment ->

                    // If ZD Ticket is closed, note that in the first comment. 

                    commentBuilder += comment.body

                }

               

                // Cleanup the close notes

                commentBuilder = commentBuilder.replaceAll("\\[code\\]", "").replaceAll("\\[/code\\]", "")


                //throw new Exception(commentBuilder)

                customerCase.u_close_notes = commentBuilder// + ", with the following comment: <br>" + replica.addedComments[0].body

                customerCase.notes_to_comments = false

                customerCase.resolution_code = "Solved - Fixed by closing related Customer Success ticket"

            }

        }

        if (needsAttention) {

            customerCase.needs_attention = true

        }

    }


    Outgoing Sync

    replica.key            = issue.key

    replica.organization   = issue.organization_id

    replica.assignee       = issue.assignee

    replica.reporter       = issue.reporter

    //replica.requester      = issue.requester

    replica.summary        = issue.summary

    replica.description    = issue.description

    replica.type           = issue.type

    //replica.labels         = issue.labels

    replica.attachments    = issue.attachments

    replica.comments       = issue.comments

    replica.status         = issue.status


    replica.priority       = issue.priority

    if (issue.customFields."Critical"?.value)

      replica.critical     = "critical"

    replica.tideworks_application = issue.customFields."Tideworks Applications"

    replica.resolution_type = issue.customFields."Resolution Type"

    //replica.resolution_code = issue.customFields."Resolution_Code"

    replica.resolution_detail = issue.customFields."Resolution Detail".value



    //Send a Custom Field value

    //replica.customFields."CF Name" = issue.customFields."CF Name"



    https://zendesknode-beer-doit-bury-ceti.exalate.cloud

    Outgoing  Sync

    replica.key            = issue.key

    replica.organization   = issue.organization_id

    replica.assignee       = issue.assignee

    replica.reporter       = issue.reporter

    //replica.requester      = issue.requester

    replica.summary        = issue.summary

    replica.description    = issue.description

    replica.type           = issue.type

    //replica.labels         = issue.labels

    replica.attachments    = issue.attachments

    replica.comments       = issue.comments

    replica.status         = issue.status


    replica.priority       = issue.priority

    if (issue.customFields."Critical"?.value)

      replica.critical     = "critical"

    replica.tideworks_application = issue.customFields."Tideworks Applications"

    replica.resolution_type = issue.customFields."Resolution Type"

    //replica.resolution_code = issue.customFields."Resolution_Code"

    replica.resolution_detail = issue.customFields."Resolution Detail".value



    //Send a Custom Field value

    //replica.customFields."CF Name" = issue.customFields."CF Name"


    Incoming Sync

     

    //def isConcluded = replica.addedComments.find (it.body.contains("has concluded"))

    //if (isConcluded) return

    if (issue.status?.name == "closed") return;

    // KCS 4/28/21 - Commenting out firstSync check, as this was the last change made before the syncs started going wonky last week.

    //if (firstSync && replica.zendesk_ticket_id != "") {

        //debug.error("replica.zendesk_ticket_id = ${replica.zendesk_ticket_id}")

    //    issue.id = Long.valueOf(replica.zendesk_ticket_id)

    //    issue.key = replica.key

    //}


    //issue.key           = replica.key

    //issue.organization  = replica.location_name

    def locationToOrg   = [

        //"Santa Marta International Terminal (smit)" : "361142949531",

      "Santa Marta International Terminal (smit)" : "361142949531",

       // "Advent Intermodal Solutions (emod)" : "361145089832",

       "Advent Intermodal" :"361145089832",

        //"Matson Navigation" : "361082465291",

        "Matson Navigation" : "361234218192",

        "Seattle - Main (sea)" : "361082465291",

        "Bethlehem" : "361142948111",

        "Buffalo" : "361142948111",

        "Chambersburg" : "361142948131",

        "Detroit" : "361142948151",

        "East St Louis" : "361142948171",

        "Evansville" : "361142948211",

        "Fairburn" : "361142948211",

        "Indianapolis" : "361142948231",

        "Louisville (LOU)" : "361142948271",

        "Memphis" : "361142948291",

        "Nashville" : "361142948311",

        "North Bergen" : "361142948351",

        "NWOH" : "361142948371",

        "Philadelphia" : "361142948391",

        "Savannah" : "361142948451",

        "South Kearny" : "361142948471",

        "Springfield" : "361142948491",

        "Syracuse" : "361142948511",

        "Tacoma Terminal (tac)" : "361142948511",

        "Tampa" : "361142948531",

        "Winterhaven" : "361142948591",

        "Worcester (csxw)" : "361142948611",

        "Port San Diego" : "361142948651",

        "Freeport (fpo)" : "361142948671",

        "Guayaquil (gye)" : "361142948691",

        "Gulfport (gpt)" : "361142948711",

        "Port Everglades (pef)" : "361142948751",

        "Westlake Village" : "361142948811",

        "Port of Wilmington (dwil)" : "361142948831",

        "Portroe" : "361142948851",

        "DRS - Philadelphia" : "361142948871",

        "DRS/Tioga - Philadelphia" : "361142948891",

        "Florida International Terminal (fit)" : "361142948911",

        "Freightliner - Birmingham Terminal (bift)" : "361142948951",

        "Freightliner - Bristol Railport" : "361142948971",

        "Freightliner - Cleveland Terminal" : "361142949011",

        "Freightliner - Coatbridge Railport" : "361142949031",

        "Freightliner - Corporate" : "361142949051",

        "Freightliner - Daventry" : "361142949071",

        "Freightliner - Ditton" : "361142949091",

        "Freightliner - Leeds Terminal" : "361142949111",

        "Freightliner - Liverpool Terminal" : "361142949131",

        "Freightliner - Manchester Terminal" : "361142949151",

        "Freightliner - Scunthorpe" : "361142949171",

        "Freightliner - Southampton" : "361142949191",

        "Freightliner - Thamesport Terminal" : "361142949191",

        "Southhampton" : "361142949191",

        "Freightliner - Thamesport" : "361142949211",

        "Freightliner - Tilbury Ramp" : "361142949231",

        "Kingston Wharves Ltd (kwl)" : "361142949251",

        "Koniambo Nickel SAS (kns)" : "361142949271",

        "Little Ferry" : "361142949291",

        "Luka Koper (kop)" : "361142949291",

        //"Conley Terminal (mct)" : "361142949311",

       "Massport-Conley Terminal" : "361142949311",

        "Fergusson Container Terminal" : "361142949331",

        "New Orleans" : "361142949351",

        "New Orleans Terminal (nol)" : "361142949351",

        "POMTOC" : "361142949371",

        "Port Everglades Terminal" : "361145090012",

        "Port Everglades Terminal (pet)" : "361142949391",

        //"Port Lafito Haiti (plh)" : "361142949411",

        "Port Lafito, Haiti" :"361142949411",

        "North Intermodal Rail Yard (nim)" : "361142949451",

        "PSA Halifax (hal)" : "361142949491",

        //"Red Hook Container Terminal (red)" : "361142949511",

         "Red Hook Container Terminal" : "361142949511",

        "Santa Marta International Terminal (smit)" : "361142949531",

        "SCIP - Dillon (ipd)" : "361142949551",

        "SCPA - North Charleston Terminal (nit)" : "361142949571",

        "Wando Welch Terminal (wwt)" : "361142949591",

        "Blount Island Terminal" : "361142949611",

        "Jacksonville Blount Island  (bij)" : "361142949611",

        "SSA Atlantic - Jacksonville Blount Island  (bij)" : "361142949611",

        "SSA Marine - SLC" : "361142949631",

        "Manzanillo (zlo)" : "361142949651",

        "SSA Mexico - Manzanillo (zlo)" : "361142949651",

        "SSA Panama - Colon (mit)" : "361142949671",

        "SSA SV International Chile Limitada" : "361142949691",

        "B63" : "361142949711",

        "C60 " : "361142949731",

        "Matson Tacoma (TAM)" : "361142949751",

        "SSAT - Matson Tacoma (TAM)" : "361142949751",

        //"Oakland International Container Terminal (oict)" : "361142949771",

        //"PCT" : "361142949791",

        "Pacific Maritime Services - PMS/PCT (Long Beach, CA)": "361142949791",

        //"SSAT - Oakland International Container Terminal (oict)" : "361142949771",

        "SSA Terminals - Oakland (OICT) - Berth 59": "361142949771",

        //"SSAT - PCT" : "361142949791",

        "SSAT Pier A (Long Beach, CA)":"361142949791",

        "SSAT - Terminal 5 (T5)" : "361142949811",

        "Terminal 18 (t18)" : "361142949831",

        "Terminal 25/30 (t30)" : "361142949851",

        "Terminal A (pa)" : "361142949871",

        "Tacoma Terminal (tac)" : "361142950251",

        "SSIT - Vietnam" : "361142949891",

        "STE - Alameda (sta)" : "361142949911",

        "Carson (stl)" : "361142949951",

        //"STE - Carson (stl)" : "361142949951",

        "Carson (stl)" : "361142949951",

        "French Camp (stf)" : "361142949971",

        "STE - French Camp (stf)" : "361142949971",

        "STE - Middle Road" : "361142949991",

        "Oakland (sto)" : "361142950011",

        "STE - Oakland (sto)" : "361142950011",

        "STE - Tukwila (sts)" : "361142950031",

        "Tukwila (sts)" : "361142950031",

        "Vale Nouvelle Caledonie" : "361142950031",

        "STE - Wilmington (stw)" : "361142950051",

        "Wilmington (stw)" : "361142950051",

        "STI - San Antonio Terminal Internacional (sai)" : "361142950071",

        "SVTI - San Vicente Terminal Internacional" : "361142950091",

        "Terminal 54/55 (b54)" : "361142950111",

        "Anchorage Office/Terminal (anc)" : "361142950171",

        "San Juan Administrative Office (ssl)" : "361142950211",

        "Trapac LA" : "361142950291",

        "Tuxpan Terminal (tpt)" : "361142950311",

        "YTI - Oakland" : "361142950351",

        "Advent Intermodal Solutions (emod)" : "361145089832",

        "Baltic Container Terminal (bct)" : "361145089852",

        "Barranquilla Container Terminal - (bitco)" : "361145089872",

        "BNSF (ssih) South Seattle Intermodal Facility" : "361145089932",

        "CN Headquarters" : "361145089972",

        "Crowley - San Juan (sjc)" : "361145090032",

        "Portsmouth" : "361145090052",

        "Main Office Jacksonville" : "361145090072",

        "BNSF - Logistics Park Chicago (lpc)" : "361159557252",

        "Freightliner - Doncaster Railport" : "361174665391",

        "Freightliner - Teesport" : "361174665931",

        "Felixstowe" : "361177035652",

        "Freightliner - Hams Hall" : "361177035772",

        "Bessemer Terminal" : "361177578311",

        "Bedford Park" : "361177583691",

        "Cincinnati" : "361177584291",

        "Cleveland" : "361177585351",

        "Columbus (colm)" : "361177586711",

        "Jacksonville" : "361177593511",

        "Charlotte" : "361179834732",

        "Charleston" : "361179835432",

        "Chicago 59th Street" : "361179837652",

        "BNSF (sig) Seattle International Gateway" : "361145089912",

        "BNSF Railway Main Office (BN01)" : "361179865332",

        "Groupe Noumea Port (gnp)" : "361180142931",

        "Gearbulk - Pasir Gudang (atm) ZD only" : "361182010311",

        "Gearbulk - Port Manatee (pmtf)" : "361182016391",

        "Batumi International Container Terminal (bict)" : "361182037551",

        "Gearbulk - Lake Charles (lcct)" : "361184195972",

                  "Talleyrand (jac)" : "361145089992",

                  "Anchorage Office/Terminal (anc)" : "361142950171",

                  "Blount Island Terminal" : "36114295019",

                  "Puerto Nuevo Terminal (sjut)" : "361142950231",

                  "Tacoma Terminal (tac)" : "361142950251",



    ]

    def orgID = locationToOrg[replica.location_name]

    //throw new Exception("orgID = ${orgID.class} ${orgID}")

    issue.organization_id = orgID


    //issue.customFields."Tideworks Applications".value = replica.tideworks_application

    issue.customFields."Tideworks Applications".value = replica.product_name



    def productMap = [

                  "Tideworks Intermodal Pro" : "intermodal_pro",

                  "Mainsail" : "mainsail",

                  "Tideworks Mainsail":"Mainsail",

                  "Spinnaker" : "spinnaker",

                  "Traffic Control" : "traffic_control",

                  "EDI Porter" : "edi_porter",

                  "Forecast" : "forecast",

                  "Terminal View" : "terminal_view",

                  "GateVision" : "gatevision",

                  "Insight" : "insight",

                  "Mainsail Billing" : "mainsail_billing",

                  "Scale Weight Reader" : "scale_weight_reader",

                  "System Integration Module (SIM)" : "sim",

                  "Buoy" : "buoy",

                  "Zendesk" : "zendesk",

                  "Tideworks Beacon" : "Beacon",

                  "Beacon" : "Beacon",

                  "Logbook" : "Logbook",

                  ]

                 


    def tideworks_application = productMap[replica."product_name"]

    //def productName = productMap[replica.product_name] ?: replica.product_name

    issue.customFields."Tideworks Applications".value  = tideworks_application //productName // commented out because this line is only blanking out the application field

    //debug.error("tideworks_application = ${replica."product_name"}, result = ${tideworks_application}")

    //debug.error("Priority = ${replica.priority.name}")

    if (replica.priority?.name == "Critical") {

        issue.customFields."Critical"?.value = true

    }


    def priorityMapping = [

        // remote side priority <-> local side priority

        "Critical" : "urgent",

        "Important" : "high",

        "Normal" : "normal",

        "Informational" : "low"

    ]

    def priorityName = priorityMapping[replica.priority?.name] ?: "normal" // set default priority in case the proper urgency could not be found

    issue.priority = nodeHelper.getPriority(priorityName) //priorityName)



    //issue.status        = replica.state

    //issue.assignee      = replica.assignee

    //throw new Exception("reporter:  ${nodeHelper.getUser(replica.caller)}")

    if (replica.caller)

        issue.reporter  = nodeHelper.getUserByEmail(replica.caller.toLowerCase())

    issue.summary       = replica.summary

    //issue.customerCase.short_description    =replica.subject

    issue.description   = replica.description ?: "No description"

    //issue.labels        = replica.labels

    issue.attachments   = attachmentHelper.mergeAttachments(issue, replica)


    /*issue.comments      += replica.addedComments.collect{ comment ->

       comment.executor = nodeHelper.getUserByEmail(comment.author?.email?.toLowerCase())

       def body = comment.body

      comment.body = body.replaceAll("\\[code\\]", "").replaceAll("\\[/code\\]", "")

      comment

    }*/


    issue.comments += replica.addedComments.collect{ comment ->

      comment.executor = nodeHelper.getUserByEmail(comment.author?.email?.toLowerCase() ?: comment.author?.username?.toLowerCase())

      comment.body = comment.body.replaceAll("\\[code\\]", "").replaceAll("\\[/code\\]", "")


      if(comment.executor == null){

         comment.body = "Comment from servicenow by "+comment.author?.username + "\n:" + comment.body

      }

      //debug.error("Html description = ${comment.body}")

      comment

    }

    //issue.comments      += replica.closenotes

    //issue.customFields."Tideworks Applications".value = replica.product_name


    //def includeComments = commentHelper.mergeComments(issue, replica).collect{it}

    //issue.comments     += nodeHelper.toMarkDownComments(includeComments)


    issue.customFields."Resolution Type".value = replica.close_code

    //issue.customFields."Triage Item"?.value = replica.u_jira_issue

    issue.customFields."SN Incident"?.value = replica.incident

    issue.customFields."Affected Version".value = replica.product_version

    issue.customFields."SN Correlation Number".value = replica.number

    issue.customFields."Customer Reference".value = replica.number

    //issue.customFields.Resolution_Code = "Select One"

    //issue.customFields."CF Name".value = replica.customFields."CF Name".value


    //Receive a Custom Field value

    //issue.customFields."CF Name".value = replica.customFields."CF Name".value


    if ((issue.status != "solved") && (issue.status != "closed")) {

        def statusMap = [

            "New" : "new",

            "Open" : "open",

            "Awaiting Info" : "open",

            "Solution Proposed" : "solved",

            "Closed" : "closed"

        ]

        //issue.status = "Awaiting Info" //(statusMapping[replica.state] ?: "New")

        def remoteStatusName = replica.state

        if ((remoteStatusName == "Solution Proposed") || (remoteStatusName == "Closed")) {

            def addComment = "IT has concluded work on " + replica.number + ".\n"

            addComment += "Resolution details: \"" + replica.close_code + "\" " + replica.close_notes

             issue.setStatus(statusMap[remoteStatusName] ?: remoteStatusName)

          issue.type = nodeHelper.getIssueType("incident")

        issue.Environment = "Production"


           // issue.comments += addComment

        }

        else {

            issue.setStatus(statusMap[remoteStatusName] ?: remoteStatusName)

        }

      CommentAdd your comment...