Important Note: ServiceForms is now ServiceTrade InspectionManager. InspectionManager retains all of the inspection automation benefits of its predecessor but with some important additions. To read more about this exciting change, read more here.
Creating Dispatch Rules
Warning: Before making any changes to your Dispatch Mapping code, it's important to understand that deleting, removing, or adding any of the code in the Dispatch Mapping section can break your dispatch rules. Before you begin, please copy your existing code into a secure and saved document so you can reference it for future use. ServiceTrade cannot guarantee a backup of your original dispatch mapping code.
Follow these instructions to create a dispatch rule that determines when this form will be dispatched to technicians. You must create a dispatch rule for every form.
- On the form details page, click Add Dispatch Rule.
- On the dispatch rule add page, select the circumstances under which this form will be dispatched.
- If you leave all settings at their defaults, this form will be dispatched for every job.
- If you choose some settings, this form will be dispatched when all of the settings that you selected match the job.
- For Example: If you choose a job type of ‘Inspection’ and a service line of ‘Sprinkler’, this form will be dispatched only for jobs of type ‘Inspection’ that contain at least one active service with the ‘Sprinkler’ service line.
- Choose whether to include only assets associated with services on the appointment (typically this will be a small, targeted set of assets) or all the assets associated with the appointment’s location (this will be a larger set of assets).
- If needed, enter dispatch mapping rules.
- Click Save Dispatch Rule to save the rule.
Dispatch Data Mappings
A dispatch rule can define how data is prefilled into the dispatched form’s fields. By default, the following data will be prefilled.
Below is a list of ServiceTrade API field names, and their corresponding InspectionManager field names
ServiceTrade API field name |
InspectionManager field name |
GET /api/job/:jobId |
|
job.id |
servicetrade_job_id |
job.number |
servicetrade_job_number |
job.description |
servicetrade_job_description |
job.location.name |
servicetrade_location_name |
job.location.address.street |
servicetrade_location_street |
job.location.address.city |
servicetrade_location_city |
job.location.address.state |
servicetrade_location_state |
job.location.address.postalCode |
servicetrade_location_postal_code |
job.customer.name |
servicetrade_customer_name |
job.customer.address.street |
servicetrade_customer_street |
job.customer.address.city |
servicetrade_customer_city |
job.customer.address.state |
servicetrade_customer_state |
job.customer.address.postalCode |
servicetrade_customer_postal_code |
job.location.primaryContact.name |
servicetrade_contact_name |
job.location.primaryContact.phone |
servicetrade_contact_phone |
job.location.primaryContact.mobile |
servicetrade_contact_mobile |
job.location.primaryContact.alternatePhone |
servicetrade_contact_alt_phone |
GET /api/appointment/:appointmentId |
|
appointment.windowStart |
servicetrade_appointment_start |
appointment.windowEnd |
servicetrade_appointment_end |
GET /api/servicerequest?status=open,in_progress&appointmentId=:appointmentId |
|
servicerequest[].id |
servicetrade_services[].service_request_id |
servicerequest[].description |
servicetrade_services[].description |
servicerequest[].serviceLine.name |
servicetrade_services[].service_line |
servicerequest[].asset.name |
servicetrade_services[].asset_name |
servicerequest[].asset.id |
servicetrade_services[].asset_id |
servicerequest[].deficiency.id |
servicetrade_services[].deficiency_id |
servicerequest[].windowStart |
servicetrade_services[].start |
servicerequest[].windowEnd |
servicetrade_services[].end |
servicerequest[].serviceRecurrence.frequency |
servicetrade_services[].frequency |
servicerequest[].serviceRecurrence.interval |
servicetrade_services[].interval |
servicerequest[].deficiency.id |
servicetrade_services[].deficiency_id |
servicerequest[].deficiency.description |
servicetrade_services[].deficiency_description |
servicerequest[].status |
servicetrade_services[].status |
GET /api/jobItem?jobId=:jobId |
|
jobItem[].id |
servicetrade_job_items[].id |
jobItem[].quantity |
servicetrade_job_items[].quantity |
jobItem[].name |
servicetrade_job_items[].name |
jobItem[].libItem.code |
servicetrade_job_items[].code |
jobItem[].cost |
servicetrade_job_items[].cost |
jobItem[].serviceRequest.id |
servicetrade_job_items[].service_id |
jobItem[].serviceRequest.description |
servicetrade_job_items[].service_description |
GET /api/asset?appointmentId=:appointmentIdto get only this appointment’s assets GET /api/asset?locationId=:locationIdto get ALL assets at this location |
|
asset[].id |
servicetrade_assets[].asset_id |
asset[].name |
servicetrade_assets[].name |
asset[].serviceLine.name |
servicetrade_assets[].service_line |
asset[].properties.* Properties vary by asset definition |
servicetrade_assets[].properties_* Properties vary by asset definition Example: properties_barcode |
GET /api/deficiency?locationId=:locationId&status=new,verified |
|
deficiency[].id |
servicetrade_deficiencies[].deficiency_id |
deficiency[].asset.id |
servicetrade_deficiencies[].asset_id |
deficiency[].severity |
servicetrade_deficiencies[].severity |
deficiency[].status |
servicetrade_deficiencies[].status |
deficiency[].description |
servicetrade_deficiencies[].description |
deficiency[].resolution |
servicetrade_deficiencies[].resolution |
deficiency[].serviceLine.name |
servicetrade_deficiencies[].service_line |
deficiency[].job.id |
servicetrade_deficiencies[].job_id |
deficiency[].reportedOn |
servicetrade_deficiencies[].reported_date |
GET /api/user/:userId userId is the ID of the clocked-in technician who receives the dispatch |
|
user.id |
servicetrade_technician_id |
user.name |
servicetrade_technician_name |
user.phone |
servicetrade_technician_phone |
user.email |
servicetrade_technician_email |
user.details |
servicetrade_technician_details |
GET /api/brand/:brandId brandId is the ID of the brand associated with the job’s office |
|
brand.displayName (or job vendor name if not set) |
servicetrade_brand_display_name |
brand.webSite |
servicetrade_brand_website |
brand.details |
servicetrade_brand_details |
Custom dispatch rules, written as JavaScript code, can be used to add or modify these default dispatch mappings. The following properties are available:
Property | Description |
SF.payload |
The payload of data that will be sent to the technician's device during the form dispatch. |
SF.job |
The job object for the form submission; will be null if the form is submitted outside the context of a job. SF.job.id is the job ID, SF.job.location.id is the job’s location ID, etc. |
SF.user |
The user object for the user to whom the form was submitted. |
The following external modules are available:
- Moment - moment-timezone
- Get - This is equivalent to Lodash get()
To make changes to the data that is dispatched as part of a form, modify the contents of SF.payload accordingly.
To stop a form from being dispatched, set SF.payload.prevent_dispatch to true. This is useful for negative matches (e.g., dispatch a form for all job types except one).
Dispatch Mapping Rule Examples
Rename 'servicetrade_assets' to 'asset_list':
SF.payload.asset_list = SF.payload.servicetrade_assets;
Add the location’s state to each asset in the ‘servicetrade_assets’ array:
SF.payload.servicetrade_assets.forEach(function(a) {
a.location_state = SF.payload.servicetrade_location_state;
});
Convert an asset property Unix timestamp to Device Magic friendly date format:
SF.payload.servicetrade_assets.forEach(function(a) {
a.properties_manufacture_date = Moment(a.properties_manufacture_date, ‘X’)
.format(‘YYYY-MM-DD’);
});
Don’t dispatch this form for emergency service calls:
if (SF.job.type === 'emergency_service_call') {
SF.payload.prevent_dispatch = true;
}
Remove the job description:
SF.payload.servicetrade_job_description = undefined;
Get a value that might or might not be defined, using ‘Get’:
SF.payload.servicetrade_contact_email = Get(SF,'job.location.primaryContact.email');
// the old way was:
SF.payload.servicetrade_contact_email = SF.job
&& SF.job.location
&& SF.job.location.primaryContact
? SF.job.location.primaryContact.email
: null;
Organize all child assets under their parents:
// separate parents from children
const parentAssets = SF.payload.servicetrade_assets.filter(a =>
!a.parent || a.parent.name === 'Location - Building'
);
const childAssets = SF.payload.servicetrade_assets.filter(a =>
a.parent && a.parent.name !== 'Location - Building'
);
// add child array container for each parent
parentAssets.forEach(p => p.children = []);
// assign children to their parents
childAssets.forEach(c => {
const foundParent = parentAssets.find(p => p.id === c.parentId);
if (foundParent) {
foundParent.children.push(c);
}
});
// servicetrade_assets is now only the parents (with their nested children underneath)
SF.payload.servicetrade_assets = parentAssets;
Destination Rules
Creating Destination Rules
Follow these instructions to create destination rules that determine what actions the middleware should take when it receives a filled-out form from a technician. Repeat these steps for every middleware rule that needs to be added. Typically you will only need one, even if you want that middleware rule to take multiple actions (such as creating assets AND creating deficiencies).
- On the form details page, click Add Destination Rule.
- In the Destination Type dropdown, choose the destination type Update ServiceTrade.
- In the Destination Name input box, enter a name that describes the actions that will be taken by this rule (for instance, ‘Create assets and deficiencies’.
- In the Destination Rule box, enter the destination rule code.
- Click Save Destination to save the destination rule.
Each destination rule is defined as a block of Javascript code. The following properties are available:
Property | Description |
SF.answers |
The answers object from the technician's form submission |
SF.job |
The job object for the form submission; will be null if the form is submitted outside the context of a job. SF.job.id is the job ID, SF.job.location.id is the job’s location ID, etc. |
The following external modules are available:
- Moment - moment-timezone
- Get - This is equivalent to Lodash get()
Each destination rule is executed inside an asynchronous function, so await should be used for asynchronous calls (such as the SF create and update methods).
Destination Rule Methods
These methods can be called inside a destination rule to create/update data inside ServiceTrade. All methods are asynchronous.
SF.createDeficiencies(deficienciesArray)
Creates a set of deficiencies from a given array. Each array member should have the following properties:
- assetId (int) - ID of asset to which deficiency should be added *Required
- jobId (int) - ID of job on which deficiency is being reported *Required
- severity (enum, one of: ‘inoperable’, ‘deficient’, ‘suggested’) *Required
- description (string) *Required
- status (enum, one of: ‘new’, ‘verified’, ‘fixed’) *Required
- reportSource (enum, one of: ‘manual’, ‘mobile’) *Required
- images (array)
SF.createAssets(assetsArray)
Creates a set of assets from a given array. Each array member should have the following properties:
- locationId (int) - ID of location to which asset should be added *Required
- type (enum, one of the types listed in GET /api/assetDefinition) *Required
- parentId (int) - ID of the asset which is the parent of the asset being added (optional; if not supplied, the top-level building asset will be used as the parent)
- properties (object -- contents vary by asset definition) *Required
SF.updateAssets(assetsArray)
Updates a set of assets from a given array. Each array member should have the following properties:
- assetId (int) - ID of the asset to be updated *Required
- properties (object -- contents vary by asset definition)
- status (enum, one of: ‘active’, ‘inactive’)
SF.createJobItems(jobItemsArray)
Creates a set of job items from a given array. Each array member should have the following properties:
- jobId (int) - ID of the job to which the job item should be added *Required
- libItemId (int) - ID of lib item to use when creating the job item. *Required, unless ‘name’ and ‘serviceLineId’ are provided
- name (string) [REQUIRED, unless ‘libItemId’ is provided]
- serviceLineId (int) - ID of service line to use when creating the job item. *Required, unless ‘libItemId’ is provided AND referenced lib item has a service line assignment.
- quantity (float) *Required
- serviceRequestId (int) - ID of the service request to which the job item should be associated
- source (object - see job item API documentation for details)
SF.createJobAttachments(attachmentsArray)
Creates a set of job attachments from the given array. Each array member should have the following properties:
- jobId (int) - ID of the job to which the attachment should be added *Required
- attachmentUrl - the URL of the attachment *Required
Returns an array of attachment objects.
Example:
/* creates a job attachment if the ‘Interesting Job Image’ question is answered */
let jobAttachmentsToCreate = [];
if (SF.answers.Interesting_Job_Image && SF.answers.Interesting_Job_Image.value) {
jobAttachmentsToCreate.push({
attachmentUrl: SF.answers.Interesting_Job_Image.value,
jobId: SF.job.id
});
}
await SF.createJobAttachments(jobAttachmentsToCreate);
SF.createAssetAttachments(attachmentsArray)
Creates a set of asset attachments from the given array. Each array member should have the following properties:
- assetId (int) - ID of the asset to which the attachment should be added *Required
- attachmentUrl - the URL of the attachment *Required
Example:
/* creates attachments on all assets that have the ‘Interesting Image’ question answered */
let assets = SF.answers.servicetrade_assets ? SF.answers.servicetrade_assets.values : [];
let assetAttachmentsToCreate = [];
for (let a of assets) {
const assetId = d.asset_id ? d.asset_id.value : null;
if (assetId && a.Interesting_Image && a.Interesting_Image.value) {
assetAttachmentsToCreate.push({
attachmentUrl: a.Interesting_Image.value,
assetId: assetId
});
}
}
await SF.createAssetAttachments(assetAttachmentsToCreate);
Dispatch Mapping Rule Examples
Create deficiencies for each asset that has a Visual or Functional test failure:
let assets = SF.answers.servicetrade_assets ? SF.answers.servicetrade_assets.values : [];
for (asset of assets) {
const assetId = asset.asset_id ? asset.asset_id.value : null;
if (!assetId) {
const assetPayload = {
locationId: SF.job.location.id,
type: 'hvac_unit',
properties: {
barcode: asset.properties_barcode ? asset.properties_barcode.value : null,
area_serviced: asset.properties_area_serviced ? asset.properties_area_serviced.value : null
}
};
const newAsset = await SF.createAssets([assetPayload]);
asset.asset_id = {value: newAsset[0].id};
}
}
const deficientAssets = assets.filter(v => {
return (v.Visual && v.Visual.value == 'Fail')
|| (v.Functional && v.Functional.value == 'Fail');
});
let deficienciesToCreate = [];
for (let d of deficientAssets) {
let description, deficiency, severity;
const assetId = d.asset_id ? d.asset_id.value : null;
const failFunctional = d.Functional && d.Functional.value == 'Fail';
const failVisual = d.Visual && d.Visual.value == 'Fail';
if (failVisual && !failFunctional) {
description = 'Visual test failure';
severity = 'deficient';
} else if (!failVisual && failFunctional) {
description = 'Functional test failure';
severity = 'inoperable';
} else {
description = 'Visual and functional test failure';
severity = 'inoperable';
}
let deficiencyPayload = {
assetId: assetId,
severity: severity,
description: description,
status: 'verified',
reportSource: 'mobile',
jobId: SF.job.id,
attachments: []
};
if (d.Visual_Failure_Image && d.Visual_Failure_Image.value) {
deficiencyPayload.attachments.push(d.Visual_Failure_Image.value);
}
if (d.Functional_Failure_Image && d.Functional_Failure_Image.value) {
deficiencyPayload.attachments.push(d.Functional_Failure_Image.value);
}
deficienciesToCreate.push(deficiencyPayload);
}
await SF.createDeficiencies(deficienciesToCreate);
Comments
0 comments
Article is closed for comments.