Field definition validation¶
General information¶
Product definition have several properties which contain lists of fields which have defined validation rules in specific object scope.
- customerFields - validation rules for properties defined in
order.customer
object. - itemFields - validation rules for properties defined in
order.orderedItem[i]
object (i - index of the item in the orderedItem array) - travelerFields - validation rules for properties defined in
order.orderedItem[i].orderedItem.traveler[j]
object - vehicleFields - validation rules for properties defined in
order.orderedItem[i].orderedItem.vehicle[j]
object
These lists of fields contain definitions which help the developers to validate required information. This varies depending on the product and the product providers. To order a T-Shirt not the same properties are required as to by a train ticket.
Info
On the backend we use these definitions to validate the data dynamically. For best user experience you do this already on the client.
Field definition should be used to build dynamic UI which collect necessary information for ordering different products.
Important
It is important to remember that there could be several field definitions with the same propertyId
in the same list of fields. That doesn't mean that property has to be rendered twice, but different validation rules applied on this property.
Concepts¶
Fields types¶
Here is the list of supported field types:
integer
orint
number
text
media
medialist
select
radio
date
datetime
duration
multiselect
bool
Parent property validation¶
When field definition has parentFieldPropertyId
defined, then validation rule for this field will be applied only if parent field validation is passed.
Parent field validation is defined using next properties:
parentFieldPropertyId
- propertyId of the parent fieldparentFieldOperator
- operator to use for parent field validation, possible values:notnullorempty
,nullorempty
,notequal
,equal
(default)parentFieldValue
- value to compare parent field value with when operator is not defined
Readonly¶
When field definition has readonly
property defined with value true
, then this field will not be modified (new value will be ignored)
during any edit operation.
Required¶
When field definition has required
property defined with value true
, then this field must be present when object is created or modified.
Value explanations:
null
- field is not relevant for this producttrue
- field is requiredfalse
- field is not required, but can be provided and used
Required for offers¶
Properties which have requiredForOffers
property defined with value different from null
will be used to validate get offers
request.
Value explanations:
null
- field is not used for offertrue
- field is required for offerfalse
- field is not required for offer, but can be provided and used in offer requests
Range validation¶
When field definition has rangeMin
and rangeMax
properties defined, then value provided for this field must be in the defined range.
This type of validation works with the following type of fields:
integer
orint
- numbernumber
- numberdate
- duration by ISO 8601datetime
- duration by ISO 8601duration
- duration by ISO 8601
For the fields with type date
and datetime
range is calculated based on property defined in rangeBasePropertyId
.
If rangeBasePropertyId
is not defined then datetime.now
is used as base value.
Sales cut off validation¶
When field definition has type=date
and additionalType=sales-cut-off
defined then special validation rules are applied..
Such field definition defines until when the product can be sold.
Field definition must have rangeMin
property defined with value in ISO 8601 duration format.
This range defines negative offset which will subtracted from datetime.now
and compared with value provided for this field.
Sales cut off validation will properly work only with the following properties:
dateFrom
- in get offers requestvalidFrom
- in create order item request
Calculation explanation:
When (requestedDate - (datetime.now + duration(rangeMin))).totalSeconds < 0)
then product cannot be sold anymore.
requestedDate
- contains date only value (e.g.2025-10-09T00:00:00
)datetime.now
- current date and time in UTC (e.g.2025-10-09T15:00:00Z
)duration(rangeMin)
- duration parsed fromrangeMin
property (e.g.-PT16H
means negative 16 hours)
Example
Next field definition means that sales will be closed at 16:00 swiss time today. So offer cannot be received for today after 16:00 swiss time. But it is still possible to receive offer for tomorrow.
{
"propertyId": "dateFrom",
"type": "date",
"additionalType": "sales-cut-off",
"rangeMin": "-P16H",
"requiredForOffers": true
}
Example 2
Next field definition means that sales will be closed at 10:30 swiss time today. So offer cannot be received for the same day after 16:00 swiss time.
{
"propertyId": "validFrom",
"type": "date",
"additionalType": "sales-cut-off",
"rangeMin": "-P10H30M"
}
Multiselect and checksum fields¶
Multiselect¶
Multiselect fields supports selecting of multiple answers. To do that it is necessary to provide serialized json array as value of property specified in the request.
{
"propertyId": "additionalProperty.question0",
"type": "multiselect",
"name": "How many people would you prefer to see in you travel group?",
"required": true,
"possibleValue": {
"answer0": "Form 2 to 5",
"answer1": "From 6 to 10",
"answer2": "More than",
"answer3": "Less than"
}
},
{
"propertyId": "additionalProperty.question0_answer2_numeric",
"type": "integer",
"name": "More than",
"required": false,
"possibleValue": {},
"rangeMin": "0",
"rangeMax": "2147483647",
"parentFieldPropertyId": "additionalProperty.question0",
"parentFieldValue": "answer2"
},
{
"propertyId": "additionalProperty.question0_answer3_numeric",
"type": "integer",
"name": "Less than",
"required": false,
"possibleValue": {},
"rangeMin": "0",
"rangeMax": "2147483647",
"parentFieldPropertyId": "additionalProperty.question0",
"parentFieldValue": "answer3"
}
{
"additionalProperty": [
{
"propertyId": "additionalProperty.question1",
"value": "[\"answer0\", \"answer2\, \"answer3\"]"
},
{
"propertyId": "additionalProperty.question0_answer2_numeric",
"value": "25"
},
{
"propertyId": "additionalProperty.question0_answer3_numeric",
"value": "30"
},
]
}
Multiselect field definitions could have child related field definitions which has specified parentFieldPropertyId
and parentPropertyValue
Information
It's not possible to specify multiple values in the parentPropertyValue
. So specifying of an array (e.g. parentPropertyValue
= "[\"answer0\", \"answer1\"]") will not have any affect. Only single values are allowed.
Checksum¶
Checksum is a multiselect field which has child fields with type checksumItem
related to its answer values.
Whenever value of checksum multiselect is selected then the field with type checksumItem
, with parentFieldPropertyId
equal to propertyId
of checksum field and with parentPropertyValue
equal to selected value will be taken into account for future calculation.
Checksum considered as valid only when the sum of the values of checksum items (which are related to the selected values of checksum) will be in between of RangeMin
and RangeMax
specified in checksum field.
Checksum item¶
It behaves as an int field and it is used in conjunction with checksum
field. The main idea is to provide user to type some number for the selected value of checksum
field.
Example of checksum items:
[
{
"type": "checksum",
"propertyId": "additionalProperty.checksum_example",
"name": "Where have you been?",
"possibleValue": {
"ukraine": "Ukraine",
"switzerland": "Switzerland",
"italy": "Italy"
},
"rangeMin": "3",
"rangeMax": "6",
"required": true
},
{
"type": "checksumItem",
"propertyId": "additionalProperty.checksum_item_ukraine",
"parentFieldPropertyId": "additionalProperty.checksum_example",
"parentFieldValue": "ukraine",
"name": "How many time you've been to Ukraine?",
"rangeMin": "0",
"rangeMax": "6",
"required": false
},
{
"type": "checksumItem",
"propertyId": "additionalProperty.checksum_item_switzerland",
"parentFieldPropertyId": "additionalProperty.checksum_example",
"parentFieldValue": "switzerland",
"name": "How many time you've been to Switzerland?",
"rangeMin": "0",
"rangeMax": "6",
"required": false
},
{
"type": "checksumItem",
"propertyId": "additionalProperty.checksum_item_italy",
"parentFieldPropertyId": "additionalProperty.checksum_example",
"parentFieldValue": "italy",
"name": "How many time you've been to Italy?",
"rangeMin": "0",
"rangeMax": "6",
"required": false
}
]
// next example has total sum of selected items euqal to 4 which is in the range [3..6]
{
"additionalProperty": [
{
"propertyId": "additionalProperty.checksum_example",
"value": "[\"italy\", \"ukraine\"]"
},
{
"propertyId": "additionalProperty.checksum_item_ukraine",
"value": 3
},
{ // this value will be ignored because it was not selected
"propertyId": "additionalProperty.checksum_item_switzerland",
"value": 6
},
{
"propertyId": "additionalProperty.checksum_item_italy",
"value": 1
}
]
}
// the same as previous one
{
"additionalProperty": [
{
"propertyId": "additionalProperty.checksum_example",
"value": "[\"italy\", \"ukraine\"]"
},
{
"propertyId": "additionalProperty.checksum_item_ukraine",
"value": 3
},
{
"propertyId": "additionalProperty.checksum_item_italy",
"value": 1
}
]
}
// next example has total sum of selected items euqal to 6 which is in the range [3..6]
{
"additionalProperty": [
{
"propertyId": "additionalProperty.checksum_example",
"value": "[\"switzerland\"]"
},
{
"propertyId": "additionalProperty.checksum_item_ukraine",
"value": 3
},
{ // only this value will be taken into calculation
"propertyId": "additionalProperty.checksum_item_switzerland",
"value": 6
},
{
"propertyId": "additionalProperty.checksum_item_italy",
"value": 1
}
]
}
```
=== "Not valid order item requests"
``` json
// next example has total sum of selected items euqal to 10 which is not in the range [3..6]
{
"additionalProperty": [
{
"propertyId": "additionalProperty.checksum_example",
"value": "[\"italy\",\"ukraine\", \"switzerland\"]"
},
{
"propertyId": "additionalProperty.checksum_item_ukraine",
"value": 3
},
{ // this value will be ignored because it was not selected
"propertyId": "additionalProperty.checksum_item_switzerland",
"value": 6
},
{
"propertyId": "additionalProperty.checksum_item_italy",
"value": 1
}
]
}
// next example has total sum of selected items euqal to 1 which is not in the range [3..6]
{
"additionalProperty": [
{
"propertyId": "additionalProperty.checksum_example",
"value": "[\"italy\"]"
},
{
"propertyId": "additionalProperty.checksum_item_italy",
"value": 1
},
]
}
Examples¶
Zürich Card¶
For Zürich Card 72 hours, the ItemField and TravelerField are defined as the following:
"ItemField": [
{
"PropertyId": "orderedItem.validFrom",
"Type": "dateTime",
"Name": "Valid from",
"Required": true,
"RequiredForOffers": true,
"PossibleValue": {},
"RangeMin": "PT1S", // one second in advance
"RangeMax": "P2M" // less than 2 months
}
],
"TravelerField": [
{
"PropertyId": "givenName",
"Type": "text",
"Name": "First name",
"Required": true,
"RequiredForOffers": false,
"PossibleValue": {}
},
{
"PropertyId": "familyName",
"Type": "text",
"Name": "Last name",
"Required": true,
"RequiredForOffers": false,
"PossibleValue": {}
},
{
"PropertyId": "birthDate",
"Type": "date",
"Name": "Date of birth",
"Required": true,
"RequiredForOffers": true,
"PossibleValue": {},
"RangeMax": "-P6Y",
"RangeBasePropertyId": "orderedItem.validFrom"
},
{
"PropertyId": "gender",
"Type": "radio",
"Name": "Gender",
"Required": false,
"RequiredForOffers": false,
"PossibleValue": {
"female": "Female",
"male": "Male",
"diverse": "Diverse"
}
}
]
Aleno multiselect/checksum with numeric fields¶
{
"propertyId": "additionalProperty.question1",
"type": "checksum",
"name": "Question with sum check",
"required": true,
"possibleValue": {
"answer0": "Salads",
"answer1": "Soups"
},
"rangeMin": "3",
"rangeMax": "3"
},
{
"propertyId": "additionalProperty.question1_answer0_numeric",
"type": "checksumItem",
"name": "Salads",
"required": true,
"possibleValue": {},
"rangeMin": "0",
"rangeMax": "3",
"parentFieldPropertyId": "additionalProperty.question1",
"parentFieldValue": "answer0"
},
{
"propertyId": "additionalProperty.question1_answer1_numeric",
"type": "checksumItem",
"name": "Soups",
"required": true,
"possibleValue": {},
"rangeMin": "0",
"rangeMax": "3",
"parentFieldPropertyId": "additionalProperty.question1",
"parentFieldValue": "answer1"
}
{
"propertyId": "additionalProperty.question0",
"type": "multiselect",
"name": "Select Question",
"required": true,
"possibleValue": {
"answer0": "How many Apéros you want? (Numeric)",
"answer1": "Option en 1",
"answer2": "Option en 2"
}
},
{
"propertyId": "additionalProperty.question0_answer0_numeric",
"type": "integer",
"name": "How many Apéros you want? (Numeric)",
"required": true,
"possibleValue": {},
"rangeMin": "0",
"rangeMax": "2147483647",
"parentFieldPropertyId": "additionalProperty.question0",
"parentFieldValue": "answer0"
}
As an example of representation of checksum with checksumitems and multiselect below are screenshots from Aleno widget which covers same logic:
Checksum with checksumitems(numeric fields)
Multiselect with additional numeric fields attached to it by values
Warning
Be aware that discover.swiss currently doesn't support multiselect fields with posiibility to add custom answer unlike Aleno widget.
Example Dynamic validation¶
This example shows how the automatic validation works on ItemField. In the case of Zürich Card, a customer can make a ticket as it is less than 2 months in the future. If it is more than the defined RangeMax
or less than RangeMin
, it will result in 400 Bad Request
and gives a validation error
Info
Assuming today is February the first.
{
"orderStatus": "Placed",
"priceCurrency": "CHF",
"orderedItem": [
{
"orderQuantity": 1,
"orderedItem": {
"product": {
"identifier": "nova_zurichcard24"
},
"validFrom": "2022-05-03T00:00:00"
}
}
],
}
POST {marketUrl}/orders
{
//not all response is shown
"validationMessages": [
{
"level": "Error",
"message": "The field Valid from must be less than 01/05/2022 15:17:03 +00:00.",
"orderItemNumber": "22-103915-1",
"source": "OrderedItem"
}
]
}
Example validation different options¶
This example shows how these properties parentFieldPropertyId
and parent Field Value
should work.
{
"PropertyId": "deliveryMode",
"Type": "select",
"Name": "delivery Mode",
"Required": true,
"RequiredForOffers": false,
"PossibleValue": {
"shipping": "Shipping",
"pickUp": "Pick-up"
}
},
{
"PropertyId": "shippingMethod",
"Type": "select",
"Name": "shipping Method",
"Required": true,
"RequiredForOffers": false,
"PossibleValue": {
"train": "Train",
"plane": "Plane"
},
"parentFieldPropertyId": "deliveryMode",
"parentFieldValue": "shipping"
},
{
"PropertyId": "pickupMethod",
"Type": "select",
"Name": "pickup Method",
"Required": true,
"RequiredForOffers": false,
"PossibleValue": {
"station1": "Station 1",
"station2": "Station 2"
},
"parentFieldPropertyId": "deliveryMode",
"parentFieldValue": "pickUp"
},
Example special additionalProperty¶
If the propertyId is prefixed with additionalProperty
. Then it is an additional property with the id of the full value of propertyId
.
Example
{
"propertyId": "additionalProperty.info1",
"type": "text",
"name": "swim experience",
"required": true
},
{
"additionalProperty": [
{
"PropertyId": "additionalProperty.info1",
"Value": "good swimmer"
},
]
}
Info
rangeBasePropertyId: can be applied for any dateTime. e.g., it concerns the validation of the traveler's age at the travel date.
Note
There are conditions to be fulfilled. For Gender and Salutation there are predefined values for PossibleValue property. For BirthDate the value should be within the age range.
Warning
In some cases itemFields can change after the first step in the checkout page because they can be dependent on the users choices.