Please enable JavaScript to view this site.

QuoteWerks Help
Version 25 (Build 2.18) 
May 30, 2025
  •      

Navigation: QuoteWerks Web > Using QuoteWerks Web

QuoteWerks Web Scripting

Scroll Prev Top Next More

Scripting in QuoteWerks Web can be used to extend functionality based on system events. The QuoteWerks Web Scripting Environment is where you can write powerful async JavaScript scripts to automate, customize, and extend your quoting workflows. This guide will walk you through everything you need to know to build, debug, and deploy custom scripts using the APIs and Event Hooks.

Users can access the QuoteWerks Web Scripting Environment by navigating to the Scripting Manager on the left-hand navigation bar.

To learn how to navigate and use the QuoteWerks Web Scripting Environment please see QuoteWerks Web Scripting Manager.

Overview

 

Scripts are written in async JavaScript and respond to lifecycle events like AfterSaveDocument, BeforeDeleteDocument, and more. They execute in a secure sandbox with access to a rich set of API functions exposed under context.api.

Scripts may display messages, modify document data, interact with the user, make HTTP calls, and more.

How it Works

 

Each event provides a context object with data and access to the context.api.

Your script is executed as an async function.

Set context.cancel = true or false at the end of your "BEFORE" scripts to control whether the normal execution should proceed.

Use await with every API call.

Common Script Pattern

 

 

// Show a toast after saving a document
await context.api.sys_toastSuccess("Document saved!");
context.cancel = false;

 

 

Available Event Hooks

 

Events are triggered before or after key user actions. Below are common events with context info:

Event Name

When it Fires

Cancelable

BeforeSaveDocument

Before a document is saved

AfterSaveDocument

After a document is saved

BeforeDeleteDocument

Before deleting a document

AfterDeleteDocument

After deleting a document

BeforeConvertDocument

Before converting a document

AfterConvertDocument

After converting a document

BeforeRenameDocument

Before renaming a document

AfterRenameDocument

After renaming a document

BeforeContactSelection

Before selecting a contact

AfterContactSelection

After selecting a contact

BeforePreviewAction

Before print/PDF generation

AfterOpenDocument

After opening a document

AfterLineItemAdded

After a new line item is added

BeforeEmailRecipientSelection

Before selecting email recipients

 

See the script examples section for additional details.

 

API Reference

 

System Functions

 

sys_logInfo(message)

sys_logWarn(message)

sys_logError(message)

sys_promptUser(type, title, message)

sys_promptUserSelect(title, message, default, list, labelField, valueField)

sys_promptUserDynamic(formDefinition, initialModel) (See detailed documentation below)

sys_delay(seconds)

sys_loaderStart(message)

sys_loaderStop(id)

sys_toastSuccess(message)

sys_toastInfo(message)

sys_toastWarn(message)

sys_toastError(message)

sys_aiGenerateText(userPrompt)

sys_httpRequest(options)

 

Application Functions

 

app_getMacro(macroName)

app_baseCurrencySymbol()

app_decimalCharacter()

app_decimalPlacesExt()

app_decimalPlacesUnit()

app_thousandsSeparator()

app_formatAsCurrency(value)

app_versionMajor()

app_versionMinor()

 

Document Functions

 

doc_getActiveDocumentIndex()

doc_documentNew(type)

doc_documentClose()

doc_documentSave()

doc_getDocumentHeaderValue(fieldName)

doc_setDocumentHeaderValue(fieldName, value)

doc_anyOpen()

 

Item Functions

 

item_addLineItemToDocument(lineItemDetails)

item_removeLineItemFromDocument(rowIndex)

item_lineItemGetValue(rowIndex, fieldName)

item_lineItemSetValue(rowIndex, fieldName, value)

item_lineItemCount()

 

Specific Call Details

 

item_addLineItemToDocument(lineItemDetails)

 

//let samplelineItemDetails = { // quantity: 5, // description: "High-end Widget", // unitCost: 45.5, // unitPrice: 60, // unitList: 75, // priceModifier: "", // manufacturer: "Acme Corp", // manufacturerPartNumber: "ACM-12345", // vendor: "WidgetSupply", // taxCode: "Y", // itemType: 1, // itemAttributes: 1, // insertFlag: false, // insertBeforeRow: 0 //}

 

Dynamic Form

 

Function: context.api.sys_promptUserDynamic(formDefinition: object, initialModel: object): Promise

Description: Displays a dynamic form for the user to fill out based on a provided form definition.

Returns the user's completed model object, or USERCANCELLED if the user cancels.

 

Form Definition Parameters:

title (string, required): Title of the form window

fields (array of objects, required): Defines the form fields

 

Each field object supports:

key (string, required): Name of the field in the model

label (string, required): Label displayed to the user

type (string, required): Field type ("text", "email", "date", "number", "textarea", "radio", "checkbox", "select")

options (array, optional): List of options for "radio" and "select" field types

required (boolean, optional): Whether the field must be filled out (true or false)

 

Initial Model Parameters:

(object, optional): Prefills the form fields with existing values.

 

Example Usage:

 

const formDefinition = {
  title: "User Info",
  fields: [
    { key: "name", label: "Name", type: "text", required: true },
    { key: "email", label: "Email", type: "email", required: true },
    { key: "dob", label: "Date of Birth", type: "date" },
    { key: "age", label: "Age", type: "number" },
    { key: "bio", label: "Biography", type: "textarea" },
    { key: "gender", label: "Gender", type: "radio", options: ["Male", "Female", "Other"], required: true },
    { key: "subscribe", label: "Subscribe to newsletter", type: "checkbox" },
    { key: "country", label: "Country", type: "select", options: ["USA", "Canada", "Mexico"], required: true }
  ]
};
 
const initialModel = {
  name: "John Lewe"
};
 
const userInput = await context.api.sys_promptUserDynamic(formDefinition, initialModel);

 

HTTP Requests

 

Function: context.api.sys_httpRequest(options: object): Promise

Description: Performs an HTTP request using the specified options.

Supports GET, POST, PUT, DELETE, and other HTTP methods. Returns a response object including status, statusText, headers, and data.

If the request fails, an object is returned with error: true, message, and any available status or data.

 

Options Object Parameters:

method (string, optional): HTTP method (default is "GET")

url (string, required): Full URL for the request

headers (object, optional): Key-value pairs of headers to send

data (object|string|null, optional): Payload for POST, PUT, etc.

params (object|null, optional): Query string parameters

timeout (number, optional): Timeout in milliseconds (default is 30000)

 

Example Usage:

 

const response = await context.api.sys_httpRequest({

  method: "POST",

  url: "https://api.example.com/send",

  headers: {

    "Authorization": "Bearer token",

    "Content-Type": "application/json"

  },

  data: {

    message: "Hello world"

  }

});

 

Sample Event Scripts

 

AfterContactSelection

let type;

switch (context.type) {

  case 0:

    type = 'Sold To'

    break;

  case 1:

    type = 'Ship To'

    break;

  case 2:

    type = 'Bill To'

    break

  case 3:

    type = 'All'

    break;

}

 

context.api.sys_toastSuccess(`AfterContactSelection (${type})`);

AfterConvertDocument

let result;

 

switch (context.result) {

  case 0:

    result = 'Success'

    break;

  case 1:

    result = 'Cancelled'

    break;

  case 2:

    result = 'Failed'

    break

}

 

context.api.sys_toastSuccess(`AfterConvertDocument: ${result}`);

AfterDeleteDocument

let result;

 

switch (context.result) {

  case 0:

    result = 'Success'

    break;

  case 1:

    result = 'Cancelled'

    break;

  case 2:

    result = 'Failed'

    break

}

 

context.api.sys_toastSuccess(`AfterDeleteDocument (${context.docRecGuid}): ${result}`);

AfterNewDocument

let source;

 

switch (context.source) {

  case 0:

    source = 'Blank'

    break;

  case 1:

    source = 'Template'

    break;

  case 3:

    source = 'Duplication'

    break

}

 

context.api.sys_toastSuccess(`AfterNewDocument (${context.docRecGuid}): ${source}`);

AfterOpenDocument

context.api.sys_toastSuccess(`AfterOpenDocument (${context.docRecGuid})`);

AfterRenameDocument

let result;

 

switch (context.result) {

  case 0:

    result = 'Success'

    break;

  case 1:

    result = 'Failure'

    break;

}

 

context.api.sys_toastSuccess(`AfterRenameDocument (${context.docRecGuid}): ${result}`);

AfterSaveDocument

let saveAction;

switch (context.saveAction) {

  case 0:

    saveAction = 'Save'

    break;

  case 1:

    saveAction = 'Save As'

    break;

  case 2:

    saveAction = 'Save As Template'

    break;

  case 3:

    saveAction = 'Save As Revision'

    break;

  case 4:

    saveAction = 'Auto Save'

    break;

}

 

let result;

switch (context.result) {

  case 0:

    result = 'Success'

    break;

  case 1:

    result = 'Save Failed'

    break;

  case 2:

    result = 'Save Canceled'

    break;

  case 4:

    result = 'Document is Locked'

    break;

  case 32:

    result = 'Document is ReadOnly'

    break;

  case 64:

    result = 'Document is ViewOnly'

    break;

}

 

context.api.sys_toastSuccess(`AfterSaveDocument (${context.docRecGuid}): ${saveAction} | ${result}`);

AfterLineItemAdded

context.api.sys_toastSuccess(`AfterLineItemAdded (${context.docRecGuid}): New Line Item Index: ${context.newI

temIndex} | Action: ${context.actionName}`);

AfterShippingSelection

let result;

switch (context.result) {

  case 0:

    result = 'Success'

    break;

  case 1:

    result = 'Failed / User Cancelled'

    break;

}

 

context.api.sys_toastSuccess(`AfterShippingSelection (${context.docRecGuid}) | ${result}`);

BeforeContactSelection

let type;

 

switch (context.type) {

  case 0:

    type = 'SoldTo'

    break;

  case 1:

    type = 'ShipTo'

    break;

  case 2:

    type = 'BillTo'

    break;

}

 

context.api.sys_toastSuccess(`BeforeContactSelection (${context.docRecGuid}) : ${type} `);

context.cancel = false;

BeforeConvertDocument

let destinationType;

 

switch (context.destinationType) {

  case 0:

    destinationType = 'Order'

    break;

  case 1:

    destinationType = 'Invoice'

    break;

  case 2:

    destinationType = 'Lost Sale'

    break;

}

 

context.api.sys_toastSuccess(`BeforeConvertDocument (${context.docRecGuid}) : ${destinationType} `);

context.cancel = false;

BeforeDeleteDocument

let source;

 

switch (context.source) {

  case 0:

    source = 'File|Delete menu'

    break;

  case 1:

    source = 'File|Open menu'

    break;

  case 2:

    source = 'Template Tab'

    break

}

 

context.api.sys_toastSuccess(`BeforeDeleteDocument: (Source: ${source})`);

context.cancel = true;

BeforeDeliver

context.api.sys_toastSuccess(`BeforeDeliver (${context.docRecGuid})`);

context.cancel = false;

BeforeEmailRecipientSelection

let recipientType;

 

switch(context.recipientType){

  case 0: // From

    recipientType = 'From';

    context.from = generateRandomEmail();

    break;

  case 1: // To

    recipientType = 'To';

    context.toList.push(generateRandomEmail());

    break;

  case 2: // CC

    recipientType = 'CC';

    context.ccList.push(generateRandomEmail());

    break;

  case 3: // BCC

    recipientType = 'BCC';

    context.bccList.push(generateRandomEmail());

    break;

}

 

context.api.sys_toastSuccess(`BeforeEamilRecipientSelection (${context.docRecGuid}) : ${recipientType} `);

context.cancel = true;

 

function generateRandomEmail(domain = "example.com") {

    const chars = "abcdefghijklmnopqrstuvwxyz0123456789";

    const usernameLength = Math.floor(Math.random() * 10) + 5; // length between 5 and 14

    let username = "";

 

    for (let i = 0; i < usernameLength; i++) {

        username += chars.charAt(Math.floor(Math.random() * chars.length));

    }

 

    return `${username}@${domain}`;

}

BeforePreviewAction

let action;

switch (context.action) {

  case 0:

    action = 'Preview / Print'

    break;

  case 1:

    action = 'Save As PDF'

    break;

}

 

let source;

switch (context.source) {

  case 0:

    source = 'Print Layout'

    break;

  case 2:

    source = 'PO Print Layout'

    break;

  case 3:

    source = 'Management Report'

    break;

}

 

let layoutType;

switch (context.layoutType) {

  case 0:

    layoutType = 'Quote'

    break;

  case 1:

    layoutType = 'Order'

    break;

  case 2:

    layoutType = 'Invoice'

    break;

  case 3:

    layoutType = 'Sales Order'

    break;

  case 4:

    layoutType = 'Purchase Order'

    break;

}

 

context.api.sys_toastSuccess(`BeforePreviewAction (${context.docRecGuid}) | ${action} | ${source} | ${layoutType} `);

console.log(context);

context.cancel = false;

BeforeRenameDocument

context.api.sys_toastSuccess(`BeforeRenameDocument: ${context.docRecGuid}`);

context.cancel = false;

BeforeSaveDocument

context.api.sys_toastSuccess(`BeforeSave (${context.docRecGuid})`);

context.cancel = false;

BeforeShippingSelection

context.api.sys_toastSuccess(`BeforeShippingSelection (${context.docRecGuid})`);

context.cancel = false;

 

Field References

 

Header Field Names

acceptanceNotes

alternateCommissionAmount

alternateCurrencyIdentifier

alternateCurrencySymbol

alternateDepositAmount

alternateGrandTotal

alternateGSTTax

alternateLocalTax

alternateProfitAmount

alternateShippingAmount

alternateSubTotal

alternateTaxableAmount

alternateTotalCost

alternateTotalList

alternateTotalTax

approvalRequestedMethod

approvalRequestedOn

approvedBy1

approvedBy2

approvedMethod1

approvedMethod2

approvedOn1

approvedOn2

attachments

baseCurrency

billToAddress1

billToAddress2

billToAddress3

billToCity

billToCMCompanyRecID

billToCMContactRecID

billToCompany

billToContact

billToCountry

billToEMail

billToFax

billToFaxExt

billToMobile

billToPhone

billToPhoneExt

billToPostalCode

billToState

billToTitle

closingNotes

commissionAmount

commissionStructure

contractEndDate

contractStartDate

convertedBy

convertedOn

convertedRef

convertedFrom

convertedFromBy

convertedFromOn

convertedFromRef

convertedTo

convertedToBy

convertedToOn

convertedToRef

coverPageMessage

created

createdBy

customDate01

customDate02

customMemo01

customMemo02

customMemo03

customMemo04

customNumber01

customNumber02

customNumber03

customNumber04

customNumber05

customText01

customText02

customText03

customText04

customText05

customText06

customText07

customText08

customText09

customText10

customText11

customText12

customText13

customText14

customText15

customText16

customText17

customText18

customText19

customText20

customText21

customText22

customText23

customText24

customText25

customText26

customText27

customText28

depositAmount

depositAmountRef

depositMethodData

docDate

docDueDate

docName

docNo

docStatus

docType

dynamicStorageDH

emailMessage

exchangeRate

exchangeRateLastUpdated

expirationDate

fob

grandTotal

gstTax

gstTaxableAmount

gstTaxExempt

gstTaxRate

holdFlags

docId

integrationData

internalNotes

introductionNotes

itemsDirty

lastModified

lastModifiedBy

lastRow

loadingFlag

localTax

localTaxRate

locked

ltFileLinksList

miscDirty

peerReviewRequestedMethod

peerReviewRequestedOn

preparedBy

profitAmount

profitPercent

projectNo

psTisCompounded

pstTaxableAmount

purchasingNotes

dynamicNotes

quoteValetDocumentID

readOnlyMode

docRecGUID

recurringRevenueAnnualSubtotal

recurringRevenueAnnualSubtotalWithTax

recurringRevenueMonthlySubtotal

recurringRevenueMonthlySubtotalWithTax

recurringRevenueQuarterlySubtotal

recurringRevenueQuarterlySubtotalWithTax

recurringRevenueWeeklySubtotal

recurringRevenueWeeklySubtotalWithTax

resources

revisionMasterDocNo

salesRep

serviceRep

shippingAmount

shippingCost

shippingPricingMethodData

shipToAddress1

shipToAddress2

shipToAddress3

shipToCity

shipToCMCompanyRecID

shipToCMContactRecID

shipToCompany

shipToContact

shipToCountry

shipToEMail

shipToFax

shipToFaxExt

shipToMobile

shipToPhone

shipToPhoneExt

shipToPostalCode

shipToState

shipToTaxCode

shipToTitle

shipVia

soldToAddress1

soldToAddress2

soldToAddress3

soldToCity

soldToCMCompanyRecID

soldToCMContactRecID

soldToCMCallRecID

soldToCMLinkedDocumentRecID

soldToCMOpportunityRecID

soldToCMQuoteRecID

soldToCompany

soldToContact

soldToCountry

soldToEMail

soldToFax

soldToFaxExt

soldToMobile

soldToPhone

soldToPhoneExt

soldToPONumber

soldToPostalCode

soldToPriceProfile

soldToState

soldToTitle

stateContentChangedForQuoteValet

stateContentChangedForQuoteWerks

stateCRMNeedsUpdating

stateDocumentIntegrityLeasingWarningMsg

stateInitiallyLocked

stateNeverBeenSaved

stateOpenedOn

statePrintedSinceOpened

stateQuoteValetCustomerAcceptedStateText

stateQuoteValetCustomerUploadedStateText

stateQVAcceptedOnString

stateQVCreatedByEntity

stateQVCustomerFacingTemplateID

stateQVDocStatusCode

stateQVPaymentCount

stateQVUploadedForCustomerOn

subTotal

superseded

synchedWithQuoteValet

taxData

taxSystem

templateType

terms

totalCost

totalList

totalTax

totalWeight

transactionLog

used

version

viewOnlyMode

Item Field Names

__quoteSheetItemDescription

__uniqueId

__vendorRFQAwarded_VendorRFQBidRequestItemRecGUID

__vendorRFQViewUrl

category

closeProbability

costModifier

customDate01

customDate02

customMemo01

customMemo02

customMemo03

customMemo04

customMemo05

customNumber01

customNumber02

customNumber03

customNumber04

customNumber05

customText01

customText02

customText03

customText04

customText05

customText06

customText07

customText08

customText09

customText10

customText11

customText12

customText13

customText14

customText15

customText16

customText17

customText18

customText19

customText20

description

distributorSONumber

documentItemCssClasses

extendedCost

extendedList

extendedPrice

extendedShippingAmount

exclusiveOptionGroup

itemTaxSummary

itemType

itemURL

lineIdentifier

lineNumber

lineNumberActual

lineType

manufacturer

manufacturerPartNumber

notes

poNumber

priceModifier

profitAmount

profitMargin

qtyBase

qtyMultiplier1

qtyMultiplier2

qtyMultiplier3

qtyMultiplier4

qtyTotal

recurringAmountWithTax

recurringBillingCycle

recurringEndDate

recurringRevenueAnnual

recurringRevenueAnnualWithTax

recurringRevenueMonthly

recurringRevenueMonthlyWithTax

recurringRevenueQuarterly

recurringRevenueQuarterlyWithTax

recurringRevenueWeekly

recurringRevenueWeeklyWithTax

recurringStartDate

salesTax

shippingAmount

styleCode

taxCode

ticketNumber

unitCost

unitList

unitPrice

unitWeight

unitofMeasure

unitofMeasureFactor

unitofPricing

unitofPricingFactor

vendor

vendorPartNumber

vendorRFQItemInfo

vendorRFQViewUrl

Example Script

 

// This script allows the user to look up exchange rates using the Frankfurter API.

// It first retrieves the available currencies and then prompts the user to select

// the 'from' and 'to' currencies using a dynamic form.

 

// Step 1: Fetch available currencies from the Frankfurter API

const currencyResponse = await context.api.sys_httpRequest({

  method: "GET",

  url: "https://api.frankfurter.app/currencies"

});

 

if (currencyResponse.error) {

  await context.api.sys_toastError("Failed to fetch currencies.");

  context.continue = false;

  return;

}

 

const currencies = Object.keys(currencyResponse.data);

 

// Step 2: Prompt the user to select 'from' and 'to' currencies

const formDefinition = {

  title: "Currency Exchange Rate Lookup",

  fields: [

    { key: "fromCurrency", label: "From Currency", type: "select", options: currencies, required: true },

    { key: "toCurrency", label: "To Currency", type: "select", options: currencies, required: true }

  ]

};

 

const initialModel = {};

const userInput = await context.api.sys_promptUserDynamic(formDefinition, initialModel);

 

if (userInput === "USERCANCELLED") {

  context.continue = false;

  return;

}

 

const fromCurrency = userInput.fromCurrency;

const toCurrency = userInput.toCurrency;

 

// Step 3: Fetch the exchange rate for the selected currencies

const exchangeRateResponse = await context.api.sys_httpRequest({

  method: "GET",

  url: `https://api.frankfurter.app/latest?from=${fromCurrency}&to=${toCurrency}`

});

 

if (exchangeRateResponse.error) {

  await context.api.sys_toastError("Failed to fetch exchange rate.");

  context.continue = false;

  return;

}

 

const exchangeRate = exchangeRateResponse.data.rates[toCurrency];

 

// Step 4: Display the exchange rate to the user

await context.api.sys_promptUser("info", "Exchange Rate", `The exchange rate from ${fromCurrency} to ${toCurrency} is ${exchangeRate}.`);

 

context.continue = true;