1 answer
- 10-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)
}
Add your comment...
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
Hi Champ,
Could you please be specific on what incoming, outgoing you are using on both sides concerning these custom fields?
Kind regards,
Ariel
I don't understand what you asking.
Hey Champ Yarbrough
Long time no see.
Ariel Aguilar is asking about the scripts on each side.