Extending Prices with Custom Fields

How To use Custom CustomFields on Prices?

You need more fields on the Prices of your Products?

This tutorial explains how to enhance the standard Price with an extra field, in case your use case requires it. In the example use case for this tutorial we'd like to have a kilo price for the products that come in different weights, like 100g or 500g. To be able to compare the prices of those products better, we'll assign a kilo price to each product telling how much the product cost if its weight was one kilogram.

This means we are going to extend the standard Price on the ProductVariant with a Custom Type that will contain the CustomField that we need for the kiloPrice.

Learn more about using Custom Fields in our self-paced Extensibility overview module.

Create Custom Type for Price with CustomField

Before we can add the custom field kiloPrice to the PriceDraft of the ProductDraft to be created we need to come up with a Custom Type for the enhanced Price resource that will contain the CustomField kiloPrice.
With the resourceTypeIds we specify that we'd like to extend the Price resource in particular and in the fieldDefinitions we'll specify that we'd like to have the CustomField named 'kiloPrice' of the CustomFieldMoneyType on the customized price that we'll give the unique key 'price-withKiloPrice'.

The POST request to the API endpoint /<project-id>/types contains following payload:

{
"key": "price-withKiloPrice",
"name": {
"en": "additional custom field kiloPrice"
},
"resourceTypeIds": [
"product-price"
],
"fieldDefinitions": [
{
"type": {
"name": "Money"
},
"name": "kiloPrice",
"label": {
"en": "kilo price",
"de": "Kilopreis"
},
"required": false,
"inputHint": "SingleLine"
}
]
}

After successful creation the HTTP status code should be 201 and the response will contain the new Custom Type with the custom field of name "kiloPrice":

{
"id": "<customized-price-type-id>",
"version": 1,
"key": "price-withKiloPrice",
"name": {
"en": "additional custom field kiloPrice"
},
"resourceTypeIds": [
"product-price"
],
"fieldDefinitions": [
{
"name": "kiloPrice",
"label": {
"en": "kilo price",
"de": "Kilopreis"
},
"required": false,
"type": {
"name": "Money"
},
"inputHint": "SingleLine"
}
]
}

Create Product with CustomField on Price

Now we are able to create a product with the standard price as well as the kiloPrice we introduced before.
For this tutorial we assume that you have created a ProductType before that we now use for creating a ProductDraft.

Let's do so by sending a POST request on the API endpoint /<project-id>/products with following payload:

{
"productType": {
"typeId":"product-type",
"id":"<product-type-id>"
},
"name": {
"en": "Product with kilo price"
},
"slug": {
"en": "product_slug_kilo_price"
},
"masterVariant": {
"sku": "SKU-kiloPrice",
"prices": [
{
"value": {
"currencyCode": "EUR",
"centAmount": 100
},
"custom": {
"typeId": "<customized-price-type-id>",
"fields": {
"kiloPrice": {
"currencyCode": "EUR",
"centAmount": 1000
}
}
}
}
]
}
}

You should have received a response to that request with the HTTP status code 201 containing the Product with the customized Price resource:

{
"id": "<created-product-id>",
"version": 1,
"createdAt": "2022-09-04T18:42:21.059Z",
"lastModifiedAt": "2022-09-04T18:42:21.059Z",
"lastModifiedBy": {
"isPlatformClient": true
},
"createdBy": {
"isPlatformClient": true
},
"productType": {
"typeId": "product-type",
"id": "<product-type-id>"
},
"masterData": {
"current": {
"name": {
"en": "Product with kilo price"
},
"categories": [],
"categoryOrderHints": {},
"slug": {
"en": "product_slug_kilo_price"
},
"masterVariant": {
"id": 1,
"sku": "SKU-kiloPrice",
"prices": [
{
"id": "<created-product-price-id>",
"value": {
"type": "centPrecision",
"currencyCode": "EUR",
"centAmount": 100,
"fractionDigits": 2
},
"custom": {
"type": {
"typeId": "type",
"id": "<customized-price-type-id>"
},
"fields": {
"kiloPrice": {
"type": "centPrecision",
"currencyCode": "EUR",
"centAmount": 1000,
"fractionDigits": 2
}
}
}
}
],
"images": [],
"attributes": [],
"assets": []
},
"variants": [],
"searchKeywords": {}
},
"staged": {
"name": {
"en": "Product with kilo price"
},
"categories": [],
"categoryOrderHints": {},
"slug": {
"en": "product_slug_kilo_price"
},
"masterVariant": {
"id": 1,
"sku": "SKU-kiloPrice",
"prices": [
{
"id": "<created-product-price-id>",
"value": {
"type": "centPrecision",
"currencyCode": "EUR",
"centAmount": 100,
"fractionDigits": 2
},
"custom": {
"type": {
"typeId": "type",
"id": "<customized-price-type-id>"
},
"fields": {
"kiloPrice": {
"type": "centPrecision",
"currencyCode": "EUR",
"centAmount": 1000,
"fractionDigits": 2
}
}
}
}
],
"images": [],
"attributes": [],
"assets": []
},
"variants": [],
"searchKeywords": {}
},
"published": false,
"hasStagedChanges": false
},
"lastVariantId": 1
}

Update Product with CustomField on Price

Creating new products was not a big deal, but what if you want to update existing products with the kiloPrice attribute that we introduced after the product was created?

That's also possible with the update actions Set Price Custom Type and Set Price CustomField. In this section we'll update the following example product that contains only the standard Price so far:

{
"id": "<product-to-be-updated-id>",
"version": 1,
"productType": {
"typeId": "product-type",
"id": "<product-type-id>"
},
"masterData": {
"current": {
"name": {
"en": "Product with Standard Price"
},
"categories": [],
"categoryOrderHints": {},
"slug": {
"en": "product_slug_standard_price"
},
"masterVariant": {
"id": 1,
"sku": "SKU-0",
"prices": [
{
"value": {
"currencyCode": "EUR",
"centAmount": 100
},
"id": "<updated-product-price-id>"
}
],
"images": [],
"attributes": []
},
"variants": [],
"searchKeywords": {}
},
"staged": {
"name": {
"en": "Product with Standard Price"
},
"categories": [],
"categoryOrderHints": {},
"slug": {
"en": "product_slug_standard_price"
},
"masterVariant": {
"id": 1,
"sku": "SKU-0",
"prices": [
{
"value": {
"currencyCode": "EUR",
"centAmount": 100
},
"id": "<updated-product-price-id>"
}
],
"images": [],
"attributes": []
},
"variants": [],
"searchKeywords": {}
},
"published": false,
"hasStagedChanges": false
},
"createdAt": "2015-10-14T11:52:33.555Z",
"lastModifiedAt": "2015-10-14T11:52:33.555Z"
}

Similar to what we did with the ProductDraft in the section about creating the product we need to update the existing Product with the customized Price first before we can actually set the kiloPrice field to it:

We'll be using the update action Set Price Custom Type for doing this. Let's assign a price-withKiloPrice-type Price resource to the existing product "SKU-0" by sending the payload below with the update request to the products API endpoint:

POST /{project-id}/products/{id-of-product-SKU-0} with following payload:

{
"version": 1,
"actions": [
{
"action": "setProductPriceCustomType",
"typeKey": "price-withKiloPrice",
"priceId":"<updated-product-price-id>"
}
]
}

From the response to the update action you'll see that the price in the master variant of the staged projection now contains the custom field, but it does not appear in the master variant of the current projection:

{
"id": "<product-to-be-updated-id>",
"version": 2,
"productType": {
"typeId": "product-type",
"id": "<product-type-id>"
},
"masterData": {
"current": {
"name": {
"en": "Product with Standard Price"
},
"categories": [],
"categoryOrderHints": {},
"slug": {
"en": "product_slug_standard_price"
},
"masterVariant": {
"id": 1,
"sku": "SKU-0",
"prices": [
{
"value": {
"currencyCode": "EUR",
"centAmount": 100
},
"id": "<updated-product-price-id>"
}
],
"images": [],
"attributes": []
},
"variants": [],
"searchKeywords": {}
},
"staged": {
"name": {
"en": "Product with Standard Price"
},
"categories": [],
"categoryOrderHints": {},
"slug": {
"en": "product_slug_standard_price"
},
"masterVariant": {
"id": 1,
"sku": "SKU-0",
"prices": [
{
"value": {
"currencyCode": "EUR",
"centAmount": 100
},
"id": "<updated-product-price-id>",
"custom": {
"type": {
"typeId": "type",
"id": "<customized-price-type-id>"
},
"fields": {}
}
}
],
"images": [],
"attributes": []
},
"variants": [],
"searchKeywords": {}
},
"published": false,
"hasStagedChanges": true
},
"createdAt": "2015-10-14T11:52:33.555Z",
"lastModifiedAt": "2015-10-14T16:17:56.430Z"
}

This is no surprise since the API behaves according to the usual product data workflow in which you update the staged projection first and then you publish the product. After publish also the current projection will contain the custom fields.

As you can see, the customized price has been set, but there is no custom field given so far; the fields are empty:

fields:{}

Due to this we'll have to assign the kiloPrice in another update action called Set Price CustomField.

POST /{project-id}/products/{id-of-product-SKU-0} with following payload:

{
"version": 2,
"actions": [
{
"action": "setProductPriceCustomField",
"name": "kiloPrice",
"priceId": "<updated-product-price-id>",
"value": {
"currencyCode": "EUR",
"centAmount": 2000
}
}
]
}

In the response we'll now find the kiloPrice field with the appropriate value:

"fields": {
"kiloPrice": {
"currencyCode": "EUR",
"centAmount": 2000
}
}

That's it. We are now done with updating an existing product price with a custom field.

Query Product with CustomField on Price

With the new 'kiloPrice' on the Product we are able to query for this field. Let's say we'd like to get all the products with a kilo price of 1000 cents. For this we need to use a query predicate on the kiloPrice field of the form kiloPrice(centAmount = 1000).
Since it is a CustomField of a price we'll need to mark it like that, like so: (prices(custom(fields(kiloPrice(centAmount = 1000)))))

The complete query request on the Products endpoint looks like below:

GET {projectKey}/products?where=masterData(staged(masterVariant(prices(custom(fields(kiloPrice(centAmount = 1000)))))))
ProductPagedQueryResponse findProductsWithCustomFieldOnPrice = apiRoot
.products()
.get()
.withWhere(
"masterData(staged(masterVariant(prices(custom(fields(kiloPrice(centAmount = 1000)))))))"
)
.executeBlocking()
.getBody();

The response to that query should only contain the products with a kilo price of 1000 cents in your project.