Skip to main content

Data Model

This page describes every entity in the Quantaprice system and how they relate to each other.

Entity Relationship Overview

                    ┌──────────┐
│ Currency │
└────┬─────┘
│ currency_code
v
┌─────────┐ ┌──────────┐ ┌───────────┐
│ Article │◄──────►│ Price │◄──────►│ Pricelist │
│ (SKU) │ sku │ │ code │ │
└────┬────┘ └──────────┘ └─────┬─────┘
│ │ parent_code
│ vat_class override │ (inheritance)
v v
┌──────────┐ ┌───────────┐ ┌──────────┐
│ Tax Area │───►│ VAT Rate │◄────────│VAT Class │
└──────────┘ └───────────┘ └──────────┘

Prices are the central join — each price connects an Article to a Pricelist at a specific quantity break and effective date. Pricelists define the pricing context (currency, VAT mode), and can inherit from a parent pricelist.

Articles

Products or SKUs in your catalog.

FieldTypeDescription
skustringUnique SKU code (primary identifier)
namestringDisplay name
metadataobjectFlexible key/value attributes (e.g. {"brand": "Acme", "category": "tools"})
updated_atdatetimeLast modification
deleted_atdatetimeSoft-delete timestamp (null = active)

Article metadata

Metadata is a free-form key/value map. These attributes can be used to:

  • Filter and search articles
  • Target pricing rules to specific product segments (e.g. all articles in a category)
  • Store custom fields without schema changes

Bundled articles

An article can be a bundle — a composite product made up of other articles. A bundle contains a list of components, each with a reference SKU and quantity. Bundle prices are calculated by aggregating component prices.

Pricelists

Define the pricing context: what currency, how VAT is handled, and optional inheritance.

FieldTypeDescription
codestringUnique pricelist code
currency_codestringISO 4217 currency (e.g. SEK, EUR)
vat_modestringNET (prices exclude VAT) or GROSS (prices include VAT)
parent_codestringParent pricelist for inheritance (optional)
start_datedatetimeEffective start date
end_datedatetimeEffective end date (optional)
namestringDisplay name
descriptionstringDescription
price_rulesarrayDynamic price transformation rules (optional)
metadataobjectCustom attributes
updated_bystringLast modifier
updated_atdatetimeLast modification
deleted_atdatetimeSoft-delete timestamp

Pricelist inheritance

Pricelists can form a hierarchy using parent_code. When a price is not found on a pricelist, the system walks up the parent chain until it finds one. This lets you define a base price list and override only the prices that differ in child lists.

Price rules

Rules are JEXL expressions attached to a pricelist for dynamic price transformation (e.g. apply a 10% markup, set a floor price). Each rule has:

FieldTypeDescription
codestringRule identifier
expressionstringJEXL expression
descriptionstringHuman-readable description

Prices

The central entity — each price connects an article (SKU) to a pricelist at a specific quantity break and effective date.

Price fields

When creating or updating a price, you provide a price structure for each quantity tier:

FieldTypeDescription
quantity_breaknumberMinimum quantity for this tier (default: 1)
fixednumberFixed override price
catalognumberCatalog/list price
costnumberCost price
floornumberMinimum allowed price
recommendednumberRecommended retail price

When reading a price, you also receive:

FieldTypeDescription
sales_pricenumberComputed final sales price (after rules, FX, VAT, rounding)

Effective dating

Prices are effective-dated — each price has an effective_from timestamp. When querying, the system returns the most recent price that is effective at the requested point in time. This allows you to:

  • Schedule future price changes
  • Query historical prices at any past date
  • Maintain a full audit trail of price changes

Quantity breaks

A single article/pricelist combination can have multiple price tiers based on quantity. For example:

Quantity breakPrice
110.00
109.50
1008.00

When querying with a quantity of 25, the system selects the tier for quantity break 10 (the highest break not exceeding the requested quantity).

VAT System

VAT calculation uses three entities working together:

Tax areas

Geographic regions with distinct tax rules.

FieldTypeDescription
codestringUnique code (e.g. SE, DE, US-CA)
namestringDisplay name
is_activebooleanActive status

VAT classes

Product tax categories.

FieldTypeDescription
codestringUnique code (e.g. STANDARD, FOOD, BOOKS)
namestringDisplay name
descriptionstringDescription
is_activebooleanActive status

VAT rates

The actual tax percentage, defined per (tax area, VAT class) pair with effective dating.

FieldTypeDescription
tax_area_codestringTax area
vat_class_codestringVAT class
ratenumberPercentage (e.g. 25 for 25%)
start_datedatetimeEffective from

VAT calculation flow

  1. Resolve the tax area (from query parameters)
  2. Determine the VAT class for the article (default, or per-article override)
  3. Look up the effective VAT rate for that (tax area, VAT class) pair at the requested date
  4. Apply based on the pricelist's vat_mode:
    • NET: Calculate VAT and add to get price inc. VAT
    • GROSS: Back-calculate VAT and subtract to get price exc. VAT
  5. Round VAT amount to the currency's vat_precision decimal places

Currencies

See Currencies for full documentation, including vat_precision and active/inactive behavior.

Exchange Rates

See Exchange Rates for full documentation, including auto-update scheduling, date lookup fallback, and cross-rate computation.

Rounding Profiles

Range-based rounding rules that can be applied per currency.

FieldTypeDescription
codestringProfile code
currency_codestringTarget currency
default_globalbooleanUse as default for all currencies
default_for_currencybooleanUse as default for this currency
rulesarrayRange-based rounding tiers

Each rounding tier defines behavior for a price range. For example:

  • Prices under 10: round to nearest 0.05
  • Prices 10–100: round to nearest 0.10
  • Prices over 100: round to nearest 1.00

Webhooks

Push notifications for entity changes. See Change Events for full documentation.

FieldTypeDescription
urlstringDelivery URL
secretstringHMAC-SHA256 signing secret
entity_type_filterstringFilter by entity type (optional, null = all)
activebooleanEnabled status

Change Log

An append-only feed of all entity changes, accessible via polling. See Change Events for full documentation.

FieldTypeDescription
entity_typestringWhich entity changed (e.g. article, price)
change_typestringcreated, updated, or deleted
entity_codestringBusiness identifier (SKU, pricelist code, etc.)
composite_keystringSecondary identifier when needed (e.g. pricelist code for prices)
changed_atdatetimeWhen the change occurred
changed_bystringWho made the change
content_hashstringHash for deduplication

Settings

Runtime configuration key-value pairs.

FieldTypeDescription
keystringSetting name (e.g. fx.rate.auto.update)
valuestringSetting value

Settings are versioned — each update creates a new entry, preserving history.

Price Calculation Pipeline

When a price is requested, the system processes it through a fixed pipeline:

  1. Resolve metadata — currency, VAT mode, article attributes
  2. Fetch base price — from the pricelist (or parent chain), selecting the correct quantity break and effective date
  3. Apply price rules — JEXL expressions on the pricelist (discounts, markups, overrides)
  4. Convert currency — FX conversion if the query requests a different currency
  5. Apply VAT — add (NET mode) or subtract (GROSS mode), rounded to vat_precision
  6. Apply rounding — final price rounding per the rounding profile
  7. Return sales price — with full audit trail of each step

Every step is recorded so that the final price is fully auditable and reproducible.