Skip to main content

Introduction to prices

Prices

There's more to prices than meets the eye. If you want to know more about prices in EVA in general, seePricing as a concept. If you're looking for a guide on how to create price lists (or adjustments) by means of the UI in Admin Suite, see Prices in Admin Suite. The remainder of this page will focus on creating them via your API.

note

When adding a new price list to organization unit or a price list adjustment of usage type selling or sale price, you can attach an alternative price list that would only be applicable to a specific group of customers by making use of the PricingGroupID property.
This property is available on all CRUD services for PriceListAdjustment, and PriceListOrganizationUnit.

Ways to create and update prices​

As said before, there's much you can do with prices via our UI in Admin Suite. There's more than one way to do so via services too however. There's a service for each specific step in the process of creating a price list (adjustment), while we've also got a single, all-encompassing service for it called PushPriceList. Obviously, we'd recommend the latter, but the more specific ones help paint a clear picture of setting pricelists and the like.

We'll start of with the specific ones, but feel free to scroll down to PushPriceList

Creating a pricelist​

First off, we need to create the actual pricelist. To do so, we call CreatePriceList. Alternatively, you can create the pricelist via Admin Suite, and only use your API interface for updating the prices.

{
"Name": "Sales NL",
"CurrencyID": "EUR",
"TimeZone": "Europe/Amsterdam",
"IncludingVat": true,
"IsActive": true
}

A pricelist needs a name, currency and a time zone. The time zone helps with calculating the eventual effective and expiry dates. Additionally, we have to specify whether or not the prices will include VAT and whether or not the pricelist should be active.

note

There is no harm in setting the pricelist to active since it's not attached to an OU yet.

Adjustment layer​

After creating the pricelist, we can start adding adjustment layers. To add an adjustment layer, call CreatePriceListAdjustment using the ID we had returned from CreatePriceList.

{
"PriceListID": 1,
"Name": "Fall/Winter 2021",
"EffectiveDate": "2021-06-31T00:00:00Z",
"ExpireDate": "2021-12-31T00:00:00Z",
"Type": "MANUAL_INPUT"
}

Make sure to set the Type to MANUAL_INPUT to allow us to manually input the prices later. The service returns the ID for the newly created pricelist.

Uploading​

In a scenario where you would like to UploadPriceListManualInputAdjustments, bear in mind that such an upload will replace all existing adjustments. There is a property in this service which is called ProcessAsync. When it's set to false, the service will process the request directly and will also give a response with messages (if there are any).

If this setting is set to true, the file will be accepted and scheduled for processing in the backend. You will not receive any message at that time. However, after the processing is done, an email notification will be sent to the user who initiated the processing.

Adding prices​

Now we can start adding prices to the created adjustment layer. This is a product-by-product process. This service, in the contract to PushPriceList cannot push multiple prices in one request. To add product prices, call CreatePriceListManualInputAdjustment using the ID we had returned from CreatePriceListAdjustment:


{
"PriceListAdjustmentID": 1,
"ProductID": 3,
"Value": 10,
"EffectiveDate": "2021-06-31T00:00:00Z",
"ExpireDate": "2021-12-31T00:00:00Z"

}

The price should be specified under Value. Make sure to match the effective and expiry dates with the ones from the adjustment layer for the sake of consistency. The service returns the ID for your ManualInputAdjustment.

As usual, we can also call this service using the product's BackendID by setting the EVA-IDs-Mode header to ExternalIDs:

{
"PriceListAdjustmentID": 3,
"ProductID": "101-111-978020137931",
"Value": 10,
"EffectiveDate": "2021-12-31T00:00:00Z",
"Γ‹xpireDate": "2022-12-31T00:00:00Z"
}

In this case, the response will look slightly different.

Attaching pricelists to an OU​

After completing your pricelist, you can attach it to an organization unit using CreatePriceListOrganizationUnit. This is also where we specify the pricelist's usage type. Possible usage types:

IDUsageTypeFront-end name
1PurchasePurchase price, used for sales price between B2B
2CostCost price, price of products as calculated for the seller itself
3SalesSelling price, price for normal B2C scenarios
4Recommended retailRecommended retail price, for display purposes only
5PromotionSale price, reduction of normal Sales price

Example request:

{
"OrganizationUnitID": 4,
"TargetOrganizationUnitID": 3, // optional
"PriceListID": 1,
"PriceListUsageTypeID": 3
}

note

It is possible to adjust pricelists after having already linked it to an organization unit.

Target organization unit​

To make it possible for an (supplying) organization unit to have different prices for different (buying) OUs, you can add more than one price list to the (supplier) OU. To specify which buying OU should get what prices, you can set the property TargetOrganizationUnitID on a price list. This way the right price list will be used for purchase orders from that 'Target' OU. To accommodate this, the target OU should have a Cost price list configured to be populated with Supplier prices.

For now, this property can only be specified with the services mentioned here, such as UpdatePriceListOrganizationUnit.

PushPriceList​

This is where we bring in the big guns. By using PushPriceList, you can combine all the above steps and create or update all of these aspects in a single take.

To make it even easier from an integration perspective, this service works with BackendIDs by default. Some properties have been renamed in comparison to the services here above, to make them clearer:

  • Adjustments are instead called Components: a PriceList is made up out of components
  • The component type Manual input is now called PriceEntries
  • Component type PriceList is now called CopyPricesFromOtherPriceList
  • IncludingVat is now called SpecifiedPricesIncludeTax
PushPriceList properties

For sake of clarity, we'll go through all properties you can use in this service.


*/
export interface PushPriceList {
/**
* The ID property makes for the unique identifier for this pricelist.
*/
ID: string;
/**
* A string that identifies the system that was used to call this service. In combination with the ID of the pricelist, this is what uniquely identifies the pricelist in EVA.
* The SystemID must be the same for every call, if it's different from a previous call then a new pricelist will be created.
*/
SystemID: string;
/**
* If SpecifiedPricesIncludeTax is set to true, then all prices specified on this price list will be interpreted as already VAT-included. If set to false, all prices will be interpreted as VAT-excluded. If not specified then the default value is true.
*
* So if a price entry of 90 EUR is added to this pricelist with SpecifiedPricesIncludeTax = true (or not specified) then the price will be interpreted as 90 EUR already including VAT.
* If SpecifiedPricesIncludeTax = false then the price will be interpreted as 90 EUR excluding VAT so that will be added on top for any orders created with this price.
*/
SpecifiedPricesIncludeTax?: boolean;
/**
* The Name of the pricelist. Must be present for the creation of a pricelist but can be left empty in subsequent calls.
*/
Name?: string;
/**
* Whether or not the pricelist is active.
*/
IsActive?: boolean;
/**
* The timezone that will be used to interpret activation times for prices.
*/
TimeZone?: string;
/**
* The currency for the pricelist. Must be present for the creation of a pricelist but can be left empty in subsequent calls.
*/
CurrencyID?: string;
/**
* The list of components that make up the pricelist. If left empty, this will leave the current configuration unchanged. If specified, only the components present will be modified. Components must be explictly deleted.
*/
Components?: Component[];
/**
* The organization units that use this pricelist. If left empty, it will leave the current configuration unchanged. If specified, will replace the current configuration.
*/
ActiveForOrganizationUnits?: ActiveOrganizationUnit[];
}

export interface Component {
/**
* The unique identifier for this component. Required.
*/
ID: string;
/**
* The *Name* of the component. If left empty on creation of the component, the *ID* will be used as the name.
*/
Name?: string;
/**
* Determines the *Type* of the component. Cannot be changed after creation.
*/
Type: ComponentTypes;
/**
* Determines when this component will become active. Until then the component will not be included in the calculation of the pricelist.
*/
StartDate?: string;
/**
* Determines when this component will become inactive. After this date the component will not be included in the calculation of the pricelist.
*/
EndDate?: string;
/**
* The *PricingGroupID* that this component is active for. Components with a *PricingGroupID* are only active for users that are members of that group. If left empty, the component will be active for all users.
*/
PricingGroupID?: string;
/**
* Components are applied in the order of their sequence number, from lowest to highest.
*/
SequenceNumber?: number;
/**
* If specified as true, the component will be deleted.
*/
Delete: boolean;
/**
* The data that accompanies a Component with Type = PriceEntries, so a component that is simply a list of product/price pairs.

* The simplest and most direct way to give a product a price.
*
* This data consists of either a list of product/price pairs or a BlobID which is a reference to a previously created blob in EVA that contains data of the following format:
*
* [
* {
* "ProductID": "<ProductID>",
* "Price": <Price
* },
* ...
* ]
*
* If you only supply a handful of prices, the inline data works well, but for large quantities of prices you may want to use the BlobID method.
*
* This data is ignored for all other Component types.
*
* PriceEntries data is processed asynchronously in the background and will not be immediately visible after the service call returns.
*/
PriceEntriesData?: PriceEntriesData;
/**
* The data that accompanies a Component with Type = PricesFromOtherPriceList which is a component to copy prices from another pricelist.
*
* Is useful for example for promotion prices, where you first 'import' the prices from the original pricelist, and then apply a negative markup to them to create a promotion discount.
*
* Which prices you copy can be modified by specifying a ProductSearchTemplateID, only products that are contained in the results of the search will be copied,
* unless ExcludeProductsInFilter is set to true, in which case the reverse is true; all products except the matching ones will have their prices copied.
*/
CopyPricesFromOtherPriceListData?: CopyPricesFromOtherPriceListData;
/**
* The data that accompanies a Component with Type = Markup which is a component that alters the price value of the previusly applied components,
* either up or down. The markup/markdown can either be an absolute value or a percentage depending on the Type.
*/
MarkupData?: MarkupData;
}

export const enum ComponentTypes {
PriceEntries = 0,
Markup = 1,
CopyPricesFromOtherPriceList = 2
}

export interface PriceEntriesData {
/**
* The blob that refers to the price entry data that was previously uploaded to a Blob in EVA. If left empty the inline data will be used.
*
* The data in the blob must be in the following format:
*
* [
* {
* "ProductID": "<ProductID>",
* "Price": <Price>
* },
* ...
* ]
*/
BlobID?: string;
/**
* The inline data that will be used to create the price entries. If left empty the blob will be used.
*/
Prices?: PriceEntryPrice[];
}

export interface PriceEntryPrice {
ProductID: string;
Price?: number;
}

export interface CopyPricesFromOtherPriceListData {
/**
* The ID of the pricelist to copy prices from.
*/
PriceListID: string;
/**
* The ID of the ProductSearchTemplate to use to filter the products to copy. If left empty all products will be copied.
* Entity type: ProductSearchTemplate
*/
ProductSearchTemplateID?: number;
/**
* If true, all products except the matching ones will have their prices copied. If false, only the matching ones will have their prices copied.
*/
ExcludeProductsInFilter: boolean;
}

export interface MarkupData {
/**
* The type of markup/markdown. Either absolute amount or a percentage. Determines how FactorValue will be interpreted.
*/
Type: FactorType;
/**
* The value of the markup/markdown. If Type is Amount, this is the amount to add to the price.
* If Type is Percentage, this is the amount that the Price will be multiplied by.
*
* A 10% markup should be specified as 1.10, a 25% discount should be specified as 0.75.
*/
FactorValue: number;
/**
* The ID of the ProductSearchTemplate to use to filter the products to copy. If left empty all products will be copied.
* Entity type: ProductSearchTemplate
*/
ProductSearchTemplateID?: number;
/**
* If true, all products except the matching ones will have their prices copied. If false, only the matching ones will have their prices copied.
*/
ExcludeProductsInFilter: boolean;
}

export const enum FactorType {
Amount = 0,
Percentage = 1
}

export interface ActiveOrganizationUnit {
OrganizationUnitID: string;
UsageType: PriceListUsageTypes;
TargetOrganizationUnitID?: string;
PricingGroupID?: string;
Delete: boolean;
}

export const enum PriceListUsageTypes {
Sales = 0,
Cost = 1,
Promotion = 2,
Purchase = 3,
RecommendedRetail = 4
}


}

PushPriceList example cases
{
"ID": "u5lj84ef10",
"SystemID": "TEST_SUITE",
"SpecifiedPricesIncludeTax": "undefined",
"Name": "248lvcuu2b",
"IsActive": "undefined",
"TimeZone": "undefined",
"CurrencyID": "EUR",
"Components": [
{
"ID": "obo5ll4h28",
"Name": "d9m2cfljbz",
"Type": "PriceEntries",
"StartDate": "2022-07-18T00:00:00-07:00",
"EndDate": "2023-05-14T00:00:00-07:00",
"PricingGroupID": "ptluplakva",
"SequenceNumber": "undefined",
"Delete": "false"
}
]
}