Refactor layerInfo to communicate what differentiates underlying variants, instead of being defined on every variant itself

This commit is contained in:
Max Martens 2025-09-30 18:04:04 +02:00
parent 67e459d1b0
commit d1b83d19b5
3 changed files with 128 additions and 353 deletions

View File

@ -421,12 +421,20 @@ paths:
All details (that the calling touchpoint is allowed to see) for the 20% Discount product.\ All details (that the calling touchpoint is allowed to see) for the 20% Discount product.\
Even though this product has sellingPeriods for multiple touchpoints (3 and 4), only the currently active sellingPeriod and price for touchpointId 4 are returned. Even though this product has sellingPeriods for multiple touchpoints (3 and 4), only the currently active sellingPeriod and price for touchpointId 4 are returned.
This product has two `productVariants`: a single month variant and a subscription variant. This product has two `productVariants`: a single month variant and a subscription variant.\
The top-level parent contains `LayerInfo` to communicate what differentiates
the underlying product-variants.\
When no `LayerInfo` is present, the touchpoint can conclude that the product is a final fulfillable product.
value: value:
{ {
"productId": 126, "productId": 126,
"parentProductId": null, "parentProductId": null,
"layerInfo": null, "layerInfo": {
"layerInfoId": 1,
"choiceKey": "isRenewable",
"choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "30001", "gboPackageTemplateId": "30001",
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -529,12 +537,7 @@ paths:
{ {
"productId": 119, "productId": 119,
"parentProductId": 126, "parentProductId": 126,
"layerInfo": { "layerInfo": null,
"layerInfoId": 1,
"choiceKey": "isRenewable",
"choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "30001", "gboPackageTemplateId": "30001",
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -649,12 +652,7 @@ paths:
{ {
"productId": 120, "productId": 120,
"parentProductId": 126, "parentProductId": 126,
"layerInfo": { "layerInfo": null,
"layerInfoId": 1,
"choiceKey": "isRenewable",
"choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "30001", "gboPackageTemplateId": "30001",
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -774,12 +772,19 @@ paths:
All details (that the calling touchpoint is allowed to see) for the parent Regio Vrij product All details (that the calling touchpoint is allowed to see) for the parent Regio Vrij product
and (7 out of 84 of) its productVariants; the full tree would be too huge to be useful as an example. and (7 out of 84 of) its productVariants; the full tree would be too huge to be useful as an example.
The full depth of the tree is included in the example for the HL62 Reduced Fare Variant. The full depth of the tree is included in the example for the HL62 Reduced Fare Variant.\
Each non-leaf-node product contains `LayerInfo` to communicate what differentiates the underlying product-variants.\
When no `LayerInfo` is present, the touchpoint can conclude that the product is a final fulfillable product.
value: value:
{ {
"productId": 49, "productId": 49,
"parentProductId": null, "parentProductId": null,
"layerInfo": null, "layerInfo": {
"layerInfoId": 2,
"choiceKey": "regio",
"choiceLabel": "Kies de gewenste regio",
"isCustomChoice": true,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": null, "gboPackageTemplateId": null,
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -922,10 +927,10 @@ paths:
"productId": 109, "productId": 109,
"parentProductId": 49, "parentProductId": 49,
"layerInfo": { "layerInfo": {
"layerInfoId": 2, "layerInfoId": 3,
"choiceKey": "regio", "choiceKey": "allowedGboAgeProfiles",
"choiceLabel": "Kies de gewenste regio", "choiceLabel": "Wat is uw geboortedatum?",
"isCustomChoice": true, "isCustomChoice": false,
}, },
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "33615", "gboPackageTemplateId": "33615",
@ -1072,9 +1077,9 @@ paths:
"productId": 114, "productId": 114,
"parentProductId": 109, "parentProductId": 109,
"layerInfo": { "layerInfo": {
"layerInfoId": 3, "layerInfoId": 1,
"choiceKey": "allowedGboAgeProfiles", "choiceKey": "isRenewable",
"choiceLabel": "Wat is uw geboortedatum?", "choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false, "isCustomChoice": false,
}, },
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
@ -1222,9 +1227,9 @@ paths:
"productId": 115, "productId": 115,
"parentProductId": 109, "parentProductId": 109,
"layerInfo": { "layerInfo": {
"layerInfoId": 3, "layerInfoId": 1,
"choiceKey": "allowedGboAgeProfiles", "choiceKey": "isRenewable",
"choiceLabel": "Wat is uw geboortedatum?", "choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false, "isCustomChoice": false,
}, },
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
@ -1372,12 +1377,7 @@ paths:
{ {
"productId": 116, "productId": 116,
"parentProductId": 115, "parentProductId": 115,
"layerInfo": { "layerInfo": null,
"layerInfoId": 1,
"choiceKey": "isRenewable",
"choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "33615", "gboPackageTemplateId": "33615",
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -1526,12 +1526,7 @@ paths:
{ {
"productId": 117, "productId": 117,
"parentProductId": 115, "parentProductId": 115,
"layerInfo": { "layerInfo": null,
"layerInfoId": 1,
"choiceKey": "isRenewable",
"choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "33615", "gboPackageTemplateId": "33615",
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -1685,10 +1680,10 @@ paths:
"productId": 112, "productId": 112,
"parentProductId": 49, "parentProductId": 49,
"layerInfo": { "layerInfo": {
"layerInfoId": 2, "layerInfoId": 3,
"choiceKey": "regio", "choiceKey": "allowedGboAgeProfiles",
"choiceLabel": "Kies de gewenste regio", "choiceLabel": "Wat is uw geboortedatum?",
"isCustomChoice": true, "isCustomChoice": false,
}, },
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "33618", "gboPackageTemplateId": "33618",
@ -1858,10 +1853,11 @@ components:
description: >- description: >-
Gives information on the choice that the customer has to make, to enable the touchpoint Gives information on the choice that the customer has to make, to enable the touchpoint
to proceed further "down the product-tree" by selecting (PATCH-ing) the correct product-variant. to proceed further "down the product-tree" by selecting (PATCH-ing) the correct product-variant.
LayerInfo is not a mandatory product-attribute, but it should always be present on all product for which LayerInfo is not a mandatory product-attribute, but it should always be present on all products for which
`parentProductId != null`. (LayerInfo makes no sense for top-level parents as there is always a single starting point). \ there are underlying products, i.e. for which `GET /products?parentProductId=...` returns a non-empty list.
When no LayerInfo is present, the touchpoint can conclude that the product is a final fulfillable product. \
**PMT should ensure that all products in the same "product-branch" (same `parentProductId`) have the same `layerInfoId` **PMT should ensure that all non-leaf-node products (i.e. products that have underlying products) have a `layerInfoId`
referenced. If a product is found to be in violation of this rule, its attribtue `isValid` should be set to `false`.** referenced. If a product is found to be in violation of this rule, its attribtue `isValid` should be set to `false`.**
required: required:
- layerInfoId - layerInfoId
@ -1877,12 +1873,12 @@ components:
type: string type: string
description: >- description: >-
Contains the JSON Key of the product-attribute that the customer has to make some Contains the JSON Key of the product-attribute that the customer has to make some
choice on (determined by PMT), so that te correct product-variant can be selected by the touchpoint. For some choice on (determined by PMT), so that te correct product-variant (one of the direct child-products) can be
decisions (like region), there is no product attribute, and thus `isCustomChoice` will be set selected by the touchpoint. For some decisions (like region), there is no product attribute, and thus
to `true`, and `choiceKey` can then be set to any string on which touchpoints can also trigger `isCustomChoice` will be set to `true`, and `choiceKey` can then be set to any string on which touchpoints
behaviour if desired (think "region picker tool"). Therefore, reuse of choiceKeys should be can also trigger behaviour if desired (think "region picker tool"). Therefore, reuse of `choiceKeys` should be
the goal, so touchoints can keep their triggers simple and prevent duplication of the goal, so touchoints can keep their triggers simple and prevent duplication of
similar choiceKeys to trigger the same behaviour. similar `choiceKeys` to trigger the same behaviour.
choiceLabel: choiceLabel:
example: Kies voor een doorlopend abonnement of een enkele termijn example: Kies voor een doorlopend abonnement of een enkele termijn
type: string type: string
@ -1894,12 +1890,10 @@ components:
example: false example: false
type: boolean type: boolean
description: >- description: >-
Indicates if the choice is a custom choice. If `false`, the PMT should fill `choiceKey` with the Indicates if the choice is a custom choice. If `false`, the PMT should validate that the `choiceKey` is a
"differing attribute for this product-layer" and the user should not be able to override this. When differentiating attribute for the underlying product-variants. When the attribute denoted by the `choiceKey` is
no single attribute can be pinpointed by PMT, the product will become invalid (`isValid == false`) until either the same for all underlying variants, PMT validation will fail and the product will become invalid (`isValid == false`)
a situation with a single differing attribute is created, or if `isCustomChoice` is set to `true` - this would, until either the underlying products are updated, or a `LayerInfo` with `isCustomChoice == true` is configured.
however, also mean that touchpoints should be notified of this, especially if the configured LayerInfo contains
a new, not previously used, `choiceKey`.
GboAgeProfileResponse: GboAgeProfileResponse:
type: object type: object
required: required:

View File

@ -421,12 +421,20 @@ paths:
All details (that the calling touchpoint is allowed to see) for the 20% Discount product.\ All details (that the calling touchpoint is allowed to see) for the 20% Discount product.\
Even though this product has sellingPeriods for multiple touchpoints (3 and 4), only the currently active sellingPeriod and price for touchpointId 4 are returned. Even though this product has sellingPeriods for multiple touchpoints (3 and 4), only the currently active sellingPeriod and price for touchpointId 4 are returned.
This product has two `productVariants`: a single month variant and a subscription variant. This product has two `productVariants`: a single month variant and a subscription variant.\
The top-level parent contains `LayerInfo` to communicate what differentiates
the underlying product-variants.\
When no `LayerInfo` is present, the touchpoint can conclude that the product is a final fulfillable product.
value: value:
{ {
"productId": 126, "productId": 126,
"parentProductId": null, "parentProductId": null,
"layerInfo": null, "layerInfo": {
"layerInfoId": 1,
"choiceKey": "isRenewable",
"choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "30001", "gboPackageTemplateId": "30001",
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -529,12 +537,7 @@ paths:
{ {
"productId": 119, "productId": 119,
"parentProductId": 126, "parentProductId": 126,
"layerInfo": { "layerInfo": null,
"layerInfoId": 1,
"choiceKey": "isRenewable",
"choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "30001", "gboPackageTemplateId": "30001",
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -649,12 +652,7 @@ paths:
{ {
"productId": 120, "productId": 120,
"parentProductId": 126, "parentProductId": 126,
"layerInfo": { "layerInfo": null,
"layerInfoId": 1,
"choiceKey": "isRenewable",
"choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "30001", "gboPackageTemplateId": "30001",
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -774,12 +772,19 @@ paths:
All details (that the calling touchpoint is allowed to see) for the parent Regio Vrij product All details (that the calling touchpoint is allowed to see) for the parent Regio Vrij product
and (7 out of 84 of) its productVariants; the full tree would be too huge to be useful as an example. and (7 out of 84 of) its productVariants; the full tree would be too huge to be useful as an example.
The full depth of the tree is included in the example for the HL62 Reduced Fare Variant. The full depth of the tree is included in the example for the HL62 Reduced Fare Variant.\
Each non-leaf-node product contains `LayerInfo` to communicate what differentiates the underlying product-variants.\
When no `LayerInfo` is present, the touchpoint can conclude that the product is a final fulfillable product.
value: value:
{ {
"productId": 49, "productId": 49,
"parentProductId": null, "parentProductId": null,
"layerInfo": null, "layerInfo": {
"layerInfoId": 2,
"choiceKey": "regio",
"choiceLabel": "Kies de gewenste regio",
"isCustomChoice": true,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": null, "gboPackageTemplateId": null,
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -922,10 +927,10 @@ paths:
"productId": 109, "productId": 109,
"parentProductId": 49, "parentProductId": 49,
"layerInfo": { "layerInfo": {
"layerInfoId": 2, "layerInfoId": 3,
"choiceKey": "regio", "choiceKey": "allowedGboAgeProfiles",
"choiceLabel": "Kies de gewenste regio", "choiceLabel": "Wat is uw geboortedatum?",
"isCustomChoice": true, "isCustomChoice": false,
}, },
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "33615", "gboPackageTemplateId": "33615",
@ -1072,9 +1077,9 @@ paths:
"productId": 114, "productId": 114,
"parentProductId": 109, "parentProductId": 109,
"layerInfo": { "layerInfo": {
"layerInfoId": 3, "layerInfoId": 1,
"choiceKey": "allowedGboAgeProfiles", "choiceKey": "isRenewable",
"choiceLabel": "Wat is uw geboortedatum?", "choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false, "isCustomChoice": false,
}, },
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
@ -1222,9 +1227,9 @@ paths:
"productId": 115, "productId": 115,
"parentProductId": 109, "parentProductId": 109,
"layerInfo": { "layerInfo": {
"layerInfoId": 3, "layerInfoId": 1,
"choiceKey": "allowedGboAgeProfiles", "choiceKey": "isRenewable",
"choiceLabel": "Wat is uw geboortedatum?", "choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false, "isCustomChoice": false,
}, },
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
@ -1372,12 +1377,7 @@ paths:
{ {
"productId": 116, "productId": 116,
"parentProductId": 115, "parentProductId": 115,
"layerInfo": { "layerInfo": null,
"layerInfoId": 1,
"choiceKey": "isRenewable",
"choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "33615", "gboPackageTemplateId": "33615",
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -1526,12 +1526,7 @@ paths:
{ {
"productId": 117, "productId": 117,
"parentProductId": 115, "parentProductId": 115,
"layerInfo": { "layerInfo": null,
"layerInfoId": 1,
"choiceKey": "isRenewable",
"choiceLabel": "Kies voor een doorlopend abonnement of een enkele termijn",
"isCustomChoice": false,
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "33615", "gboPackageTemplateId": "33615",
"tapConnectProductCode": null, "tapConnectProductCode": null,
@ -1685,10 +1680,10 @@ paths:
"productId": 112, "productId": 112,
"parentProductId": 49, "parentProductId": 49,
"layerInfo": { "layerInfo": {
"layerInfoId": 2, "layerInfoId": 3,
"choiceKey": "regio", "choiceKey": "allowedGboAgeProfiles",
"choiceLabel": "Kies de gewenste regio", "choiceLabel": "Wat is uw geboortedatum?",
"isCustomChoice": true, "isCustomChoice": false,
}, },
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"gboPackageTemplateId": "33618", "gboPackageTemplateId": "33618",
@ -1858,10 +1853,11 @@ components:
description: >- description: >-
Gives information on the choice that the customer has to make, to enable the touchpoint Gives information on the choice that the customer has to make, to enable the touchpoint
to proceed further "down the product-tree" by selecting (PATCH-ing) the correct product-variant. to proceed further "down the product-tree" by selecting (PATCH-ing) the correct product-variant.
LayerInfo is not a mandatory product-attribute, but it should always be present on all product for which LayerInfo is not a mandatory product-attribute, but it should always be present on all products for which
`parentProductId != null`. (LayerInfo makes no sense for top-level parents as there is always a single starting point). \ there are underlying products, i.e. for which `GET /products?parentProductId=...` returns a non-empty list.
When no LayerInfo is present, the touchpoint can conclude that the product is a final fulfillable product. \
**PMT should ensure that all products in the same "product-branch" (same `parentProductId`) have the same `layerInfoId` **PMT should ensure that all non-leaf-node products (i.e. products that have underlying products) have a `layerInfoId`
referenced. If a product is found to be in violation of this rule, its attribtue `isValid` should be set to `false`.** referenced. If a product is found to be in violation of this rule, its attribtue `isValid` should be set to `false`.**
required: required:
- layerInfoId - layerInfoId
@ -1877,12 +1873,12 @@ components:
type: string type: string
description: >- description: >-
Contains the JSON Key of the product-attribute that the customer has to make some Contains the JSON Key of the product-attribute that the customer has to make some
choice on (determined by PMT), so that te correct product-variant can be selected by the touchpoint. For some choice on (determined by PMT), so that te correct product-variant (one of the direct child-products) can be
decisions (like region), there is no product attribute, and thus `isCustomChoice` will be set selected by the touchpoint. For some decisions (like region), there is no product attribute, and thus
to `true`, and `choiceKey` can then be set to any string on which touchpoints can also trigger `isCustomChoice` will be set to `true`, and `choiceKey` can then be set to any string on which touchpoints
behaviour if desired (think "region picker tool"). Therefore, reuse of choiceKeys should be can also trigger behaviour if desired (think "region picker tool"). Therefore, reuse of `choiceKeys` should be
the goal, so touchoints can keep their triggers simple and prevent duplication of the goal, so touchoints can keep their triggers simple and prevent duplication of
similar choiceKeys to trigger the same behaviour. similar `choiceKeys` to trigger the same behaviour.
choiceLabel: choiceLabel:
example: Kies voor een doorlopend abonnement of een enkele termijn example: Kies voor een doorlopend abonnement of een enkele termijn
type: string type: string
@ -1894,12 +1890,10 @@ components:
example: false example: false
type: boolean type: boolean
description: >- description: >-
Indicates if the choice is a custom choice. If `false`, the PMT should fill `choiceKey` with the Indicates if the choice is a custom choice. If `false`, the PMT should validate that the `choiceKey` is a
"differing attribute for this product-layer" and the user should not be able to override this. When differentiating attribute for the underlying product-variants. When the attribute denoted by the `choiceKey` is
no single attribute can be pinpointed by PMT, the product will become invalid (`isValid == false`) until either the same for all underlying variants, PMT validation will fail and the product will become invalid (`isValid == false`)
a situation with a single differing attribute is created, or if `isCustomChoice` is set to `true` - this would, until either the underlying products are updated, or a `LayerInfo` with `isCustomChoice == true` is configured.
however, also mean that touchpoints should be notified of this, especially if the configured LayerInfo contains
a new, not previously used, `choiceKey`.
GboAgeProfileResponse: GboAgeProfileResponse:
type: object type: object
required: required:

View File

@ -1243,12 +1243,22 @@ paths:
] ]
} }
getDetailsGboPadProduct: getDetailsGboPadProduct:
summary: GBO product (PAD required, renewable, allowedGboAgeProfiles, padBirthDate) summary: GBO product (PAD required, layerInfo, allowedGboAgeProfiles, padBirthDate)
description: >-
This product has `layerInfo` defined, which means that this product is not a final fulfillable product, but that there are underlying variants
that the customer has to choose between. The `layerInfo` defines what distinguishes the underlying variants - in this case the
`allowedGboAgeProfiles` array contains different values for the underlying variants. Based on age information provided by the customer,
the correct underlying variant can be selected.
value: value:
{ {
"productId": 3, "productId": 3,
"parentProductId": null, "parentProductId": null,
"layerInfo": null, "layerInfo": {
"layerInfoId": 1,
"choiceKey": "allowedGboAgeProfiles",
"choiceLabel": "Wat is uw geboortedatum?",
"isCustomChoice": false
},
"fikoArticleNumber": "1234", "fikoArticleNumber": "1234",
"isValid": true, "isValid": true,
"isArchived": false, "isArchived": false,
@ -1279,16 +1289,7 @@ paths:
"name": "B2C" "name": "B2C"
} }
], ],
"customerSegments": [ "customerSegments": null,
{
"customerSegmentId": 5,
"name": "Student"
},
{
"customerSegmentId": 6,
"name": "Ooievaarspas-gerechtigde"
}
],
"allowedGboAgeProfiles": [ "allowedGboAgeProfiles": [
{ {
"gboAgeProfileId": 2, "gboAgeProfileId": 2,
@ -1377,221 +1378,8 @@ paths:
"serviceOptions": [], "serviceOptions": [],
"validityDuration": "P1M", "validityDuration": "P1M",
"maxStartInFutureDuration": "P6W", "maxStartInFutureDuration": "P6W",
"isRenewable": true, "isRenewable": null,
"sendInvoice": true, "sendInvoice": null,
"imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
"productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
"termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
"isSellableAtHtm": true,
"needsSolvencyCheckConsumer": false,
"needsSolvencyCheckBusiness": false,
"sellingPeriods": [
{
"sellingPeriodId": 5,
"fromInclusive": "2024-09-01T00:00:00.000+00:00",
"toInclusive": "2024-12-31T23:59:59.999+00:00",
"salesTouchpoint": {
"salesTouchpointId": 3,
"name": "Website (Perplex)",
"isActive": true,
"retailer": {
"retailerId": 1001,
"name": "HTM externe touchpoints",
"street": "Koningin Julianaplein",
"number": "10",
"numberAddition": null,
"postalCode": "2595 AA",
"city": "Den Haag",
"country": "Nederland",
"emailAddress": "info@htm.nl",
"phoneNumber": "070 374 9002",
"taxId": "572309345923",
"imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg"
}
},
"forbiddenPaymentMethods": [
{
"forbiddenPaymentMethodId": 3,
"name": "creditcard",
"issuer": "American Express"
}
],
"sellingPrices": [
{
"sellingPriceId": 5,
"amountExclTax": null,
"amountInclTax": 5900,
"taxCode": "V09",
"taxPercentage": 9.0000,
"fromInclusive": "2024-09-01T00:00:00.000+00:00",
"toInclusive": "2024-12-31T23:59:59.999+00:00",
"internalPrice": 5413.0000
}
]
}
],
"purchasePrices": [
{
"purchasePriceId": 1,
"amountExclTax": null,
"taxCode": "V09",
"taxPercentage": 9.0000,
"amountInclTax": 0,
"fromInclusive": "2024-09-01T00:00:00.000+00:00",
"toInclusive": "2024-12-31T23:59:59.999+00:00"
}
],
"auditTrail": [
{
"auditTrailId": 2,
"action": "update",
"user": "api",
"timestamp": "2024-09-03T08:39:38.000+00:00"
},
{
"auditTrailId": 1,
"action": "insert",
"user": "api",
"timestamp": "2024-09-03T08:38:24.000+00:00"
}
]
}
getDetailsProductVariantWithParent:
summary: GBO product-variant, meaning that parentProductId and layerInfo are present
description: >-
This product is not a top-level parent, but a product-variant that refers to another product (via `parentProductId`).
This means that the `layerInfo` should be present for this product-variant. The parent product references is `productId 3`,
so the product in this example is an extension of that product-definition. MOre specifically: in this product-variant, we
removed some `allowedGboAgeProfiles` from the definition - this means that the customer has to provide the birthdate of
the token-owner (padBirthdate) in order to be able to buy this product-variant.
value:
{
"productId": 4,
"parentProductId": 3,
"layerInfo": {
"layerInfoId": 1,
"choiceKey": "allowedGboAgeProfiles",
"choiceLabel": "Wat is uw geboortedatum?",
"isCustomChoice": false
},
"fikoArticleNumber": "1234",
"isValid": true,
"isArchived": false,
"gboPackageTemplateId": "33610",
"tapConnectProductCode": null,
"productName": "HTM Regio Vrij DH73 Reductietarief",
"productDescription": "Voor een vast bedrag onbeperkt reizen met EBS, HTM en RET in het gekozen gebied in de regio Rotterdam Den Haag.",
"validityPeriod": {
"validityPeriodId": 3,
"fromInclusive": "2024-09-01T00:00:00.000+00:00",
"toInclusive": "2024-12-31T23:59:59.999+00:00"
},
"productTranslations": [
{
"language": "en",
"name": "HTM Regio Free DH73",
"description": "For a fixed amount unlimited travel with EBS, HTM and RET in the chosen area in the Rotterdam The Hague region."
}
],
"productOwner": {
"productOwnerId": 1,
"name": "Corneel Verstoep",
"organization": "HTM"
},
"marketSegments": [
{
"marketSegmentId": 1,
"name": "B2C"
}
],
"customerSegments": [
{
"customerSegmentId": 5,
"name": "Student"
},
{
"customerSegmentId": 6,
"name": "Ooievaarspas-gerechtigde"
}
],
"allowedGboAgeProfiles": [
{
"gboAgeProfileId": 2,
"name": "Kind (4 t/m 11 jaar)",
"ageFromInclusive": 4,
"ageUntilInclusive": 11
},
{
"gboAgeProfileId": 3,
"name": "Jongere (12 t/m 18 jaar)",
"ageFromInclusive": 12,
"ageUntilInclusive": 18
}
],
"productCategory": {
"productCategoryId": 2,
"isTravelProduct": true,
"name": "Afgekocht reisrecht"
},
"requiredCustomerLevel": {
"requiredCustomerLevelId": 3,
"name": "profile"
},
"requiredProducts": null,
"incompatibleProducts": [
{
"incompatibleProductId": 4,
"productName": "HTM Regio Vrij DH73 Reductietarief",
"description": "Kan niet combineren met zichzelf"
}
],
"mandatoryCustomerDataItems": [
{
"mandatoryCustomerDataItemId": 4,
"customerDataItem": "emailAddress"
},
{
"mandatoryCustomerDataItemId": 8,
"customerDataItem": "padBirthDate"
},
],
"requiredGboPersonalAttributes": [
{
"requiredGboPersonalAttributeId": 1,
"name": "NAME"
},
{
"requiredGboPersonalAttributeId": 2,
"name": "BIRTHDATE"
},
{
"requiredGboPersonalAttributeId": 3,
"name": "PHOTO"
}
],
"tokenTypes": [
{
"tokenTypeId": 1,
"name": "EMV"
},
{
"tokenTypeId": 2,
"name": "OVpas physical"
},
{
"tokenTypeId": 3,
"name": "OVpas digital"
}
],
"paymentMoment": {
"paymentMomentId": 1,
"name": "prepaid"
},
"serviceOptions": null,
"validityDuration": "P1M",
"maxStartInFutureDuration": "P6W",
"isRenewable": true,
"sendInvoice": true,
"imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg", "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
"productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina", "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
"termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina", "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
@ -2663,10 +2451,11 @@ components:
description: >- description: >-
Gives information on the choice that the customer has to make, to enable the touchpoint Gives information on the choice that the customer has to make, to enable the touchpoint
to proceed further "down the product-tree" by selecting (PATCH-ing) the correct product-variant. to proceed further "down the product-tree" by selecting (PATCH-ing) the correct product-variant.
LayerInfo is not a mandatory product-attribute, but it should always be present on all product for which LayerInfo is not a mandatory product-attribute, but it should always be present on all products for which
`parentProductId != null`. (LayerInfo makes no sense for top-level parents as there is always a single starting point). \ there are underlying products, i.e. for which `GET /products?parentProductId=...` returns a non-empty list.
When no LayerInfo is present, the touchpoint can conclude that the product is a final fulfillable product. \
**PMT should ensure that all products in the same "product-branch" (same `parentProductId`) have the same `layerInfoId` **PMT should ensure that all non-leaf-node products (i.e. products that have underlying products) have a `layerInfoId`
referenced. If a product is found to be in violation of this rule, its attribtue `isValid` should be set to `false`.** referenced. If a product is found to be in violation of this rule, its attribtue `isValid` should be set to `false`.**
required: required:
- layerInfoId - layerInfoId
@ -2682,12 +2471,12 @@ components:
type: string type: string
description: >- description: >-
Contains the JSON Key of the product-attribute that the customer has to make some Contains the JSON Key of the product-attribute that the customer has to make some
choice on (determined by PMT), so that te correct product-variant can be selected by the touchpoint. For some choice on (determined by PMT), so that te correct product-variant (one of the direct child-products) can be
decisions (like region), there is no product attribute, and thus `isCustomChoice` will be set selected by the touchpoint. For some decisions (like region), there is no product attribute, and thus
to `true`, and `choiceKey` can then be set to any string on which touchpoints can also trigger `isCustomChoice` will be set to `true`, and `choiceKey` can then be set to any string on which touchpoints
behaviour if desired (think "region picker tool"). Therefore, reuse of choiceKeys should be can also trigger behaviour if desired (think "region picker tool"). Therefore, reuse of `choiceKeys` should be
the goal, so touchoints can keep their triggers simple and prevent duplication of the goal, so touchoints can keep their triggers simple and prevent duplication of
similar choiceKeys to trigger the same behaviour. similar `choiceKeys` to trigger the same behaviour.
choiceLabel: choiceLabel:
example: Kies voor een doorlopend abonnement of een enkele termijn example: Kies voor een doorlopend abonnement of een enkele termijn
type: string type: string
@ -2699,12 +2488,10 @@ components:
example: false example: false
type: boolean type: boolean
description: >- description: >-
Indicates if the choice is a custom choice. If `false`, the PMT should fill `choiceKey` with the Indicates if the choice is a custom choice. If `false`, the PMT should validate that the `choiceKey` is a
"differing attribute for this product-layer" and the user should not be able to override this. When differentiating attribute for the underlying product-variants. When the attribute denoted by the `choiceKey` is
no single attribute can be pinpointed by PMT, the product will become invalid (`isValid == false`) until either the same for all underlying variants, PMT validation will fail and the product will become invalid (`isValid == false`)
a situation with a single differing attribute is created, or if `isCustomChoice` is set to `true` - this would, until either the underlying products are updated, or a `LayerInfo` with `isCustomChoice == true` is configured.
however, also mean that touchpoints should be notified of this, especially if the configured LayerInfo contains
a new, not previously used, `choiceKey`.
GboAgeProfileResponse: GboAgeProfileResponse:
type: object type: object
required: required: