Skip to main content

Extension points

On this page we showcase the currently available and supported extension points for scripting.

Variables you can use

See the Available variables section to check which variables are available per extension point.

Permissions

Influencing different parts of EVA via scripting requires a scoped scripting permission on the corresponding functionality impacted (here are a few):

ExtensionPointFunctionalityScope
CalculateShippingCostsShippingCostsScripting
CheckOrderLineValidationOrdersScripting
CheckOrderValidationOrdersScripting
CheckoutOptionsCheckoutOptionsScripting
DiscountFinancialDispersionDiscountsScripting
EventExportEventExportConfigurationScripting
GenerateOrderlineCancellationDiscountCouponDiscountCouponsScripting
GenerateUserDiscountCouponDiscountCouponsScripting
OpenIDAuthorizationOpenIDProvidersScripting
OrderCustomStatusOrderCustomStatusScripting
OrderCustomTypeOrderCustomTypesScripting
OrdersMonitorMonitorsScripting
UserTaskPriorityUserTasksScripting

Restrict product sales unless elevated

Extension point name: CheckOrderLineValidation

This extension point adds an extra checkout step where a store supervisor/manager approval is prompted on sales of a specific item (OrderLine). The script would be an extension to CheckOrderLineValidation that would script an OrderRequirement and in turn require an employee verification.

The scripted behavior is as follows:

  • POS and Companion will respond to an Employee Verification step needed on a specified OrderLine and initiate an elevation flow.
  • There are two methods to verify an order, either by a QR code scan or a PIN code entry by a user with VerifyOrder rights. After a verification is performed, the regular output of the extension point continues as usual. In our example here, it's the "validate" output.

Sample script


{ "Name" = "Verify orderline",
"Dialect" = "Extension",
"IsActive" = "true",
"Source" = "extend CheckOrderLineValidation

set validate to false

if OrderLine.Product.BackendID = 'PRODUCT_1' then
set validate to true
end

if OrderLine.IsVerified then
set validate to false
end

output validate"

}

Whereas,

Name is string,
Dialect is the “extension”,
IsActive is a boolean,
Source is as shown above.

Things to consider for this scenario

If you want the VerifyOrder elevation to be via Pin code then the setting Security:ElevatedFunctionalityProvider should be set with a value TemporaryElevationCode. In POS App a user can find the pin code in Generate elevation code and on Companion App it's under the information chapter. Further, you can specify the validity period of such Pin code using the setting Security:TemporaryElevationCode:ExpireInMinutes (default validity period is 20 hours if setting left untouched).

Restrict order returns unless elevated

Extension point name: CheckOrderLineValidation

This extension point adds an extra checkout step where a store supervisor/manager approval is prompted on return of a specific item (OrderLine). The script would be an extension to CheckOrderLineValidation that would script an OrderRequirement and in turn require an employee verification.

Sample scripts

{     "Name" = "Verify orderline",
"Dialect" = "Extension",
"IsActive" = "true",
"Source" = "extend CheckOrderLineValidation

set validate to false

if Order.HasReturnLines then
set validate to true
end

if Order.IsVerified then
set validate to false
end

output validate"

}

Incentive upon subscription

Extension point name: GenerateUserDiscountCoupon

This extension point adds an extra step to a coupon discount action where a newly subscribed/added user (in this scenario a customer) would receive a welcome gift in the form of a discount coupon if certain custom user field requirements are met.

The scripted behavior would be an extension to GenerateUserDiscountCoupon and would script a discount BackendID that would pertain to the coupon.

Prerequisites

A discount with a trigger type Coupon should already be configured on the organization unit in order for this to work. The BackendID of which should be used here as your input.

Sample scripts

{     "Name" = "Generate user discount coupon",
"Dialect" = "Extension",
"IsActive" = "true",
"Source": "extend GenerateUserDiscountCoupon

set discountBackendID to ''

if User.CustomFields = 'ExpectedValue' then
set discountBackendID to 'DiscountABC123'
end

output discountBackendID"

}

When you use a custom user field in scripting, this refers to the name of the custom user field, not the backend one.

GenerateLoyaltyDiscountCoupon

This extension point will let you setup the generation of a coupon upon the changing of loyalty points.


GenerateUserDiscountCoupon sample
{     "Name" = "Generate user discount coupon",
"Dialect" = "Extension",
"IsActive" = "true",
"Source": "extend GenerateUserDiscountCoupon

extend GenerateLoyaltyDiscountCoupon

set discountBackendID to ''

if NewBalance >= 50 then
set discountBackendID to 'Tier1CouponDiscount'
end

if NewBalance >= 100 then
set discountBackendID to 'Tier2CouponDiscount'
end

if GeneratedCouponCount > 0 then
set discountBackendID to ''
end

output discountBackendID"

}

Generate coupon on users birthday

Extension point name: GenerateUserDiscountBirthdayCoupon

This extension point works hand in hand with a discount of trigger type coupon whereas, a user (in this scenario a customer) would receive a discount coupon a certain number of days before their birthday.

Mandatory setting

For this script to work, a mandatory setting called GenerateBirthdayCouponXDaysBefore needs to be set. This setting determines the number of days before a user's birthday when a coupon will be generated. As the name implies, the setting expects an integer specifying how many days before the birthdate a user should receive a coupon.

The scripted behavior would be an extension to GenerateUserDiscountBirthdayCoupon and would script a discounts BackendID (the coupon discount BackendID).

Prerequisites

A discount with a trigger type Coupon should already be configured on the organization unit in order for this to work. The BackendID of which should be used in the script as part of your input.

Sample script


{
"Name" = "Generate user discount coupon",
"Dialect" = "Extension",
"IsActive" = "true",
"Source": "extend GenerateUserDiscountBirthdayCoupon

output 'generate_birthday_coupon_backendid'" (this is the BackendID of the discount with trigger type coupon)

}

note

The output you specify in the script should match the BackendID naming of the corresponding discount with trigger type Coupon. Furthermore, when creating such a discount via the Promotion engine chapter, condition(s) are not mandatory and could be left blank. This would imply that the coupon could be used by the user(s) without any conditions and therefore, instantly benefit from the discounts action.

A few things to keep in mind when using this extenstion point
  • A task will be run daily on all user's birthdate input to determine applicability (time zone of each user is taken into consideration).
  • If meets requirements (birthdate and X days setting), a coupon will be generated and registered so that this type of coupon is generated only once a year.

Discount financial implications

Extension point name: DiscountFinancialDispersion

Excluded discount actions

This script has no impact on discount actions Get a product, Other costs, Pick a product, Custom field discount (except for value determined ones), and Generate a coupon, since financial implications are not applicable to such actions.

This extension point allows you to create a customized discount financial dispersion behavior. One that is different from the default inputs made under the Discount actions Financial implications card. Namely, the inputs made under the fields Distribution type and Products to consider (if applicable to the chosen action).

For the sake of terminology clarity:

  • Distribution type: Will be referred to as FinancialDispersionType.
  • Products to consider: will be referred to as ProductActionFinancialDispersionType.

By opting to use a script for this (ticking the Use script when applicable box found under the Financial implications card), you're basically empowered to customize the behavior of those two fields if certain variables apply.

Once ticked, you will be presented with a dropdown list of created/existing DiscountFinancialDispersion scripts. In other words, the dropdown list will display the ListScripts response, filtered on dialect extensions with a name DiscountFinancialDispersion, which is the underlying extension point for this scripting scenario.

Sample script

 {  "Name": "DiscountFinancialDispersion",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend DiscountFinancialDispersion

set financialDispersion to DefaultFinancialDispersion
set productDispersion to DefaultProductSetFinancialDispersion

if User.CustomFields.InputHere = 'ExpectedValue'
then set financialDispersion to 0
set productDispersion to 0
end

output financialDispersion, productDispersion"

}

What is being attempted from the above script is that if a user custom field "X" is = to an 'Expectedvalue', then default FinancialDispersion and default ProductActionFinancialDispersionType would be overridden to 0 and 0 instead of their respective default inputs. The available enum values for FinancialDispersion and ProductActionFinancialDispersionType can be found here.

Override

A valid script return would always override the default dispersion input(s).

We can also derive from the above sample script that the service return would be an integer array. This corresponds well to action(s) that support both financial and product dispersion, as the case is with the Product sets action. The return however, can also be a single integer. This corresponds well to action(s) that only support a financial dispersion, as the case is with the Order discounts action.

Script returns validation

Some discount actions expect an integer array return and some single. But what if an integer array is returned by the script while only a single integer was expected?

Single integer returns


Discount actions that only require a single integer return from the script, will not fail if an integer array is returned. The extra integer will be ignored.

Let's assume that an underlying discount action required only a FinancialDispersionType to be defined, and that the default input for it was set to 0:

  • Script returns 1

Implication:

FinancialDispersion is set to MostExpensiveToCheapest 1 (Name on front end: Most expensive first) instead of the default input value of 0.

  • Script returns 1.1

Implication:

FinancialDispersion is set to MostExpensiveToCheapest 1 (Name on front end: Most expensive first) instead of the default input value of 0
Second value is ignored.

  • Script returns 9.1

Implication:

FinancialDispersion will remain 0 (the default input value) because the returned script value is invalid (9).
Second value is ignored

  • Script returns 1.9

Implication:

FinancialDispersion is set to MostExpensiveToCheapest 1 (Name on front end: Most expensive first) instead of the default input value of 0
Second value is ignored

Array integer returns


Discount actions that require an integer array return from the script (both a financial and a product dispersion), will not fail if a single integer is returned (financial dispersion only). The missing integer pertaining to ProductActionFinancialDispersionType will revert to the default configured input.

Let's assume that an underlying discount action required both a FinancialDispersion and a ProductActionFinancialDispersionType configured, and that the default inputs were set to 0 for both.

  • Script returns 1

Implication:

FinancialDispersion is set to MostExpensiveToCheapest 1 (Name on front end: Most expensive first)
ProductActionFinancialDispersion remains unchanged. The default value configured on the discount action applies (in this example it would mean AllProductsInSet 0 (Name on front end: All products in action)

  • Script returns 1,1

Implication:

FinancialDispersion is set to MostExpensiveToCheapest 1 (Name on front end: Most expensive first)
ProductActionFinancialDispersion is set to AllDiscountableProductsInSet 1 (Name on front end: All discountable order lines)

  • Script returns 9,1

Implication:

Both FinancialDispersion and ProductActionFinancialDispersion remain unchanged since the FinancialDispersion return value of 9 is invalid. In this example it would mean that both would remain at their default values configured of 0.

Financial and Product Dispersion enum values


The following table represents the enum values pertaining to FinancialDispersionType and ProductActionFinancialDispersionType. Those values are used when creating a discount financial implications script under the `Source` property.
FinancialDispersionType
{
"DivideInProportionToProductPrice" = 0,
"MostExpensiveToCheapest" = 1,
"CheapestToMostExpensive" = 2,
"HighestToLowestTaxRate" = 3,
"LowestToHighestTaxRate" = 4
}
ProductActionFinancialDispersionType
{
"AllProductsInAction" = 0,
"AllDiscountableProducts" = 1,
"AllNonDiscountableProducts" = 2,
"AllDiscountableOrderLines" = 3
}
Naming

ProductActionFinancialDispersionType naming on front end may slightly differ.

Event export

Extension point name: EventExport

This extension point allows you to create a customized event export that would only perform an export if certain filter(s) are matched. This basically means that you can now create filter(s) which you can then specify within the script, and only if that is matched, would the event export be performed and sent to an endpoint of your choice based your event export configuration.

  • The script which will filter your event export configurations, is fed to the event properties, and in case of an event with Target Order, then it would be fed to order properties as well.
  • The script returns a boolean if an event should be exported. So, return true if you want this exported, and false if not. It's as simple as that.

The script is only executed if all other filter parameters match, and thus making it an additional compounded requirement before exporting. Returning nothing or something else from this script, will skip the requirement, and thus end up exporting.

The following Targets are supported:

  • Order
  • PaymentTransaction
  • Invoice
  • Customer
  • TargetOrganizationUnit
  • UserTask
  • Repair
  • StockNotification

Sample script

{
"Name": "EventExport",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend EventExport

set export to false

if Order.Type = 'Sales'
then set export to true
end

output export"

}

Your last step to link this script to your event export would be to take the response ScriptID and place it in the ScriptID property of CreateEventExportConfiguration service or UpdateEventExportConfiguration.

User Task Priority

Extension point name: UserTaskPriority.

This extension point allows you to create a dynamic user task priority which means that you can take in a user task or a user task model as input(s), and return a new priority for that task. The returned priority is a number and would be based on your existing user task priorities scale setup.

Trigger

The script runs after a user task has been created.

Sample script

{
"Name": "UserTaskPriority",
"Dialect": "Extension",
"IsActive": "true",
"Source": "Extend UserTaskPriority

set priority to null

if StartTime has value ``
then set priority to 1234
end

output priority"

}

Generate Order line Cancellation Discount Coupon

Extension point name: GenerateOrderlineCancellationDiscountCoupon

This extension point allows you to send a coupon when customer order lines have been cancelled for whatever reason (ex: out-of-stock, delivery issues, etc...). It is a promotion that is mainly built as a sort of compensation to the incident of having cancelled line(s) on a customers order.

Outcome of the script is a DiscountBackendID on which two things are generated:

  • A discount coupon - sample script and details below
  • A discount coupon event - more on this can be found under Event Exports namely, EventExportTarget 19 "Coupon".

Sample script

{     
"Name": "GenerateOrderlineCancellationDiscountCoupon",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend GenerateOrderlineCancellationDiscountCoupon

set DiscountID to ''

for each CancelledLine in CancelledLineStates do
if CancelledLine.CancellationQuantity > 0
then set DiscountID to 'coupon_cancel'

end
end

output DiscountID"

}

DiscountID "coupon_cancel" specified in the above sample, is the DiscountBackendID for a discount with trigger type Coupon we have set up. This should of course be adjusted based on your respective DiscountBackendID with trigger type Coupon.

Settings
  • Cancellation of order lines can happen at various moments of an order flow. Therefore, a setting called OrderLineCancelled:CustomerInteraction:Timeout can be used to determine the waiting time before a count starts to determine the quantity of cancelled orders. This setting is mainly used to avoid having multiple coupons issued for an order where multiple order lines become cancelled at different moments. The value of this setting is by default set to 10 minutes. The values specified should be in minutes.
  • A setting called OrderLineCancelled:GenerateCoupon needs to be set to true in order for the script to work and actually generate the desired coupon.
  • The CouponCode is then included as a string that is exposed in the root of the OrderLineCancelled stencil.

Orders Monitor

Extension point name: OrdersMonitor

This extension point allows you to create advanced order monitors (in the Scripted Orders monitor) where the script verifies if an order is applicable for monitoring or not. The script lets you specify the desired order property that you would wish to monitor.

If you want to configure these monitors, first create your OrdersMonitor script and then point to this script in your monitor.

Below are a few sample order monitor scripts. These scripts include 1) order returns that were returned using cash as payment method, 2) a script which shows refunds which failed in your orders monitor results on Admin Suite and 3) a script which will check for order data that's exported with a failed payment. The latter is for use with Adyen's fraud detection.

Sample scripts

{
"Name": "OrdersMonitor",
"Dialect": "Extension",
"IsActive": "true",
"Source": "Extend OrdersMonitor

set includeInResult to false

if HasReturnLines = true
then for each item in PaymentTransactions
do if item.Description = 'Cash'
then set includeInResult to true
end
end
end

output includeInResult"

}

User Inquiry

Extension point name: UserInquires.

This extension point allows you to create a dynamic user inquiry where an inquiry would only be prompted if the content specified in the script is met.

As an example, the below script implies that if a customer is added with a first name "James" then the underlying inquiry tagged would be prompted.

Sample script

{
"Name": "UserInquiry",
"Dialect": "Extension",
"IsActive": "true",
"Source": "Extend UserInquiry

set apply to false
if User.FirstName = 'James'
then set apply to true

end

output apply"
}

Single sign-on

Extension point name: OpenIDAuthorization.

This extension point allows you to add a custom script to your JWT token used for SSO. This way you can extend the claims by adding more relevant information to the token, such as an OU and RoleID for example. EVA will then use the information available in the script iteratively.

  • The values for the claims is not something we can influence from our side and should be populated with values in the OpenID provider.
  • This script is ran only once for a User per Login. It will return all active OuSet / Role combinations.
  • New roles will be added to the user, and existing roles that were created by SSO and no longer active will be removed.
  • If you add a role in EVA by hand, it will never be removed.

Sample script

Here we've got two sample scripts, the second one a little more extensive than the first.

{
"Name": "OpenIDAuthorization",
"Dialect": "Extension",
"IsActive": "true",
"Source":

"extend OpenIDAuthorization

set rr to 'OuSetBackendID1|RoleCode2', 'OuSetBackendID3|RoleCode3'

set rr to rr + ('SomeOtherID' + '|' + 'MANAGER')

output rr"
}
Available properties
  • User.FirstName
  • User.LastName
  • User.Nickname
  • User.EmailAddress
  • User.BackendID
  • User.Type
  • Actor
  • Issuer
  • Id
  • ValidFrom
  • ValidTo
  • IssuedAt
  • Audiences (string[])
  • Subject
  • Claims, subdivided in:

    Issuer; Type; Value.


SSO: OpenIDRole

Extension point name: OpenIDRole

The OpenIDRole extension point is the successor to the above OpenIDAuthorization.

This extension point allows for more customization, including the use of OU custom fields, and reduces the need for manual mapping for SSO.

To clarify how this new extension point works with an example:

If you have 5 organization units sets, A through E and 3 roles, X through Z. EVA will invoke the script for every unique set of these, so a total of 5 (sets) times 3 (roles) = 15 times.

The input of the script will be called for every single one of these sets and the script just has to tell EVA if the organization unit set and the role should be allowed access to this combination.

Note: if both the OpenIDAuthorization and the OpenIDRole scripts are active, the OpenIDAuthorization script supersedes.

Using it in practice
  • If an employee was not yet known by EVA but the OpenIDRole script declares access to a store, the employee is created automatically (and employee logs in immediately) with the correct role assigned.

  • If an employee is known by EVA and the script declares access to a store that matches the EVA config, the employee logs in immediately.

  • If an employee is known by EVA and the script declares access to a store while the current EVA config indicates NO access to the store, the store is added to the employee and the previous EVA config is cleared (while the employee logs in immediately). If the current EVA config indicates a different role for that store, the role is added to the employee and previous EVA config is also cleared.

Samples
extend OpenIDRole

for each claim in Claims do
# assuming there is a special claim that the OpenID provider exposes and we are currently checking
# if the role 'SUPER_ADMIN' is being checked

if claim.Type = 'SuperAdmin' and claim.Value = 'true' and Role.Code = 'SUPER_ADMIN' then

# allow the user access to the super admin role on the current organization unit set

set output to true

# could even check, or exclude certain organization unit sets, so the previous line becomes:

if OrganizationUnitSet.OrganizationUnit.CustomFields.GetField('MyExclusionCustomField') = 'true' then
set output to false
end
end
end

Checkout options

Extension point name: CheckoutOptions

This extension point allows you to set specific conditions for when your Checkout options tile(s) would be shown. Once you've created your script, edit the relevant checkout option to attach the ScriptID to it.

Mind that checkout options come with a default script which gets overwritten when you manually add a script.

The samples below each contain a description of their intended function inside.


{
"Name": "CheckoutOptions",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend CheckoutOptions

// This will make the checkout option only apply for orders that are return orders

if Order.HasReturnLines
then set output to true
end"

}

Output.Data

The following script is for the CustomFieldLine checkout option specifically; it benefits from a little extra information.

It differs from the other samples here above due to its data object. Where the checkout option normally shows all order lines with a value for that custom field, this data.Output allows you to filter on those order lines. Only those order lines which fit your filter in the object will then be displayed in the corresponding modal in the front end.

The output.Visible object is still used to determine whether the checkout option needs to be visible or not.

The following sample entails that the checkout option will only be visible in orders with serial numbers, while if you tap the checkout option, it will still display all of the order lines with a serial number (this sample's data object does not filter any further).

extend CheckoutOptions 
set output to false

if Order.Customer has value
set output.Data to Order.OrderLines.Where line -> line.SerialNumber <> null
set output.Visible to Order.OrderLines.HasAny line -> line.SerialNumber <> null

end

To reiterate: For now this output.Data object is limited to CustomFieldLine checkout option.

Barcode result

Extension point name: BarcodeResult

This extension point allows you to influence what will happen when using the ParseBarcode service.

We can divide these rougly into Output.Actions and Output.Types. Using an Action will lead to a certain action being performed, such as displaying a message on the screen. The Output.Type (and TypeParameters) simply passes additional information to EVA, allowing for a different UnitPriceInTax for example.

The following actions and types are supported:

Actions
  • AttachCustomerToOrder
  • SetOrderCustomField
  • AddDiscountCouponToOrder
  • AddManualDiscountToOrder
  • CreatePayment
  • ShowMessage

Note that more than one action can be simultaneously used within the BarcodeResult script.

Types
  • USERTASK
  • SECONDCHANCEPRODUCTREGISTRATION
  • SOCIAL_SECURITY_NUMBER
  • PRODUCT_BARCODE
  • SCRIPT
  • REPAIR
  • CONFIGURE:CFD
  • CONFIGURE:CFD:V2
  • DeviceHub
  • DISCOUNTCOUPON
  • ORDER
  • SCANMODE
  • SESSION
  • STATION
  • USER_BACKENDID
  • USER
  • USER_CUSTOMID
  • USER_SUBSCRIPTION_IDENTIFIER
  • VERIFYCUSTOMER
  • BOARDINGPASS
  • GLOBALBLUE
  • ElevationBarcode
  • EVAPAY:TOTP
  • INITCC
  • SHIP_FROM_STORE_DELIVERY_CONFIRMATION

Sample scripts

The following sample scripts use the actions AttachCustomerToOrder and ShowMessage respectively:

{
"Name": "BarcodeResult",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend BarcodeResult

set barcodeParts to (Split with Source as Barcode, SplitOn as '[your definition of string characters]')
if Like with Source as Barcode, Search as '*[Something thats tells us that its a customer card]*' then
set output.Actions[1].Name to 'AttachCustomerToOrder'
set output.Actions[1].Parameters[1].Name to 'UserID'
set output.Actions[1].Parameters[1].Value to barcodeParts[2]
end"
}
Readability

Although valid, the as keyword is not required after the name of a function parameter.

Example 1

Assume your customer barcodes look like this (note the steering characters):

{
"Barcode": "! 1234567 !"
}

Creating the following script would remove the steering characters, in this example that would be the !.

note

Your input here is space sensitive, thus "the exclamation mark followed by a space" should be the script input for SplitOn in between the apostrophes, as shown below.


{
"Name": "BarcodeResult",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend BarcodeResult

set barcodeParts to (Split with Source as Barcode, SplitOn as '! ')
set output.Actions[1].Name to 'AttachCustomerToOrder'
set output.Actions[1].Parameters[1].Name to 'UserID'
set output.Actions[1].Parameters[1].Value to barcodeParts[2]"
}

The above script will result in a response that would look like this:

{
"Barcode": "1234567"
}
tip

The above script will kick in irrespective the barcode being scanned. Whether the barcode being scanned pertains to a shipment, a product, or a customer/loyalty card, an attempt to attach customer to order from the incoming parse barcode request will be made using the script.

If the script does not succeed in attaching a customer, when let's say a product was scanned and not a customer loyalty card, the result would not be blocking.

However, a sequence or letters that identify the barcode as a customer "something that tells it's a customer card" would be advisable. See example 2 on how the script would then look like.

Example 2

Assume you use prefixes to identify the various barcodes you have. A barcode prefix CUS means it's a customer barcode that's been scanned, PO pertains to products, and so forth.

So, customer related barcode scans would look something like:

{
"Barcode": "! CUS1234567 !"
}

In such scenario the script can be modified slightly in comparison to what it looked like in example 1. That way the script will only kick in if it sees that CUS prefix in the parse barcode request.

The script would look like this (note the extra line "if Like with Source as Barcode, Search as 'CUS' then". This line was not included in the script for example 1):

{
"Name": "BarcodeResult",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend BarcodeResult

set barcodeParts to (Split with Source as Barcode, SplitOn as '! ')
if Like with Source as Barcode, Search as '*CUS*' then
set output.Actions[1].Name to 'AttachCustomerToOrder'
set output.Actions[1].Parameters[1].Name to 'UserID'
set output.Actions[1].Parameters[1].Value to barcodeParts[2]
end"
}

StockNotification

Extension point name: StockNotification

EVA facilitates stock notifications for consumers, to notify them when a certain product is available again. You can read more about in Consumer stock notifications.

Sample script

{
"Name": "StockNotification",
"Dialect": "Extension",
"IsActive": "true",
"Source":

"extend StockNotification

set limit to 0

if OrganizationUnit.BackendID is "OUTLET001" then
set limit to 100
end

output limit"
}

UserTaskSendPushNotification

Extension point name: UserTaskSendPushNotification

By means of scripting you can send your users notifications based on the status (e.g. incoming deadlines) of specific tasks.

Permissions

To be able to create/modify these scripts, you require the Script scope in the Usertasks functionality.

First off you need to create a PushNotification template in Stencil. Use parameters here to differentiate your template based on the TaskType.

Now you can create your script to fine-tune the moment of transmission of your notifications per type.

Sample script

{
"Name": "UserTaskSendPushNotification",
"Dialect": "Extension",
"IsActive": "true",
"Source":

"extend UserTaskSendPushNotification

if OrganizationUnit.BackendID = "36"
and Type = "ShipFromStore"
and SubType = "Ship"
and MinutesUntilDeadline <= 30 then
set output to true
end"
}

CustomFieldSecurity

Extension point name: CustomFieldSecurity

Custom field security refers to the custom fields' visibility, edibility, and required attributes from the logged in user's perspective. Or in other words, if I'm an employee trying to create/update a customer, can I see the custom field? Can I edit it / fill it in? Do I need to fill it in, or is it optional?

EVA offers extended customizability for this by scripting on this extension point. This way you can indicate custom fields to be required only in specific cases, for example when a user has a certain other custom field filled.

Attaching ID to custom field

Once you've created your preferred script, you can enable it by adding its SecurityScriptID to the custom field in question.

On the front-end side, this can be done using the related scripting field called Script. This can also be done via services by specifying the ID in the SecurityScriptID field via SetCustomFieldOptions.

Sample script


{
"Name": "CustomFieldSecurity",
"Dialect": "Extension",
"IsActive": "true",
"Source":

// This example could be set on any [other] custom field to make it visible and required if the user is a loyalty user, but never editable

"extend CustomFieldSecurity

set output.IsRequired to User.CustomFields.IsLoyaltyUser = 'true'
set output.IsVisible to User.CustomFields.IsLoyaltyUser = 'true'
set output.IsEditable to false"

}

Combining security and eligibility

Combining security with eligibility is possible, but there is some overruling involved:

  • Security is visible -> eligibility can say it's not (and not the other way around)
  • Security is not required -> eligibility can say it is (and not the other way around)

CustomFieldEligibility

Eligibility refers to the custom fields' visibility and required attributes, but this time from the perspective of the customer that's being created/updated; in other words, based on the customer information that's being provided. This isn't just limited to customer data, but it paints a clear picture.

This section will show you all available extension points pertaining to eligibility, along with the properties their corresponding variables can contain.

Each script starts off with the basic custom field information, followed by the information specific to its variable, as in the following example:

UserCustomFieldEligibility

CustomFieldTypeID: 4
CustomFieldTypeKey: "key"
CustomFieldDataTypeID: 2
CustomFieldName: "tier"
CustomFieldBackendID: "custom-tier"
User: {
BackendID: "john-doe1234",
FirstName: "John",
LastName: "Doe",
EmailAddress: "john.doe@google.com",
Nickname: "Johnny",
Username: "john.doe",
DateOfBirth: "2023-08-29",
Type: 2 // Customer,
HasCompany: false,
HasSubscriptions: true,
CustomFields: <CustomFieldsVariable>
// accessing i.e User.CustomFields.MyCustomFieldName = "value"
}

This could be used in a script as follows:

UserCustomFieldEligibility

"Name": "CustomFieldSecurity",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend UserCustomFieldEligibility

set output.IsRequired to User.CustomFields.IsLoyaltyUser = 'true'

end"

Attaching ID to custom field

Once you've created your preferred script, you can enable it by adding its EligiblityScriptID to the custom field in question.

On the front-end side, this can be done using the related scripting field called Eligibility script. This can also be done via services by specifying the ID in the EligibilityScriptID field via SetCustomFieldOptions.

There are many extension points available, divided across the following types:

DiscountCustomFieldEligibility
Available data

{ Discount: {
BackendID: "D-3532"
StartDate: "2024-01-01",
EndDate: "2025-01-01",
Enabled: true,
LayerID: 2,
Trigger: 0
}
}

Script example

{
extend DiscountCustomFieldEligibility

if (Discount.Enabled) and
(Discount.BackendID.Contains with Value 'NewBlack') and
(Discount.StartDate > Date with Year 2024, Month 1, Day 1) and
(Discount.EndDate < Date with Year 2025, Month 1, Day 1) and
(CustomFieldBackendID = 'NB-DISCOUNTCUSTOMFIELDELIGIBILITY') then

set output.IsRequired to true
end

}
OrderCustomFieldEligibility

CustomFieldTypeID: 4
CustomFieldTypeKey: "key"
CustomFieldDataTypeID: 2
CustomFieldName: "tier"
CustomFieldBackendID: "custom-tier"
Order: {
UserAgent: "eva-suite-admin/0.27.0",
HasDelivery: false,
HasCarryOut: false,
HasReserveLines: false,
HasLinesToBeOrdered: false,
HasMixedLineActionTypes: false,
HasReturnLines: true,
OrderCustomStatus: "VerifiedByQA",
OrderProperties: 2 // enum OrderProperties,
IsVerified: true,
IsOffer: false,
TotalAmountInTax: 3.5,
SoldFromOrganizationUnit: {
ID: "123",
Name: "Almere",
BackendID: "ou1234",
CountryID: "NL",
Type: 1, // enum OrganizationUnitTypes
Company: {
ID: "456",
Name: "NewBlack",
BackendID: "cb1234",
RegistrationCountryID: "NL"
}
},
ShipFromFromOrganizationUnit: {
// same structure as SoldFromOrganizationUnit
},
PickupOrganizationUnit: {
// same structure as SoldFromOrganizationUnit
},
OrderLines: [
{
ID: 123,
IsVerified: true,
IsCarryOut: false,
Product: {
ID: 456,
BackendID: "p1234",
BackendStatusID: 1, // enum ProductBackendStatus
},
QuantityOrdered: 3,
QuantityShipped: 2,
quantityDelivered: 2,
QuantityCanceled: 1,
QuantityExported: 2,
TotalQuantityToShip: 2,
FulfillmentOrganizationUnit: {
// same structure as SoldFromOrganizationUnit
}
}
],
PaymentTransactions: [
{
// see example from eligibility for payment types
}
],
CustomFields: <CustomFieldsVariable>
// accessing i.e User.CustomFields.MyCustomFieldName = "value"
Customer: {
// see example from eligibility for users
}
}


ShipmentCustomFieldEligibility

CustomFieldTypeID: 4
CustomFieldTypeKey: "key"
CustomFieldDataTypeID: 2
CustomFieldName: "tier"
CustomFieldBackendID: "custom-tier"
Shipment: {
BackendID: "sb1234",
ShippedFromOrganizationUnitID: 123,
TotalQuantityShipped: 4,
NetTotalQuantityShipped: 4,
TotalQuantityDelivered: 4,
NetTotalQuantityDelivered: 4,
ShippingMethodID: 456,
StatusID: 0 // enum ShipmentStatuses
}

UserTaskCustomFieldEligibility

CustomFieldTypeID: 4
CustomFieldTypeKey: "key"
CustomFieldDataTypeID: 2
CustomFieldName: "tier"
CustomFieldBackendID: "custom-tier"
UserTask: {
User: {
BackendID: "john-doe1234",
FirstName: "John",
LastName: "Doe",
EmailAddress: "john.doe@google.com",
Nickanme: "Johnny",
Username: "john.doe",
DateOfBirth: "2023-08-29",
Type: 2 // Customer,
HasCompany: false,
HasSubscriptions: true,
CustomFields: <CustomFieldsVariable>
// accessing i.e User.CustomFields.MyCustomFieldName = "value"
},
Type: {
Name: "ShipFromStore",
SubTypeName: "Ship"
}
}

OrganizationUnitCustomFieldEligibility

CustomFieldTypeID: 4
CustomFieldTypeKey: "key"
CustomFieldDataTypeID: 2
CustomFieldName: "tier"
CustomFieldBackendID: "custom-tier"
OrganizationUnit: {
ID: "123",
Name: "Almere",
BackendID: "ou1234",
CountryID: "NL",
Type: 1, // enum OrganizationUnitTypes
Company: {
ID: "456",
Name: "NewBlack",
BackendID: "cb1234",
RegistrationCountryID: "NL"
}
}

PaymentMethodCustomFieldEligibility

CustomFieldTypeID: 4
CustomFieldTypeKey: "key"
CustomFieldDataTypeID: 2
CustomFieldName: "tier"
CustomFieldBackendID: "custom-tier"
PaymentTransaction: {
BackendID: "pt1234",
BackendSystemID: "spt1234"
Type: {

},
IsRefund: false,
IsAuthorizationAdjustment: false,
Status: 1 // enum PaymentStatuses,
IsConfirmed: false,
Amount: 3.5,
PaidAmount: 3.5,
CapturedAmount: 3.5,
AmountPendingCapture: 0,
AuthorizationAdjustmentAmount: 0,
AmountPendingAuthorizationAdjustment: 0,
Change: 0,
PaymentDate: "2023-08-29",
ReturnedPaymentTransaction: {
// PaymentTransactionVariable
},
OrganizationUnit: {
ID: 123,
Name: "Almere",
BackendID: "oub1234",
CountryID: "NL",
Type: 1 // Shop,
Company: {
ID: "456",
Name: "NewBlack",
BackendID: "cb1234",
RegistrationCountryID: "NL"
}
},
RefundedAmount: 0,
AmountToRefund: 0,
BackendRelationID: "br12345",
CurrencyID: "EUR",
Description: "This is a transaction",
RefundableAmount: 3.5,
HasDispute: false,
HasUnresolvedDispute: false,
IsDownPayment: false,
CaptureMoment: 1 // enum PaymentTypeCaptureMoment,
Properties: 0 // enum PaymentTransactionProperties
}

CaseCustomFieldEligibility

CustomFieldTypeID: 4
CustomFieldTypeKey: "key"
CustomFieldDataTypeID: 2
CustomFieldName: "tier"
CustomFieldBackendID: "custom-tier"
Case: {
BackendID: "c1234",
IsCreatedByCustomer: false,
}

RepairCustomFieldEligibility

CustomFieldTypeID: 4
CustomFieldTypeKey: "key"
CustomFieldDataTypeID: 2
CustomFieldName: "tier"
CustomFieldBackendID: "custom-tier"
Repair: {
IsWarranty: true,
IsImmediateRepair: false,
IsDeferredRepair: true
}

WishlistCustomFieldEligibility

CustomFieldTypeID: 4
CustomFieldTypeKey: "key"
CustomFieldDataTypeID: 2
CustomFieldName: "tier"
CustomFieldBackendID: "custom-tier"
Wishlist: {
BackendID: "w1234",
Products: [
{
ID: 1,
BackendID: "product-1",
BackendStatusID: 1 // None
}
]
}

User Requirements

Extension point name: UserRequirementForCreate Extension point name: UserRequirementForOrder

The user requirement extension point is used to make any user requirement dynamic. Whereas, you link a script to a specific requirement and in turn it returns it when applicable. This link can be made with required for values Create using extension point UserRequirementForCreate, and for values Place order and Payment using extension point UserRequirementForOrder.

Here is a sample script where we link a script to the Phone number user requirement on an order whereas, I want that user requirement to be returned if an order has delivery lines. In other words, the script returns true in case of delivery lines. For this scenario we use the UserRequirementForOrder extension point.

Sample script

{
"Name": "UserRequirementForOrder",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend UserRequirementForOrder

if Order.HasDelivery
then set output to true
end"
}

Here is a sample response

{
"ID": 10012,
"HasErrors": false,
"HasWarnings": false,
"Warnings": []
}

Here is another sample script where we link a script to the FiscalID user requirement on a customer create whereas, I want that user requirement to be returned if the customer is a company. In other words, the script returns true in case of company only and not individual. For this scenario we use the UserRequirementForCreate extension point.

{
"Name": "UserRequirementForCreate",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend UserRequirementForCreate

if (User.HasCompany = true)
then set output to true
end"
}

Response will be similar to the sample above.

Linking scripts to user requirements

Linking a script to a user requirement can be done via the UpdateUserRequirement service by stating the response Script ID from the above script in the ScriptID property, or via Admin Suite by ticking the respective checkbox and selecting the respective script.

OrderCustomType

You can create your own custom order types and use them in your orders by means of scripting. This way you can differentiate your orders when they are between different companies in your organization for example.

{
"Name": "OrderCustomType",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend OrderCustomType

set CustomType to ''

if Order.SoldFromOrganizationUnit.Company.ID = <CompanyID> then
set CustomType to 'SPECIAL_ORDER'
end

output CustomType"

}

Creating a new order custom type

You can create your own types, such as Special_order, via its namesake chapter in Admin Suite or via API by using CreateOrderCustomType.

{
"BackendID": "INT_PO",
"Name": "InterCompanyPurchaseOrder",
"Description": "An Intercompany Purchase Order"
}
Match exact values

Ensure that the Name set in the CreateOrderCustomType matches the one used in the script exactly, including any spaces. Or use the BackendID.

Check with GetOrder

After having created the new type, and the order being validated in the script, you will be able to find the new OrderCustomType in a GetOrder call as follows:

    "OrderCustomType": {
"ID": 2,
"BackendID": "SPECIAL_ORDER",
"Name": "SPECIAL_ORDER",
"Description": "SPECIAL_ORDER"
},

OrderCustomStatus

The OrderCustomStatus script lets you script based on the open amount. These variables include In Tax, PendingInTax and Capturable. It also holds an IsCompleted property and a list of Payments that have the Status information.

The following script samples show you the individual variables in practice and how they might be combined.

{
"Name": "OrderCustomStatus",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend OrderCustomStatus

# Paid status

if Order.PendingInTax = 0 and Order.IsCompleted = false
set status to 'Paid'
end


# Downpayment status

if Order.PendingInTax <> 0
set status to 'Downpayment'
end


# Shipped status

if Order.AreAllLinesShipped
set status to 'Shipped'
end


# Completed status

if Order.IsCompleted
set status to 'Completed'
end


# Cancelled status

if Order.AreAllLinesCancelled
set status to 'Cancelled'
end

PaymentTypeAvailability

By attaching this script to a payment method, you can fine-tune if it should be available for certain orders.

By entering an Order variable in the script, it can judge the order details. If the order does not match the script requirements, it can return false and remove this payment type from the available payment types.

Without the script, true is the default, and of course all other logic that would otherwise remove payment types from availability all still apply - this optional script is always additional.

The following sample makes the payment type in question available only for orders which are going to ship product 9000737.

In the script you can both use the Order variable or PaymentType variable, the latter containing its Code and Name.

{
"Name": "PaymentTypeAvailability",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend PaymentTypeAvailability

set available to false

for each line in Order.OrderLines do
if line.Product.BackendID = '9000737' and line.TotalQuantityToShip <> 0
then set available to true
end
end

output available"
}

ReturnableStatus

EVA facilitates you in making eligibility analyses for returns without retake. Its default is true, for backward compatibility reasons.

For examples of the script's supported variables, see the following section.

Examples for variables
  • Order properties
    • example: order.totalAmountInTax < 50 euro (to cover for the order amount value)
  • Consumer properties
    • example: UserSubscriptionID has value (to cover for the loyalty status)
  • ReturningOrder, which holds:
    • ReturningOrderLines - indicating the lines the customer intends to return
    • TotalAmountInTax - as a sum of the returning lines (negative number), for sake of convenience
  • ReturningOrderLine, which holds:
    • Product (as ProductVariable) - representing the product intended for return
    • ReturningQuantity - for the quantity to be returned (positive number)
    • TotalAmountInTax - for the value this return holds (negative number)

The front end will check the GetReturnableStatusForOrder to analyse the state of the original order, verifying if:

  • the orderLine can still be returned (duration of returnable period: Returnable:InvoicedDays)
  • it can be returned at all (hygienic products)
  • it can be returned in this organization unit
  • it can be returned without retake, i.e. process this ReturnableStatus script

The results of the script can be threefold, which are all specified in the script by means of enum values:

  • None = 0;
  • ReturnWithoutProducts = 1;
  • ReturnWithProducts = 2.

None is the script's default state. None entails that the current behaviour will not be impacted; the decision made by employees (in Admin Suite) on whether to return items or not will be respected.

Additionally, since it's the default, there is rarely a need to specifically use it in your script. You only need to specify the 1 or 2 scenarios unless there's something important for which you want to override the 1 or 2 values again.

The script results are leading, meaning that when selecting 1 or 2 as a result in the script, then that will override any other choices. Additionally, if one order line in the script has ReturnWithoutProducts: 1 (without products), while another has ReturnWithoutProducts: 2 (with products), EVA will override and the order will force products to be returned (regardless of which line is being refunded).

{
# only if the customer has a subscription called 'New Black', will the customer be able to return items

"Name": "ReturnableStatus",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend ReturnableStatus

if Order.Customer.Subscriptions.HasAny line -> line.Subscription.Name = 'New Black' then
set output.IsReturnable to true
set output.Reason to 'Right away sir'
else
set output.IsReturnable to false
set output.ChildrenAreReturnable to false
set output.Reason to 'Can't do that sir'

end"

}
All or nothing

As a general reminder: it is impossible to create a mixed return. Either the customer can keep all products, or none.

AllowOrderLineModificationDuringFulfillment

The following script allows you to set special conditions for when you will be able to edit an order after it's been exported via SFS or C&C.

This means that by default, it's not allowed to make edits to orders unless you configure a script that specifically outputs true.

{
"Name": "AllowOrderLineModificationDuringFulfillment",
"Dialect": "Extension",
"IsActive": "true",
"Source": "Extend AllowOrderLineModificationDuringFulfillment

# You can use any kind of user type you need in here.

if CurrentUser.Type = 'Employee' or CurrentUser.Type = 'Api' then
output true

end"

}

The functionality this script offers is similar to the settings that allow you to modify an order after going through the warehouse order exporter. To show those similarities more clearly, we offer the following sample:


extend AllowOrderLineModificationDuringFulfillment

# Always allow orders fulfilled by warehouse CZ to be changed

if FulfillmentOrganizationUnit.BackendID = 'Warehouse CZ'
output true
end

# Don't allow anything except warehouse NL to modify anything

if FulfillmentOrganizationUnit.BackendID <> 'Warehouse NL'
output false
end

set fromLedger to (
when Reason = 'CancelOrder' then 'X'
else then 'Y')

set toLedger to (when Reason = 'CancelOrder' then 'XYZ'
else then 'ABC')

# Don't allow orders that don't have the required ledger type yet

if (Order.HasLedgerEntries with Type fromLedger) = false then
output false
end

# Don't allow orders that DO have the ledger type

if Order.HasLedgerEntries with Type toLedger then
output false
end

output true

FindSoldFromForPurchaseOrder

The FindSoldFromForPurchaseOrder extension point allows for finding the right OU for your purchase order, which will then serve as the SoldFrom OU for the PO.

To that end, the script will take your purchase order and go through the property called OrganizationUnits which contains all active OUs and consequently find the one that matches your parameters.

Sample
{
"Name": "FindSoldFromForPurchaseOrder",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend FindSoldFromForPurchaseOrder

# Find the first OU in the list of OUs that has BackendID 'HEADQUARTERS'

output OrganizationUnits.First ou -> ou.BackendID = 'HEADQUARTERS'"

}

WatchtowerMonitor

You can create a Watchtower monitor via the Scripting chapter or the Monitors chapter directly.

These scripts allow you to for example keep track of when a Watchtower goes offline, or how often it has to kick in.

Sample - Triggers whenever a Watchtower goes offline

extend WatchtowerMonitor

if Data.Online = false then
output true
end

The notification resulting from the script activation relies on the configuration of the following stencil template: WatchtowerMonitorNotification.

Creating a script directly from the Monitors chapter

When creating a script in the Watchtower Monitor directly, mind that navigating from New Script to Existing script will erase any unsaved progress.

Shipping Restrictions

Extension point name: ShippingRestriction

This extension point allows you to create a customized shipping restriction that would restrict shipments if the specified criteria is matched.

Here is a sample that uses the CountryID to restrict shipments to a specified country.

Linking the script via Admin Suite

Ensure the script is linked to the desired Shipping Restriction through the Admin Suite, under the Shipping chapter, using the Script restriction radio button.

Please refer to the Admin Suite documentation on Shipping restrictions on how this is done.

Sample script

Sample

extend ShippingRestriction
if ShippingAddress.CountryID = 'NL' then
output false
end
output true
Country ID format

This sample uses the CountryID property. The property expects a country code in ISO 3166-2 standard.

Other properties

Another scenario would, for example, use the Address1 in combination with a certain keyword to restrict shipping. The if statement would in this scenario look like this if ShippingAddress.Address1 = 'Alleyway' then

SerialNumberValidation

You can use this extension point to configure the need for a verification or validation of a serial number based on the object in question:

  • OrderLine
  • Repair
  • CaseRelatedItem
  • UserBroughtProduct

This script is included in our serial number flows.

The following sample results in all validations and verification for serial numbers attached to order lines being skipped:

Sample

extend SerialNumberValidation

if OrderLine has value then

set output.SkipValidation to true

set output.SkipVerification to true

else

set output.SkipValidation to false

set output.SkipVerification to false

end