Skip to content

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 or int
  • 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 field
  • parentFieldOperator - 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 product
  • true - field is required
  • false - 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 offer
  • true - field is required for offer
  • false - 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:

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 request
  • validFrom - 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 from rangeMin 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)

Aleno - Checksum with checksumItems Aleno - Checksum with checksumItems

Multiselect with additional numeric fields attached to it by values

Aleno - Multiselect with numeric

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.