Skip to main content
Table of Contents

Before Create Order scripts Cookbook

This Before Create Order(BCO) script cookbook provides practical, ready-to-use code examples and techniques for ShipStream users working with BCO scripts. You'll find working code snippets, debugging…

Kevin
Updated by Kevin

This Before Create Order(BCO) script cookbook provides practical, ready-to-use code examples and techniques for ShipStream users working with BCO scripts. You'll find working code snippets, debugging strategies, common filtering patterns, and complete script examples that solve real-world business logic challenges. Whether you need to add items to orders, implement geographic restrictions, manipulate shipping dates, or debug script behavior, this cookbook offers copy-paste solutions and best practices to get your BCO scripts working quickly and reliably.

Debugging

Adding print statements while debugging helps you see what variables contain and which sections of logic were executed.

print('Inside Loop. Variable contains: ' + theVariable + '\n')

Printing out portions of an array object using JSON.stringify()

print("The order's SKUs:\n" + JSON.stringify(order.items, ['qty', 'sku', 'product'], 4) + '\n')
print('The script was executed on this order.')

print('The SKU ' + theSku + ' was added to the order.\n')

print('The script was executed but no action was taken.')

print statements are very helpful for adding notes to the Order's History, allowing users to audit actions taken on an order. This helps users quickly see that a script added items, removed items, or modified order data.

Common filters/checks needed to find key data

Filter for country

API and UI orders are submitted slightly differently. To handle this difference, use the following pattern when checking the country.

const isUSA = Boolean(order.address.country ? order.address.country == 'US' : order.address.country_id == 'US')

Filter for Region

API and UI orders are submitted slightly differently. To handle this difference, use the following pattern when checking the region.

const isPuertoRico = Boolean(order.address.region && order.address.region.match(/^(PR|Puerto Rico)$/))

Filter order.items for specific SKUs
const skus = /^(19-177-1719|34-737-8757)$/
const foundSkus = order.items.filter(item => item.product && item.product.sku.match(skus))

if (foundSkus.length > 0) {
//statments...
}

Checking for empty values before modifying

The properties tbp_group_id, hold_indefinitely, and delayed_ship_date should be checked in the following fashion. Because of the various ways that these can be set and used these properties have the potential to contain different empty values.

if (order.options.hold_indefinitely === '' 
|| typeof order.options.hold_indefinitely === 'undefined'
|| order.options.hold_indefinitely === null)
{
//statments...
}

Calculations

Calculate the total weight of all order items
const totalWeight = order.items.reduce((total, item) => {
const productWeight = parseFloat((item.product||{weight:0}).weight)
return total + parseInt(item.qty) * productWeight
}, 0)

Calculate the total quantity of all order items
const totalItemQty = order.items.reduce((total, item) => {
return total + parseInt(item.qty)
}, 0)

Lock order to a specific warehouse See Order Allocation for more information.
order.options.allocation_options = {"algorithms":["specific-locked"],"allowed_stock_ids":[2]}

Examples of full BCO scripts

Hold Orders

Automatically Hold Orders Shipping to Alaska or Hawaii

This script places any order with a shipping address in Alaska or Hawaii on hold to ensure special handling or review.

//  Merchant: Acme Inc.
// Place all order with destination Alaska and Hawaii on hold.
//

const isUSA = Boolean(order.address.country ? order.address.country == 'US' : order.address.country_id == 'US')

// Check if destination is USA...
if (isUSA) {

const isAlaska = Boolean(order.address.region && order.address.region.match(/^(AK|Alaska)$/))
const isHawaii = Boolean(order.address.region && order.address.region.match(/^(HI|Hawaii)$/))

// Check if region is Alaska or Hawaii...
if (isAlaska || isHawaii) {

// Print statement...
print('Destination address is to ')
if (isAlaska) {print('Alaska.\n')}
if (isHawaii) {print('Hawaii.\n')}
print('Placing order on hold.')

// Place order on hold.
order.options.hold_indefinitely = true
}
}

Hold Orders Containing one or more specified SKUs for Merchant Review

This script automatically places any order containing the SKU "protein-orange" on hold until it can be reviewed by the merchant.

// Put Orders for SKU protein-orange on hold till reviewed by Merchant.
//
// For each ordered item do...
order.items.forEach(function(item, index) {

// Look at the Item's SKU and if it matches...
if (item.sku.match(/^protein-orange$/)) {

// Print out info to the Order's History and place the order on hold.
print('Order contains SKU: "protein-orange".\nOrder on hold till review.')
order.options.hold_indefinitely = true
}
})

Send Email Notification

Hold Orders Using "Client Provided Label" Shipping Method

This script automatically places orders on hold when the "Client Provided Label" shipping method (code "vendor") is selected, clears the requested ship date, and sends notification emails to support and the merchant.

// Auto hold all orders with custom external shipping method "Client Provided Label" method code "vendor"
if (order.options.shipping_method == 'external_vendor') {
// Hold order
order.options.hold_indefinitely = true

// override requested ship date
order.options.requested_ship_date = null

// Send email to support and merchant
order.options.send_email = 'me@example.com, you@example.com'
}

Automatically Assign TPB Group for UPS Orders with Specific SKUs

This script automatically assigns a Third Party Billing (TPB) group ID to orders that contain certain SKUs and use a UPS shipping method; if the shipping method is not UPS, it places the order on hold and sends a notification email.

//  Merchant: Acme Inc.
// Description: Set the Third Party Billing(TPB) Group ID when the order uses a UPS shipping methods and
// if one of the search SKUs is found on the order.

const tpbGroup = 212
const searchSkus = /^(AM0011|AM0012)$/
const foundSkus = order.items.filter(item => item.product && item.product.sku.match(searchSkus))
const isUpsMethod = Boolean(order.options.shipping_method && order.options.shipping_method.match(/^ups_.+$/))

// Check that a search SKU is found
if (foundSkus.length > 0) {

// Check that the Shipping Method is UPS...
if (isUpsMethod) {

// Check to see if the TPB group has already been set. If not set then continue.
if (typeof order.options.tpb_group_id === 'undefined' || order.options.tpb_group_id === null) {

// Print info to the Order's History about what was done...
print('Shipping Method matches an approved TPB Carrier Method\n')
print('Setting Third Party Billing to Group: '+tpbGroup+'\n')

// Make the changes to the order.
order.options.tpb_group_id = tpbGroup
}
}

// If not UPS shipping method, place order on hold and email X
if ( ! isUpsMethod) {
print('Order contains the search SKUs but is not a UPS Shipping Method.')
order.options.hold_indefinitely = true
order.options.send_email = "example@example.com"
}
}

Add a SKU to the order

Simply Adding a SKU to the Order

When pushing new items onto an order, be aware that the System from one script to the next does not populate the product object for any added items. There are times when you may need to add data on the new item that a later script is relying on. Defensive coding is always the best practice.

It is particularly helpful to add the product.sku field, as this is often referenced in other scripts.

const thankYouFlyer = 'thank-you-flyer-v2'
order.items.push({ sku: thankYouFlyer, qty: 1, product: {sku: thankYouFlyer}})

Automatically add a special SKU for each matching SKU item ordered

This script checks for BT-Speaker items in an order and automatically adds a matching quantity of "Speaker-Hardware" items for each BT-Speaker ordered.

//  Merchant: Acme Inc.
// If a BT-Speaker is ordered then add one Speaker-Hardware for each BT-Speaker item ordered.

// SKUs to match.
const searchSkus = /^(BT-Speaker-Black|BT-Speaker-Blue)$/

// Filtering order items to find if there are any matching search SKUs.
const foundSkus = order.items.filter(item => item.product && item.product.sku.match(searchSkus))

// Check if any search SKUs were found...
if (foundSkus.length > 0) {

// Find the total quantity of BT-Speaker items ordered.
const totalBtsQty = foundSkus.reduce((total, item) => { return total + parseInt(item.qty) }, 0)

// Check if any BT-Speaker items were counted (0 == false)...
if (totalBtsQty) {
// Add Speaker-Hardware equal to number of BT-Speaker items counted.
order.items.push({ sku:'Speaker-Hardware', qty:totalBtsQty, product: {sku: 'Speaker-Hardware'}})
print('Added '+totalBtsQty+' BT-Speaker Hardware itme(s) to the order.\n')
}
}

Time Manipulation

Automatically Delay Ship Dates for After-Hours and Weekend Orders
  • Timestamp manipulation and delaying orders by a specific number of days and then auto-releasing them in the morning.

This script reviews incoming orders and, unless a delayed ship date is already set or the order is on indefinite hold, automatically postpones the ship date to 9:00am on the next business day if the order is received after 5pm or during the weekend, ensuring orders are only processed during standard business hours.

//  Merchant:  Acme Inc.
// Iteration: 4
// Created At: 2020-04-29
// Updated At: 2023-04-08
// Title: Delay orders to allow for review.
// Description:
// This script will add delayed ship date for orders received:
// -- At or after 5pm delayed till 9am the next day unless it is Friday or the weekend
// -- Friday at or after 5pm delayed till the next Monday at 9am
// -- Saturday and Sunday delayed till Monday at 9am
// However, if the order was originally submitted with a Delayed Ship Date then
// no action will be taken. A note will be left in history to indicate the
// order was evaluated.
// Note: Orders that are already on indefinite hold will be skipped.
//

// Constant Variables
const currentTimestamp = order.timestamp.format('D M jS, Y (T)') // Get timestamp
const day = parseInt(order.timestamp.format('w'))
const hour = parseInt(order.timestamp.format('G'))
const releaseHour = 9 // hour to unhold orders; 24hr time.
const before9AM = hour < 9 // any hour before 9am
const after4PM = hour > 16 // any hour after 4pm. Excludes 4pm

// Checking that the Order is not already being held...
if (order.options.hold_indefinitely !== true) {

// If delayed ship date is not set... continue
if (order.options.delayed_ship_date === ''
|| typeof order.options.delayed_ship_date === 'undefined'
|| order.options.delayed_ship_date === null)
{

// action variables
let changed = false
let addDays = ''
let message = ''

// When delayed ship date is not already set do...
switch (day) {
case 5: // Friday

// Before 9am then place on hold till 9am
if (before9AM) {
changed = true
message = 'Order received before 9:00am on Friday.\n'
addDays = '+0 day'
}
// After or equal to 5pm add 3 days
if (after4PM) {
changed = true
message = 'Order received at or after 5:00pm on Friday.\n'
addDays = '+3 day'
}
break;
case 6: // Saturday add 2 days
changed = true
message = 'Order received on Saturday.\n'
addDays = '+2 day'
break;
case 0: // Sunday add 1 day
changed = true
message = 'Order received on Sunday.\n'
addDays = '+1 day'
break;
default:
// Before 9am then place on hold till 9am
let weekdays = { 1: 'Monday', 2: 'Tuesday', 3: 'Wednesday', 4: 'Thursday'}
if (before9AM) {
changed = true
message = 'Order received before 9:00am on '+weekdays[day]+'.\n'
addDays = '+0 day'
}
// After or equal to 5pm
if (after4PM) {
changed = true
message = 'Order received at or after 5:00pm on '+weekdays[day]+'.\n'
addDays = '+1 day'
}
}
if (changed) {
print('Today\'s Date: '+(currentTimestamp)+'\n') // Print now Date
print(message)
if (order.options.hold_indefinitely !== true) {
changeDelayedShipDate(addDays)
}
print('Order on hold '+(order.options.hold_indefinitely ? '':'till: 9:00am '+(order.timestamp.format('D M jS, Y (T)'))+'\n')) // Print delayed Date
}
} else {
// When delayed ship date was already set do...
print('No action taken. Order was submitted with a Delayed Ship Date of "'+order.options.delayed_ship_date+'".\n')
}
}

function changeDelayedShipDate (daysAdded) {
// Formatted to release at the top of the hour.
order.options.delayed_ship_date = order.timestamp.modify(daysAdded).format('Y-m-d '+(releaseHour)+':0:0')
}

Order Item Manipulation

Automatically Convert 1-pack SKU to Multi-Pack SKU in Orders
  • Example of transforming submitted SKUs with a BCO script

This script checks for orders containing multiple single-pack TRAY SKUs and automatically converts pairs into double-pack combo SKUs, updating the order quantities and leaving a message when changes are made.

//  Merchant:  Acme Inc.
// Iteration: 3
// Created At: 2021-05-21
// Updated At: 2022-08-02
// Title: Tranformed 'TRAY-<type>' into approriate number of combo SKUs.
// Description:
// If only the base sku is found then the quantity will be checked. If only one
// is ordered then no changes will be made. When more than one are ordered,
// then the script will divide the quantity by 2. The integer will be set as
// the amount of the "-X2" sku that will be added. The integer of the remainder
// will be set as the new quantity for 'TRAY-BCH'. An approriate message will
// be printed when changes are made.
//

// Match exact SKUs for singles
const traySkus = ['TRAY-BCH','TRAY-BLK','TRAY-WNT']

// Add this suffix to get doubles
const skuSuffix = '-X2'

// For each single-pack SKU
traySkus.forEach(function(traySku) {
let singlePackIndex = order.items.findIndex(item => item.sku === traySku)

// if found in the order items
if (singlePackIndex > -1) {

// divide the qty by two and floor to get the qty of double-packs
let doublePackQty = Math.floor(order.items[singlePackIndex].qty / 2)

// then if there is enough to make one or more double packs
if (doublePackQty > 0) {

// check for existing double-pack item and update it
let doublePackIndex = order.items.findIndex(item => item.sku === (traySku+skuSuffix))
if (doublePackIndex > -1) {
order.items[doublePackIndex].qty = parseInt(order.items[doublePackIndex].qty) + doublePackQty
}

// or else add a new item for the double-pack
else {
order.items.push({ sku: traySku+skuSuffix, qty: doublePackQty, product: {sku: traySku+skuSuffix}})
}

// then subtract the quantity replaced by double packs from the single packs
order.items[singlePackIndex].qty -= (doublePackQty*2)

// and remove the single-pack item if the new qty is 0
if (order.items[singlePackIndex].qty === 0) {
order.items.splice(singlePackIndex, 1)
}

// and add a helpful message
print('Replaced '+(doublePackQty*2)+' of '+traySku+' with '+doublePackQty+' of '+traySku+skuSuffix+".\n")
}
}
})

Add Packaging Features

Add Special Packing Instructions for Orders Matching Parameter

This script checks if "ABS" appears in the order's shipping address or the recipient's first name, and if so, adds a specific packing instruction to the order.

//	Merchant: Acme Inc.
// REV: 2022-11-07
// Add packing instructions pouch if order address contains "ABS"

const isABS = Boolean(order.address.street.match(/( ABS|^ABS)/) || order.address.firstname.match(/( ABS|^ABS)/))

if(isABS) {
print ("Adding Packaging Feature Instruction to order.\n")

// Add a PF Instruction to the order
Object.assign(order.options.other_shipping_options || {}, {
"packaging": [{
"sku": "packslip_boxtop"
}]
})
}

Add Packing/Order Instructions to the order by script

Change Shipping Method and Add Order Instruction for Lightweight Single-Item Orders

This script automatically changes the shipping method to USPS Priority Mail and adds a packing instruction when an order contains only one item, with a quantity of one, weighing 2 lbs. or less.

// If only one SKU AND only one qty AND weighs less then or equal to 2 lbs. 
// THEN change shipping method and add a Packing Instruction
if (order.items.length == 1 && order.items[0].qty == 1 && order.items[0].product.weight <= 2) {

// Sets Shipping Method
order.options.shipping_method = "usps_US-PM"

// Add a Packing Instruction to the order
let instruction = {
"note" : "Package using a Jiffy Mailer.",
"presentation" : "once_per_order",
}
if ( ! order.options.instructions) {
order.options.instructions = []
}
order.options.instructions.push(instruction)

// Print note to Order History
print('A single product item ordered with weight less then or equal to 2 lbs.; Changing shipping method to to "USPS – PM" and adding a Packing Instruction')
}

Add Reference Numbers

Specify where Reference Numbers are Added

This script adds invoice and purchase order reference numbers to the shipping label for each order.

//  Merchant: Acme Inc
// REV: 2023-02-08
// Add ref numbers to shipping labels

order.options.other_shipping_options = {
"reference_numbers":
{
"default": null,
"invoice": order.options.unique_id,
"purchase_order": order.options.order_ref
}
}

Add Carrier-Specific Reference Numbers

This script checks for a customer note on the order and, if present, adds it to the shipping label as a reference number for UPS and FedEx shipments.

//  CLIENT: Acme Inc.
// REV: 2024-08-22
// Add Customer Note to Shipping Label

if (order.options.note.length > 0) {
const CustomerNote = order.options.note
order.options.other_shipping_options = {
"reference_numbers": {
"default" : order.options.order_ref,
"ups": {
"AT": CustomerNote
},
"fedex": {
"DEPARTMENT_NUMBER": CustomerNote
}
}
}
print('Adding "'+CustomerNote+'" note to shipping label.\n')
}

How did we do?

Before Create Order Scripts

Preprocess Packing Solution Scripts

Contact