Cookbook/Pricing

= Modeling Price Information =

This page describes how the GoodRelations ontology for e-commerce can be used to model price information. Such can be embedded using the W3C RDFa syntax into Web pages and will be crawled and used by next generation Web search engines.

One of the design principles of GoodRelations (*) is that


 * simple things should be simple to express, while
 * complex things should be possible to model without tweaking or extending the vocabulary.

On this page, we demonstrate how the established GoodRelations elements can be used to expose fine-granular price information like

"We lease our boats for 24 USD per 6 hours for 0-3 days, and for 75 USD per day for any longer rental."

(*) Wikiquote attributes this principle to Alan Kay.

Exposing GoodRelations rich meta-data can be as simple as this:

Minimal Example: Turtle Syntax
@prefix gr: &lt;http://purl.org/goodrelations/v1#&gt;. @prefix xsd: &lt;http://www.w3.org/2001/XMLSchema#&gt;. @prefix rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;.


 * company a gr:BusinessEntity;

gr:legalName "Hepp Space Ventures Inc."@en; gr:offers :offering.


 * offering a gr:Offering;

rdfs:label "Volkswagen Station Wagon, 4WD, 400 $"@en; rdfs:comment "I sell my old Volkswagen Station Wagon, 4WD, for 400 $"@en; gr:hasBusinessFunction gr:Sell; gr:hasPriceSpecification [ a gr:UnitPriceSpecification; gr:hasCurrencyValue "400"^^xsd:float; gr:hasCurrency "USD"^^xsd:string. ].

Minimal Example: RDFa Syntax
   

But when you have more fine-grained data available on your end, you will likely expose that full level of detail on the Web.

= Preliminaries =

Prefixes
First, we have to define the prefixes used in the examples: @prefix foo: &lt;http://www.example.com/xyz#&gt;. @prefix gr: &lt;http://purl.org/goodrelations/v1#&gt;. @prefix xsd: &lt;http://www.w3.org/2001/XMLSchema#&gt;. @prefix rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;.

Simple Offer
Next, let's model a simple offer to sell potatoes.
 * 1) Offer to sell potatoes

foo:company a gr:BusinessEntity ; gr:legalName "Hepp Space Ventures, Inc."@en ; gr:offers foo:offer.

foo:offer a gr:Offering ; rdfs:label "Potatoes for sale"@en ; rdfs:comment "We sell potatoes"@en ; gr:hasBusinessFunction gr:Sell ; gr:validFrom "2010-05-01T00:00:00Z"^^xsd:dateTime ; gr:validThrough "2010-12-31T23:59:59Z"^^xsd:dateTime ; gr:hasPriceSpecification foo:price.

Note: The actual object being offered (i.e. the potatoes) can (and should) be described in more detail. See the other GoodRelations recipes for examples. This recipe focuses on modeling the pricing.

= Pricing =

Example: 1 USD per piece
The UN/CEFACT Common Code for "piece" or "item" is "C62". foo:price a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "1.0"^^xsd:float ; gr:hasUnitOfMeasurement "C62"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean.
 * 1) 1 USD per piece (= per potatoe)

Example: 4 USD per kilogram
The UN/CEFACT Common Code for "kilogram" is "KGM". foo:price a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "4.0"^^xsd:float ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean.
 * 1) 4 USD per kilogram

Example: Price given in per liter, kg, meter, ...
There are two different ways of modeling this, and both are supported by GoodRelations:

1. You can e.g. say that you offer salt or red wine and that the price is USD 1 per 10 g or 1 l.

2. You can say that you offer exactly 10g of salt or 1 l of red wine, but maybe accept this offer multiple times.

In case, you model it using gr:hasUnitOfMeasurement attached to the gr:UnitPriceSpecification.

Example: 1 l of Bordeaux red wine for 1 USD/liter foo:offer a gr:Offering; gr:hasBusinessFunction gr:Sell; gr:includesObject [ a gr:TypeAndQuantityNode; gr:amountOfThisGood "1"^^xsd:float; gr:hasUnitOfMeasurement "C62"^^xsd:string; gr:typeOfGood foo:redwine ]; gr:hasPriceSpecification [ a gr:UnitPriceSpecification; gr:hasCurrency "USD"^^xsd:string; gr:hasCurrencyValue "1.00"^^xsd:float; gr:validThrough "2012-12-31T23:59:59Z"^^xsd:dateTime; gr:hasUnitOfMeasurement "LTR"^^xsd:string ].

foo:redwine a gr:SomeItems, &lt;http://www.productontology.org/id/Bordeaux_wine&gt;; gr:name "ACME Bordeaux Red Wine - dry and tasty"@en. This approach works only if the price is related to a standard unit (C62, INH, LTR, GRM, ...). So you can say that the price is 1 USD / liter (unit code: LTR), but this way, you cannot say that it is 1 USD per 10 gram or per ten liters.
 * 1) This represents an arbitrary amount of red wine:

If you want to say something like "USD 1 per 10 g", you must


 * create a gr:SomeItems node representing some objects / some amount of the good,
 * add this to a gr:Offering with gr:includesObject and a gr:TypeAndQuantityNode with 10 g as the quantity
 * set the price to 1 USD per 1 piece (code for piece:C62) of this offer (because one piece of this gr:SomeItems instance means 10 g).

Example: Table salt for 1 USD / 10 g foo:offer a gr:Offering; gr:hasBusinessFunction gr:Sell; gr:includesObject [ a gr:TypeAndQuantityNode; gr:amountOfThisGood "10"^^xsd:float; gr:hasUnitOfMeasurement "GRM"^^xsd:string; gr:typeOfGood foo:salt ]; gr:hasPriceSpecification [ a gr:UnitPriceSpecification; gr:hasCurrency "USD"^^xsd:string; gr:hasCurrencyValue "1.00"^^xsd:float; gr:validThrough "2012-12-31T23:59:59Z"^^xsd:dateTime; gr:hasUnitOfMeasurement "C62"^^xsd:string ].

foo:redwine a gr:SomeItems, &lt;http://www.productontology.org/id/Salt&gt;; gr:name "ACME Table Salt - clear and salty"@en.
 * 1) This represents an arbitrary amount of table salt:

Case 1: The price per unit is reduced for ADDITIONAL items beyond a certain minimum quantity
Example: Something costs 4 USD per kilogram up to 5 kilogram, 3 USD for any quantity beyond that. If you buy 9 kilogram, you will have to pay


 * 5 kg * 4 USD for the first 5 kg PLUS
 * 4 kg * 3 USD for the additional 4 kg

The total will be 32 USD.

GoodRelations pattern: Simply attach multiple instances of gr:UnitPriceSpecification to the gr:Offering and constrain their validity using non-overlapping gr:hasEligibleQuantity and gr:hasUnitOfMeasurement properties, i.e., two unit price specifications, one for 0..5 kg, and one for everything beyond 5 kg. Since there is no way to indicate "greater than", we have to use "5.00000001"^^xsd:float in the example below. foo:price1 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "4.0"^^xsd:float ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean ; gr:hasEligibleQuantity [ a gr:QuantitativeValue ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:hasMinValue "0"^^xsd:float ; gr:hasMaxValue "5"^^xsd:float ].
 * 1) 4 USD per kilogram up to 5 kilogram, 3 USD for any quantity beyond that

foo:price2 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "3.0"^^xsd:float ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean ; gr:hasEligibleQuantity [ a gr:QuantitativeValue ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:hasMinValue "5.00000001"^^xsd:float ]. You have to link from the offering to both price specifications now. Simply change


 * gr:hasPriceSpecification foo:price.

to


 * gr:hasPriceSpecification foo:price1, foo:price2.

So the offer node will look as follows: foo:offer a gr:Offering; gr:includes foo:potatoes; gr:validFrom "2010-05-01T00:00:00Z"^^xsd:dateTime ; gr:validThrough "2010-12-31T23:59:59Z"^^xsd:dateTime ; gr:hasPriceSpecification foo:price1, foo:price2.

Case 2: The price per unit is reduced for ALL items beyond a certain minimum quantity
Example: Something costs 4 USD per kilogram for purchases of up to 5 kilogram 3 USD for any quantity beyond that. If you buy 9 kilogram, you will have to pay


 * 9 kg * 3 USD = 27 USD

GoodRelations pattern: You have to use two gr:Offering instances in this case, one for quantities up to 5 kg and one for quantites beyond 5 kg. So in this second scenario, you constrain the validity of the two offers using non-overlapping gr:hasEligibleQuantity and gr:hasUnitOfMeasurement properties. Since there is no way to indicate "greater than", we have to use "5.00000001"^^xsd:float in the example below. foo:offer1 a gr:Offering ; rdfs:label "Potatoes for sale"@en ; rdfs:comment "We sell potatoes"@en ; gr:hasBusinessFunction gr:Sell ; gr:validFrom "2010-05-01T00:00:00Z"^^xsd:dateTime ; gr:validThrough "2010-12-31T23:59:59Z"^^xsd:dateTime ; gr:hasPriceSpecification foo:price1 ; gr:hasEligibleQuantity [ a gr:QuantitativeValue ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:hasMinValue "0"^^xsd:float ; gr:hasMaxValue "5"^^xsd:float ]. foo:price1 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "4.0"^^xsd:float ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean.
 * 1) We sell potatoes for 4 USD per kg if you buy between 0 and 5 kg

foo:offer2 a gr:Offering ; rdfs:label "Potatoes for sale"@en ; rdfs:comment "We sell potatoes"@en ; gr:hasBusinessFunction gr:Sell ; gr:validFrom "2010-05-01T00:00:00Z"^^xsd:dateTime ; gr:validThrough "2010-12-31T23:59:59Z"^^xsd:dateTime ; gr:hasPriceSpecification foo:price2 ; gr:hasEligibleQuantity [ a gr:QuantitativeValue ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:hasMinValue "5.00000001"^^xsd:float ].
 * 1) We sell potatoes for 3 USD per kg if you buy more than 5 kg

foo:price2 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "3.0"^^xsd:float ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean. Important: Don't forget to use gr:offers to link from the company (gr:BusinessEntity) to BOTH offers (gr:Offering) now: foo:company gr:offers foo:offer1, foo:offer2.

Seasonal Discounts and Special Offers
Simply attach multiple gr:UnitPriceSpecification instances to the gr:Offering and constrain their temporal validity using gr:validFrom and gr:validThrough.

Example: 4 USD per kilogram, 3 USD from May 1 - June 30, 2010 foo:price1 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "4.0"^^xsd:float ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean ; gr:validFrom "2010-07-01T00:00:00Z"^^xsd:dateTime ; gr:validThrough "2010-12-31T23:59:59Z"^^xsd:dateTime.
 * 1) 4 USD per kilogram from July 1 - December 31, 2010

foo:price2 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "3.0"^^xsd:float ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean ; gr:validFrom "2010-05-01T00:00:00Z"^^xsd:dateTime ; gr:validThrough "2010-06-30T23:59:59Z"^^xsd:dateTime. Same as in the previous example, you have to link from the offering to both price specifications now.
 * 1) 4 USD per kilogram from May 1 - June 30, 2010

Change


 * gr:hasPriceSpecification foo:price.

to


 * gr:hasPriceSpecification foo:price1, foo:price2.

Billing Increments
The property gr:billingIncrement with a domain of gr:UnitPriceSpecification and a range of xsd:float allows indicating the smallest billable unit of an item.

Examples:


 * Price 2 euro per hour, billing in units of 15 minutes
 * The price be 4 USD per kilogram of potatoes, but the billing will be based on the quantity rounded up to the next multiple of 100 grams.

The UN/CEFACT Common Code for "kilogram" is "KGM", that for gram is "GRM". Note that you have to use the same unit for the billing increment and the unit of measurement for the price reference, so either you express this as 4 USD per kilogram with an increment of 0.1 kilograms or as a price of 0.004 USD per gram with a billing increment of 100 grams. foo:price a gr:UnitPriceSpecification; gr:hasCurrency "USD"^^xsd:string; gr:hasCurrencyValue "4.0"^^xsd:float; gr:hasUnitOfMeasurement "KGM"^^xsd:string; gr:valueAddedTaxIncluded "true"^^xsd:boolean ; gr:billingIncrement "0.1"^^xsd:float. Important: The billing increment specifies only the rounding steps for determining the billable quantity. The price is given per unit of measurement. For instance, the example above expresses that the price is 4 USD per kilogram and not 4 USD per 0.1 kilograms.
 * 1) 4 USD per kilogram, billing increment 100 g

Price Ranges
Sometimes, the price can only be given as an interval, depending on characteristics of the actual item or the buying party. Simply use gr:hasMaxCurrencyValue and gr:hasMinCurrencyValue for the upper and lower bounds instead of gr:hasCurrencyValue. An RDFS-style reasoner will expand gr:hasCurrencyValue to two matching gr:hasMinCurrencyValue and gr:hasMaxCurrencyValue statements anyway. When querying, you should process gr:hasMinCurrencyValue and gr:hasMaxCurrencyValue instead of gr:hasCurrencyValue, unless you want to ignore offers that give ranges only. foo:price a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasMinCurrencyValue "3.0"^^xsd:float ; gr:hasMaxCurrencyValue "4.0"^^xsd:float ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean.
 * 1) 3..4 USD per kilogram, depending on the size of the potatoes

Indicating List Prices
You can indicate retails prices as recommended by the supplier etc. using the gr:priceType property. This attribute can be used to distinguish multiple different gr:UnitPriceSpecification instances for the same offer. It supersedes the former gr: isListPrice property. The following values are recommended:


 * SRP: "suggested retail price" - applicable for all sorts of a non-binding retail price recommendations, e.g. such published by the manufacturer or the distributor. This value replaces the former gr:isListPrice property.
 * INVOICE: The invoice price, mostly used in the car industry - this is the price a dealer pays to the manufacturer, excluding rebates and charges.

The absence of this property marks the actual sales price.
 * 1) 4 USD per kilogram with a "suggested retail price" of 4.50 USD.

foo:price1 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "4.0"^^xsd:float ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean.

foo:price2 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "4.5"^^xsd:float ; gr:hasUnitOfMeasurement "KGM"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean ; gr:priceType "SRP"^^xsd:string.

Car Rental: 25 USD / day plus 1 USD per mile
You can combine multiple gr:UnitPriceSpecification instances that are valid in combination. For example, a rental car business might say that the price for a particular rental car is 25 USD per day plus 1 USD per mile.

Note that the UN/CEFACT Common Code for "day" is "DAY", that for "car mile" is "1A". Also note that the gr:BusinessFunction for rental is gr:LeaseOut.
 * 1) Car Rental: 25 USD / day, 1 USD per mile

foo:company a gr:BusinessEntity ; gr:legalName "Hepp Space Ventures, Inc."@en ; gr:offers foo:car_offer.

foo:car_offer a gr:Offering ; rdfs:label "Car rental"@en ; rdfs:comment "We rent out cars"@en ; gr:hasBusinessFunction gr:LeaseOut ; gr:hasPriceSpecification foo:rental_price1, foo:rental_price2.

foo:rental_price1 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "25"^^xsd:float ; gr:hasUnitOfMeasurement "DAY"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean.

foo:rental_price2 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "1"^^xsd:float ; gr:hasUnitOfMeasurement "1A"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean. Note: The vehicles being offered can (and should) be described in more detail. See the other GoodRelations recipes for examples. This recipe focuses on modeling the pricing.

Cancellation Rules
GoodRelations does not provide a means for modeling cancellation policies and fees in detail, but you can easily attach them using rdfs:comment to the gr:UnitPriceSpecification node. foo:rental_price a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "75"^^xsd:float ; gr:hasUnitOfMeasurement "DAY"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean ; rdfs:comment "Cancellation Policy: You can cancel free of charge up to 72 hours prior to the start of the lease.                     In case of any later cancellation, we will charge the rental fees for one day."@en. = Complex Examples =

Turtle Syntax

 * 1) Boat Rental: 100 USD / day 0-3 days, 75 USD per day for any longer rental

foo:company a gr:BusinessEntity ; gr:legalName "Hepp Space Ventures, Inc."@en ; gr:offers foo:boat_offer.

foo:boat_offer a gr:Offering ; rdfs:label "Boat rental"@en ; rdfs:comment "We rent out boats"@en ; gr:hasBusinessFunction gr:LeaseOut ; gr:hasPriceSpecification foo:rental_price1, foo:rental_price2.

foo:rental_price1 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "100"^^xsd:float ; gr:hasUnitOfMeasurement "DAY"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean ; gr:hasEligibleQuantity [ a gr:QuantitativeValueInteger ; gr:hasUnitOfMeasurement "DAY"^^xsd:string ; gr:hasMinValueInteger "0"^^xsd:int ; gr:hasMaxValueInteger "3"^^xsd:int. ].

foo:rental_price2 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "75"^^xsd:float ; gr:hasUnitOfMeasurement "DAY"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean ; gr:hasEligibleQuantity [ a gr:QuantitativeValueInteger ; gr:hasUnitOfMeasurement "DAY"^^xsd:string ; gr:hasMinValueInteger "4"^^xsd:int. ].

RDFa Syntax
      

Boat Rental: 24 USD / 6 hours for 0-3 days, 75 USD per day for any longer rental
Note that the price information "24 USD per 6 hours" must be translated into 24/6 = 4 USD per hour and a billing increment of 6.0 hours. The UN/CEFACT Common Code for "hour" is "HUR".
 * 1) Boat Rental: 24 USD / 6 hours for 0-3 days, 75 USD per day for any longer rental

foo:rental_price1 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; # 24 USD per 6 hours =&gt; 4 USD / hour gr:hasCurrencyValue "4"^^xsd:float ; gr:hasUnitOfMeasurement "HUR"^^xsd:string ; gr:billingIncrement "6"^^xsd:float gr:valueAddedTaxIncluded "true"^^xsd:boolean ; gr:hasEligibleQuantity [ a gr:QuantitativeValueInteger ; gr:hasUnitOfMeasurement "DAY"^^xsd:string ; gr:hasMinValueInteger "0"^^xsd:int ; gr:hasMaxValueInteger "3"^^xsd:int. ].

foo:rental_price2 a gr:UnitPriceSpecification ; gr:hasCurrency "USD"^^xsd:string ; gr:hasCurrencyValue "75"^^xsd:float ; gr:hasUnitOfMeasurement "DAY"^^xsd:string ; gr:valueAddedTaxIncluded "true"^^xsd:boolean ; gr:hasEligibleQuantity [ a gr:QuantitativeValueInteger ; gr:hasUnitOfMeasurement "DAY"^^xsd:string ; gr:hasMinValueInteger "4"^^xsd:int. ].