Extension points
On this page we showcase the currently available and supported extension points for scripting.
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):
ExtensionPoint | Functionality | Scope |
---|---|---|
CalculateShippingCosts | ShippingCosts | Scripting |
CheckOrderLineValidation | Orders | Scripting |
CheckOrderValidation | Orders | Scripting |
CheckoutOptions | CheckoutOptions | Scripting |
DiscountFinancialDispersion | Discounts | Scripting |
EventExport | EventExportConfiguration | Scripting |
GenerateOrderlineCancellationDiscountCoupon | DiscountCoupons | Scripting |
GenerateUserDiscountCoupon | DiscountCoupons | Scripting |
OpenIDAuthorization | OpenIDProviders | Scripting |
OrderCustomStatus | OrderCustomStatus | Scripting |
OrderCustomType | OrderCustomTypes | Scripting |
OrdersMonitor | Monitors | Scripting |
UserTaskPriority | UserTasks | Scripting |
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.
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"
}
{ "Name" = "Verify orderline",
"Dialect" = "Extension",
"IsActive" = "true",
"Source" = "extend CheckOrderValidation
set validate to false
if Order.TotalAmountInTax < -75 then
set validate to true
end
if Order.IsVerified then
set validate to false
end
output validate"
}
{ "Name" = "Verify orderline",
"Dialect" = "Extension",
"IsActive" = "true",
"Source" = "extend CheckOrderValidation
if Order.Customer.FirstName = 'Pedro'
set reason to 'High Returner'
end
output reason "
}
The last example, where returns are verified and any verification reasons outputted for the employee, depends on verification reasons being set via the OrderVerificationReason
CRUD services.
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.
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"
}
{ "Name": "Generate user discount coupon",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend GenerateUserDiscountCoupon
set Generate_a_coupon to 'false'
if User.CustomFields.CatsDogs = 'Dogs'
then set Generate_a_coupon to 'true'
end
output Generate_a_coupon"
}
{
"Name" = "Generate user discount coupon",
"Dialect" = "Extension",
"IsActive" = "true",
"Source": "extend GenerateUserDiscountCoupon
// If you want the script to check if a coupon was already generated
set discountBackendID to ''
if User.CustomFields = 'ExpectedValue' then
set discountBackendID to 'DiscountABC123'
end
if ExtensionInformation.GeneratedCouponCount > 0 then
set discountBackendID to ''
end
output discountBackendID"
}
{
"Name" = "Generate user discount coupon",
"Dialect" = "Extension",
"IsActive" = "true",
"Source": "extend GenerateUserDiscountCoupon
// This will allow you to reset a usage count based on a number of days, making it available to a user again
// DaysSinceLastGenerated = DateNow - LastCouponGeneratedOn
// IsGenerated = GeneratedCouponCount != 0 or LastCouponGeneratedOn is set
set discountBackendID to ''
if (IsGenerated = false or DaysSinceLastGenerated > 4) then
set discountBackendID to '{discountBackendID}'
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.
{ "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.
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).
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)
}
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
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.
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
}
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
- StockMutation
- 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"
}
extend EventExport
set export to false
if UserAgent.Name = 'EVA-TestSuite' 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.
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"
}
{
"ID": 10008,
"HasErrors": false,
"AST": {
"Name": "GenerateOrderlineCancellationDiscountCoupon",
"RootBlock": {
"Statements": [
{
"Name": "DiscountID",
"Value": {
"Value": "",
"ExpressionType": 6,
"ValueType": "Unknown",
"NodeType": "String",
"Info": "69,70,1,69|69,70,1,69",
"ID": 1
},
"NodeType": "SetVariable",
"Info": "51,53,1,51|69,70,1,69",
"ID": 2
},
{
"LoopVariableName": "CancelledLine",
"LoopBlock": {
"Statements": [
{
"Branches": [
{
"Condition": {
"Operator": "GreaterThan",
"Left": {
"Variable": "CancelledLine.CancellationQuantity",
"ExpressionType": 4,
"ValueType": "Unknown",
"NodeType": "ReadVariable",
"Info": "124,157,1,124|124,157,1,124",
"ID": 5
},
"Right": {
"Value": 0,
"ExpressionType": 1,
"ValueType": "Unknown",
"NodeType": "Number",
"Info": "161,161,1,161|161,161,1,161",
"ID": 6
},
"ExpressionType": 0,
"ValueType": "Unknown",
"NodeType": "Comparison",
"Info": "124,157,1,124|161,161,1,161",
"ID": 7
},
"IfTrueBlock": {
"Statements": [
{
"Name": "DiscountID",
"Value": {
"Value": "coupon_cancel",
"ExpressionType": 6,
"ValueType": "Unknown",
"NodeType": "String",
"Info": "186,201,1,186|186,201,1,186",
"ID": 9
},
"NodeType": "SetVariable",
"Info": "168,170,1,168|186,201,1,186",
"ID": 10
}
],
"NodeType": "Block",
"Info": "168,170,1,168|186,201,1,186",
"ID": 8
},
"NodeType": "IfBranch",
"Info": "121,122,1,121|163,166,1,163",
"ID": 11
}
],
"NodeType": "If",
"Info": "121,122,1,121|203,205,1,203",
"ID": 12
}
],
"NodeType": "Block",
"Info": "168,170,1,168|203,205,1,203",
"ID": 4
},
"Collection": {
"Variable": "CancelledLineStates",
"ExpressionType": 4,
"ValueType": "Unknown",
"NodeType": "ReadVariable",
"Info": "98,116,1,98|98,116,1,98",
"ID": 3
},
"NodeType": "ForEach",
"Info": "72,74,1,72|207,209,1,207",
"ID": 13
},
{
"Expression": {
"Variable": "DiscountID",
"ExpressionType": 4,
"ValueType": "Unknown",
"NodeType": "ReadVariable",
"Info": "218,227,1,218|218,227,1,218",
"ID": 14
},
"NodeType": "Output",
"Info": "211,216,1,211|218,227,1,218",
"ID": 15
}
],
"NodeType": "Block",
"Info": "168,170,1,168|218,227,1,218",
"ID": 0
},
"NodeType": "ExtensionPoint",
"Info": "0,5,1,0|218,217,1,228",
"ID": 16
}
}
{
"Name": "GenerateOrderlineCancellationDiscountCoupon",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend GenerateOrderlineCancellationDiscountCoupon
set id to ''
for each Shipment in Order.Shipments do
if Shipment.ShippingMethodName = 'DHL Fast' and Shipment.ShippingMethodCode = 'DHL_F' then
set id to 'FreeShippingDiscount'
end
end
for each line in Order.Lines do
if line.IsFullyExported = false then
set id to ''
end
end
output id
}
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.
- 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"
}
{
"Name": "OrdersMonitor",
"Dialect": "Extension",
"IsActive": "true",
"Source": "Extend OrdersMonitor
set result to false
for each payment in PaymentTransactions
do if payment.IsRefund and payment.Status = 'Failed'
then set result to true
end
end
output result"
}
"Name": "OrdersMonitor",
"Dialect": "Extension",
"IsActive": "true",
"Source": "Extend OrdersMonitor
set exported to false
set paymentFailed to false
for each orderline in Order.OrderLines
do if orderline.QuantityExported > 0
then set exported to true
end
end
if exported = true
for each pt in Order.PaymentTransactions
do if pt.PaymentDate has value and pt.Status = 'Failed'
then set paymentFailed to true
end
end
end
output exported and paymentFailed"
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"
}
Context:
- Claim Type RoleCode, value ‘MNGR’
- Claim Type OUsetBackendID, value = ‘PT_Stores’
{
"Name": "OpenIDAuthorization",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend OpenIDAuthorization
for each claim in Claims do
if claim.Type = 'OUSetBackenID' then
set store to claim.Value
end
if claim.Type = 'RoleCode' then
set role to claim.Value
end
end
set sr to (store + '|' + role)
output sr"
}
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
extend OpenIDRole
table LocationMapping
| AzureLocation | EvaStoreBackendID |
| 'South' | 'POS-South' |
| 'public' | 'POS-PUBLIC' |
| 'dummy' | 'DUMMYSTORE' |
| 'Unknown' | 'UNKNOWN' |
table RoleMapping
| AzureJobTitle | EvaRole |
| 'Newblack' | 'NB' |
| 'Employee' | 'SE' |
| 'Manager' | 'OM' |
| 'Unknown' | 'UNKNOWN' |
set ouMatch to false
set roleMatch to false
for each claim in Claims do
if claim.Type = 'OfficeLocation' then
set store to (from LocationMapping where AzureLocation = claim.Value select EvaStoreBackendID)
if (store = OrganizationUnitSet.OrganizationUnit.BackendID) and store <> null then
set ouMatch to true
end
end
if claim.Type = 'JobTitle' then
set role to (from RoleMapping where AzureJobTitle = claim.Value select EvaRole)
if (role = Role.Code) and role <> null then
set roleMatch to true
end
end
end
set output to ouMatch and roleMatch
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"
}
{
"Name": "CheckoutOptions",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend CheckoutOptions
// This ensures the tile will only be shown if the order has a customer
if Order.Customer has value
then set output to true
else
set output to false
end"
}
{
"Name": "CheckoutOptions",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend CheckoutOptions
// Allows you to use the availability of a serial number as a validation
set output.Visible to Order.OrderLines.HasAny line -> line.SerialNumber <> null"
}
{
"Name": "CheckoutOptions",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend CheckoutOptions
// Displays it only if the order amount is over a certain amount
set MinAmount to 50
if Order.TotalAmountInTax > MinAmount
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
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"
}
{
"Name": "BarcodeResult",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend BarcodeResult
if Barcode = 'MODIFY_THIS_CONDITION' then
set output.Actions[1].Name to 'SHOWMESSAGE'
set output.Actions[1].Parameters[1].Name to 'Title'
set output.Actions[1].Parameters[1].Value to 'My First Title'
set output.Actions[1].Parameters[2].Name to 'Message'
set output.Actions[1].Parameters[2].Value to 'My First Message'
end"
}
{
"Name": "BarcodeResult",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend BarcodeResult
table Messages
| BarcodePrefix | Message |
| '(02)' | 'This is a message from scripting because it starts with (02)!' |
| '(03)' | 'This is another message because the barcode starts with (03)' |
set BarcodePrefixes to '(02)', '(03)'
for each prefix in BarcodePrefixes do
if Barcode.Starts with Value ($'{prefix}')
set ConfigMessage to (from Messages where BarcodePrefix = $'{prefix}' select Message)
if (ConfigMessage has value)
set output.Actions[1].Name to 'SHOWMESSAGE'
set output.Actions[1].Parameters[1].Name to 'Title'
set output.Actions[1].Parameters[1].Value to 'Custom barcode found'
set output.Actions[1].Parameters[2].Name to 'Message'
set output.Actions[1].Parameters[2].Value to ConfigMessage
end
end
end"
}
{
"Name": "BarcodeResult",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend BarcodeResult
if Barcode = 'MyCustomBarcodeToReturnAProduct' then
set output.Type to 'PRODUCT_BARCODE'
set output.TypeParameters[1].Name to 'ProductID'
set output.TypeParameters[1].Value to 1507
set output.TypeParameters[2].Name to 'TypeID'
set output.TypeParameters[2].Value to 1
set output.TypeParameters[3].Name to 'Quantity'
set output.TypeParameters[3].Value to 1
set output.TypeParameters[4].Name to 'UnitPriceInTax'
set output.TypeParameters[4].Value to 18
set output.TypeParameters[5].Name to 'Description'
set output.TypeParameters[5].Value to 'Orange woven bracelet'
end"
}
{
"Type": "PRODUCT_BARCODE",
"Value": "MyCustomBarcodeToReturnAProduct",
"Data": {
"ProductID": 1507,
"TypeID": 1,
"Quantity": 1,
"UnitPriceInTax": 18,
"Description": "Orange woven bracelet"
}
}
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 !.
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"
}
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.
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.
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"
}
{
"Name": "CustomFieldSecurity",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend CustomFieldSecurity
if (User.FirstName.Contains with Value 'Pedro') and
(User.EmailAddress.Contains with Value '@docs.org') then
set output.IsEditable to true
set output.IsRequired to false // we will override this value in the eligibility scripts
set output.IsVisible to true
end"
}
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:
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:
"Name": "CustomFieldSecurity",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend UserCustomFieldEligibility
set output.IsRequired to User.CustomFields.IsLoyaltyUser = 'true'
end"
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. Keep in mind here that for the latest overview of available variables, you can use ParseScript.
DiscountCustomFieldEligibility
{ Discount: {
BackendID: "D-3532"
StartDate: "2024-01-01",
EndDate: "2025-01-01",
Enabled: true,
LayerID: 2,
Trigger: 0
}
}
{
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
}
}
You can also use this script to allow users to skip an order (line) custom field that is otherwise required.
extend OrderCustomFieldEligibility
if Order.UserAgent has value then
set output.IsRequired to false
end
extend OrderCustomFieldEligibility
if OrderLine.IsShippingCost then
set output.IsRequired to false
end
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 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.
You can change the type at any point of the order flow, even when the order has already been completed.
{
"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"
}
{
"Name": "OrderCustomType",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend OrderCustomType
set CustomType to ''
if Order.SoldFromOrganizationUnit.Company.ID = 63 then
set CustomType to 'IntercompanyPurchaseOrder'
else if Order.SoldFromOrganizationUnit.Company.ID is not Order.SoldToOrganizationUnit.Company.ID
set CustomType to 'Stock Transport Purchase Order'
end
output CustomType"
}
{
"Name": "OrderCustomType",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend OrderCustomType
set CustomType to ''
if Order.IsSalesOrder and Order.IsPaid and Order.SoldFromOrganizationUnit.IsWebShop and Order.Lines.HasAny line -> line.IsReserveLine then
set type to 'EcommercePickupFromStore'
end
output CustomType"
}
{
"Name": "OrderCustomType",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend OrderCustomType
set CustomType to ''
if (Order.Lines.HasAny line -> line.OriginalOrder.SoldFromOrganizationUnit.Type = 'Shop') and Order.SoldFromOrganizationUnit.Type = 'Webshop' then
set CustomType to 'Online order, Store Return'
end
output CustomType"
}
{
"Name": "OrderCustomType",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend OrderCustomType
if Order.Lines[1].FulfillmentOrganizationUnit.BackendID = 'Almere' then
set type to 'FulfilledByAlmereQuickDelivery'
end
output type"
}
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"
}
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. You can change the status at any point of the order flow, even when the order has already been completed.
Its variables include among others, InTax, 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
{
"Name": "OrderCustomStatus",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend OrderCustomStatus
# A script to show that this extensionpoint has access to:
# - Amounts
# - Shipped status of lines
# - Payments status
if Order.IsCompleted = false and Order.InTax = 123.45 then
for each payment in Order.Payments do
if payment.Status <> 'Confirmed' then
set status to 'VerifiedByQA'
end
end
end
output status"
}
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"
}
{
# if the TotalAmountInTax on the Order < 50 then no products need to be returned; if it's between 50 and 100 it's up to the employee; if it's > 100 then all products need to be returned
"Name": "ReturnableStatus",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend ReturnableStatus
if Order.TotalAmountInTax <= 50
set output.ReturnWithoutProducts to 1
end
if Order.TotalAmountInTax > 100
set output.ReturnWithoutProducts to 2
end"
}
{
# if the returning order has a specific (low) value, then no products need to be returned
"Name": "ReturnableStatus",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend ReturnableStatus
if ReturningOrder has value and ReturningOrder.TotalAmountInTax <= 10.00
set output.ReturnWithoutProducts to 1
end"
}
{
# if the returning order consists of a single item, no products need to be returned
"Name": "ReturnableStatus",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend ReturnableStatus
if ReturningOrder has value and ReturningOrderLine.ReturningQuantity = 1
set output.ReturnWithoutProducts to 1
end"
}
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"
}
{
"Name": "AllowOrderLineModificationDuringFulfillment",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend AllowOrderLineModificationDuringFulfillment
# For SFS the ExternalSystem = 'SHIP_FROM_STORE'
if ExternalSystem <> 'SHIP_FROM_STORE' then
output true
end"
}
{
"Name": "AllowOrderLineModificationDuringFulfillment",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend AllowOrderLineModificationDuringFulfillment
# Allow changing if there's a PICKED ledger
if Order.HasLedgerEntries with Type 'PICKED' then
output true
end"
}
{
"Name": "AllowOrderLineModificationDuringFulfillment",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend AllowOrderLineModificationDuringFulfillment
# Or alternatively only allow changing when there's a ledger of type 'ACCEPTED'
if (Order.HasLedgerEntries with Type 'ACCEPTED') = true then
output true
end"
}
{
"Name": "AllowOrderLineModificationDuringFulfillment",
"Dialect": "Extension",
"IsActive": "true",
"Source": "extend AllowOrderLineModificationDuringFulfillment
# Or allow changing, but only if the reason is changing the shipping address AND it's being fulfilled by a store
if Reason = 'ChangeShippingAddress' and FulfillmentOrganizationUnit.Type = 'Shop' 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.
{
"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.
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.
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.
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
Here is a sample that uses the CountryID to restrict shipments to a specified country. Mind that the CountryID property expects a country code in ISO 3166-2 standard.
extend ShippingRestriction
if ShippingAddress.CountryID = 'NL' then
output false
end
output true
The following sample restricts shipments to a specified address.
extend ShippingRestriction
if ShippingAddress.Address1 = 'Alleyway' then
output false
end
output true
By altering shipping restrictions via the corresponding CRUD services, you can decide to make the script run second. To do so you can set the property CheckScriptOnly to false, in which case the regular OU/Country/Product filter will be applied before running the script.
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 for sales orders, while keeping the requirements intact for returns (< 0 QuantityOrdered) and Repairs / CaseRelatedItem / UserBroughtProducts.
extend SerialNumberValidation
if OrderLine.QuantityOrdered > 0 then
set output.SkipValidation to true
set output.SkipVerification to true
else then
set output.SkipValidation to false
set output.SkipVerification to false
end
Even the when the script's outcome is to skip verification, please note that it’s still required to input the serial number itself (as based on the RequireSerialNumberForSale property, which can be set when importing the product). The script outcome will simply remove the need to validate and/or externally verify the input.
Generating coupons for returns
The following extension point will trigger when return orders are paid/refunded and consequently generate coupons: GenerateUserCouponOnOrderReturn.
The expected output of the script is a discount BackendID that will be used to generate a coupon. The discount is required to be of trigger Coupon(2) and have CouponsGeneratedByOtherDiscount: true.
extend GenerateUserCouponOnOrderReturn
set discountBackendID to ''
if Order.TotalAmountInTax < 0 and Order.TotalAmountInTax > -10 then
set discountBackendID to 'discount-coupons-from-return-script'
end
output discountBackendID
Generating coupons for cancelled repairs
To be able to offer customers coupons for repairs that are cancelled, for example because they turn out to be too costly, you can use the GenerateRepairCancellationDiscountCoupon extension point.
The script returns the BackendID of a discount that should be active in the organization unit of the repair with Coupon as its trigger. The coupon value itself should be customized by means of repair custom fields, which can then be selected in the flow.
A generated discount is added as CouponCode to the RepairCancelled email stencil template.
extend GenerateRepairCancellationDiscountCoupon
output 'RepairCancellationCoupon200'
if Repair.CustomFields.CancellationCoupon = '200,-'
then output 'RepairCancellationCoupon200'
else if Repair.CustomFields.CancellationCoupon = '300,-'
then output 'RepairCancellationCoupon300'
else if Repair.CustomFields.CancellationCoupon = '400,-'
then output 'RepairCancellationCoupon400'
else if Repair.CustomFields.CancellationCoupon = '500,-'
then output 'RepairCancellationCoupon500'
else if Repair.CustomFields.CancellationCoupon = '100%'
then output 'RepairCancellationCoupon100%'
end
Mapping Vertex Properties to EVA Data
Extension point name: VertexCalculationRequest
This extension point is only for use with a Vertex integration.
When configured and active, this extension point processes any Vertex request through a script. The script maps specified EVA data to corresponding Vertex properties. This mapping addresses any field naming discrepancies, ensuring accurate tax return processing across both EVA and Vertex.
{
extend VertexCalculationRequest
if Order has value then
###### possible properties for mapping #######
# output.LocationCode -> To more precisely define the location of the OU
# output.DocumentNumber -> Can be used to override the DocumentNumber (not advised, the default DocumentNumber is ok)
#######
# set the locationcode (in this example based on a custom field)
set output.LocationCode to Order.OriginatingOrganizationUnit.CustomFields.GetField('LocationCode')
set i to 1
for each ol in Order.OrderLines do
###### possible properties for mapping #######
# output.LineItem[i].LineItemID -> to define the orderlineID
# output.LineItem[i].LineItemNumber -> to define the line number (consequetive)
# output.LineItem[i].CustomerCodeValue -> can be used to define a customer identifier such as the backendID
# output.LineItem[i].ProductValue -> can be used to define a product identifier such as the backendID
# output.LineItem[i].FlexibleCode1 / FlexibleCode9 -> can be used to define any Vertex flexible field
#######
#set the orderline ID
set output.LineItem[i].LineItemID to ol.ID
set output.LineItem[i].LineItemNumber to i
# set the product ID
set output.LineItem[i].ProductValue to ol.Product.BackendID
# set the customer number
set output.LineItem[i].CustomerCodeValue to Order.Customer.BackendID
# Some example use cases of the Vertex Fields
# FlexibleNumeric1 -> Orderline unit price
# FlexibleCode2 -> Product name
# FlexibleCode3 -> TaxExemption Certificate Number
# FlexibleCode5 -> Set to value 'B2B' if it's a B2B order
# FlexibleCode7 -> The type of order, using custom order types
set output.LineItem[i].FlexibleNumeric1 to ol.UnitPrice
set output.LineItem[i].FlexibleCode2 to ol.Product.PrimitiveName
set output.LineItem[i].FlexibleCode3 to ol.TaxExemptionVerification
if (Order.Customer.HasCompany)
set output.LineItem[i].FlexibleCode5 to 'B2B'
end
if Order.OrderCustomType has value
set output.LineItem[i].FlexibleCode7 to Order.OrderCustomType
end
set i to i + 1
end
end
}
SignOrderRequirement
The following extension point allows you to create a script that will turn a SignOrder checkout option into a requirement.
This means that a signature will become mandatory before being able to proceed to checkout and perform payment.
extend SignOrderRequirement
if(Order.HasCarryOut and Order.HasLoyaltyProgram) then
output true
end
output false
CheckOrderValidation
The following extension point allows you to create a script that triggers an order requirement where eployees need to manually verify the order before being able to proceed.
Example script without a validation reason:
extend CheckOrderValidation
set validate to false
if Order.TotalAmountInTax < 10 then
set validate to true
end
output validate
Example script which returns an Order Verification Reason:
extend CheckOrderValidation
if Order.TotalAmountInTax > 10 and Order.TotalAmountInTax <= 20 then
output 'reason name'
else if Order.TotalAmountInTax > 20
output 'other reason name'
end
output null