Developer's Guide > Developer's Guide > Codebeamer Development > Execute a custom script > An example scripted Workflow transition: How to configure Codebeamer to synchronize Leads tracker with others using a custom Workflow action
An example scripted Workflow transition: How to configure Codebeamer to synchronize Leads tracker with others using a custom Workflow action
The requirement: Synchronizing various trackers on a Workflow transition
The Sales Template project contains the following trackers:
Leads - An item representing a prospective customer that is created when an individual or business shows interest and provides his or her contact information.
Accounts - The Account, along with contacts as related records, is like an address book. It is an entity to store a company’s name, address, phone number and other important pieces of information.
Opportunities - An Opportunity represents a potential sale to a new or established customer. Helps us forecast future business demands and sales revenues.
Concats - An individual’s personal information by which we can reach the individual to discuss their needs & what we can offer.
Activities - An Activity is basically a record of actions undertaken by our sales team and other stakeholders.
The goal is that when a Lead finalizes, i.e. it changes to the "SQL" workflow state then we should create appropriate items in the Accounts, Opportunities, Contants trackers.
This diagram illustrates how these trackers relate to each other.
Synchronization mapping between trackers
Source Tracker fields
Mapping to...
Lead
Contact
Account
Opportunity
Description
Company Profile
First name
First name
Last name
Last name
Title
Title
Department
Department
Company
Account name
Phone
Phone
Phone
E-mail contact
E-mail
Street
Street
Street
Post code
Zip/Postal code
Post code
City
City
City
Country
Country
Country
Employees
Employees
Website
Company website
Lead source
Lead source, Opportunity, Description
Comment
Comment
Geolocation
Geolocation
Industry
Industry
Account should refer the new "Account"
Account should refer the new "Account".
Contacts should refer to the new "Contact".
Here is the required mapping of fields. The Workflow should copy these fields from the "Leads" tracker to the fields of the other tracker as described here.
The script code
Here is the Groovy script, which contains the logic for the Workflow-action. For that you should create a new file as $CB_HOME/CB/tomcat/webapps/cb/WEB-INF/classes/synchronizeLeads.groovy. Paste this Groovy script below to that file, and save it.




// Groovy script implements a Workflow state-transition action as requested here: https://codebeamer.com/cb/issue/326159

// registered in my-applicationContext.xml on cb.com only

import com.intland.codebeamer.persistence.dto.*;

import com.intland.codebeamer.persistence.dto.base.*;

import com.intland.codebeamer.persistence.dao.*;

import com.intland.codebeamer.manager.*;

import com.intland.codebeamer.controller.importexport.*;

import org.apache.commons.lang3.*;



if (!beforeEvent) {

return; // do NOTHING on after-event, everything is already handled in the before-event!

}



logger.info("-------------------------------------");

logger.info("Synchronizing Lead issue:" + subject);



trackerDao = applicationContext.getBean(TrackerDao.class);

trackerItemManager = applicationContext.getBean(TrackerItemManager.class);



projectId = subject.tracker.project.id



// read custom fields in "Leads" tracker

// using a helper class to access fields by name

fieldAccessor = new com.intland.codebeamer.text.excel.FieldAccessor(applicationContext);

fieldAccessor.setUser(user);

def getByLabel = { fieldName -> fieldAccessor.getByLabel(subject, fieldName) };



// set a field on contact by finding the field using its label

def setField(issue, fieldName, value) {

field = fieldAccessor.getFieldByName(issue, fieldName);

if (field != null) {

field.setValue(issue, value);

} else {

logger.warn("Can not find field <" + fieldName +"> on " + issue);

}

};



def copyField(toIssue, fieldName, toFieldName=null, defaultValue=null) {

value = fieldAccessor.getByLabel(subject, fieldName);

if (value == null && defaultValue != null) {

value = defaultValue;

}



if (toFieldName == null) {

toFieldName = fieldName;

}

setField(toIssue, toFieldName, value);

}



def updateOriginal(fieldName, value) {

setField(subject, fieldName, value);

}



def getOrCreateChoice(issue, fieldName, value) {

if (StringUtils.isBlank(value)) {

return null;

}



choicesProvider = new ChoicesProvider(applicationContext);

choiceField = choicesProvider.getFieldByName(user, issue, fieldName);

if (choiceField == null) {

return null;

}

asChoice = choicesProvider.getOrCreateChoiceByName(user, issue.tracker, choiceField, value, null);

return asChoice;

}



// first check/create account if does not exist, because this is required by contact

account = getByLabel("Account");

if (account == null) {

try {

account = new TrackerItemDto();

accounts = trackerDao.findByNameAndProjectId("Accounts", projectId);

account.tracker = accounts;



// use the trackerItemManager to copy of the source issue, because this copies comments and attachments too

request = event.getRequest();

fieldMapping = new HashMap();

Map<TrackerItemDto,TrackerItemDto> copied = trackerItemManager.copy(request, user, Collections.singletonList(subject), null, account, null, fieldMapping);

account = copied.get(subject);

logger.info("copied account's id:" + account.id);



// required fields

copyField(account, "Company", "Account Name");

copyField(account, "Description", "Company Profile"); // must fill with something, this is a required field



copyField(account, "Street");

copyField(account, "Post code");

copyField(account, "City");

copyField(account, "Country");

copyField(account, "Employees");

copyField(account, "Website", "Company website");

copyField(account, "Comment");

copyField(account, "Phone");



// industry is a choice field in the target, creating a new choice if necessary

industry = getByLabel("Industry");

logger.warn("Creating industry:" + industry)

industryAsChoice = getOrCreateChoice(account, "Industry", industry);

logger.warn("industryAsChoice:" + industryAsChoice);

if (industryAsChoice != null) {

setField(account, "Industry", Arrays.asList(industryAsChoice));

}



logger.info("Created Account:" + account);

// trackerItemManager.create(user, account, event.getData());

trackerItemManager.update(user, account, event.getData());



updateOriginal("Account", Arrays.asList(account));

} catch (Throwable th) {

logger.warn("Failed to create Account for " + subject, th);

}

}



// only create a new contact if that does not exist yet!, this also avoids infinite event loops !

contact = getByLabel("contact");

if (contact == null) {

try {

// create a new Contact

contact = new TrackerItemDto();

contacts = trackerDao.findByNameAndProjectId("Contacts", projectId);

contact.tracker = contacts;



copyField(contact, "First Name");

copyField(contact, "Last Name");

copyField(contact, "Title"); // TODO: there is NO such field here

copyField(contact, "Department"); // TODO: there is NO such field here

copyField(contact,"Phone");

copyField(contact,"E-mail", "Email");

copyField(contact,"Street");

copyField(contact,"Post code", "Zip/Postal code");

copyField(contact,"City");

copyField(contact,"Country");

copyField(contact, "Geolocation"); // TODO: no such field!



// fill the Mandatory Account field

setField(contact, "Account", Arrays.asList(account));



trackerItemManager.create(user, contact, event.getData());

logger.info("Created Contact:" + contact);



updateOriginal("Contact", Arrays.asList(contact));

} catch (Throwable th) {

logger.warn("Failed to create Contact for " + subject, th);

}

}



try {

// Account - Main Contact field should have a default value for Contact person created upon conversion

mainContact = fieldAccessor.getByLabel(account, "Main Contact");

if (mainContact == null || mainContact.isEmpty()) {

setField(account, "Main Contact", Arrays.asList(contact));

trackerItemManager.update(user, account, event.getData());

}

} catch (Throwable th) {

logger.warn("Failed to set Main Contanct field", th);

}



opportunity = getByLabel("opportunity");

if (opportunity == null) {

try {

opportunities = trackerDao.findByNameAndProjectId("Opportunities", projectId);



// create a new opportunity

opportunity = new TrackerItemDto();

opportunity.tracker = opportunities;



defaultVal = getByLabel("Account"); // use this as default value if the "opportunity" field would be empty, because this is a required field

opportunity.name = account.name; // REQUIRED field

opportunity.description = "--"; // REQUIRED field

// copyField(opportunity, "Lead Source", "Opportunity", defaultVal); // REQUIRED field

// copyField(opportunity, "Lead Source", "Description", defaultVal); // REQUIRED field

copyField(opportunity, "Lead Source");

setField(opportunity, "Account", Arrays.asList(account));



// store the "Account" to the "Contacts" table

opportunityTables = new com.intland.codebeamer.manager.trackeritems.TableFields(user, opportunity, applicationContext);

contactsTable = opportunityTables.getTableByName("Contacts");

contactColumn = contactsTable.getTableColumnByName("Contact");

contactColumn.setReferenceValues(0, Arrays.asList(contact));



trackerItemManager.create(user, opportunity, event.getData());

logger.info("Created Opportunity:" + opportunity);



updateOriginal("opportunity", Arrays.asList(opportunity));

} catch (Throwable th) {

logger.warn("Failed to create Opportunity for " + subject, th);

}

}



logger.info("-------------------------------------");
Was this helpful?