# Data model

> How product, variation, shop, stock and price are related in BILLZ.

**Almost everything in BILLZ is scoped to a shop.**

## Product

A catalog unit. `product_type_id` takes one of three values: product, service or bundle; the reference list is `GET /v2/product-type`. Variability is a separate flag, `is_variative`; both a product with variations and its variations have the type “product”.

| Field                      | Meaning                                                                                                                                                                                     |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                       | product UUID                                                                                                                                                                                |
| `name`, `sku`, `barcode`   | name, SKU, barcode                                                                                                                                                                          |
| `is_variative`             | flag of a product with variations                                                                                                                                                           |
| `parent_id`                | reference to the parent for a variation; an empty string for a standalone product                                                                                                           |
| `product_type_id`          | product / service / bundle (exactly three values)                                                                                                                                           |
| `categories[]`, `brand_id` | classification                                                                                                                                                                              |
| `custom_fields[]`          | arbitrary company-defined characteristics: `custom_field_id`, `custom_field_name`, `custom_field_system_name`, `custom_field_value`, `from_parent` (the value is inherited from the parent) |
| `product_attributes[]`     | variation attributes: `attribute_id`, `attribute_name`, `attribute_value_id`, `attribute_value`                                                                                             |

## Variation

A child product with its own barcode and set of attributes. References the parent via `parent_id`. The variation name is built as “parent name / attribute value / attribute value”; the original parent name is stored separately in `base_name`.

Grouping with `group_variations=true` is supported only in `POST /v2/product-search-with-filters`: there the parent stays in the list and the variations come nested in `variations`. `GET /v2/products` has no such parameter: variations come as a flat list.

An attribute and a custom field are different entities, although filters use the common `product_field_filters` parameter split by `field_type`: `attribute` or `custom`.

## Shop

A physical point of sale. Stock, prices, cash registers, sales and warehouse processes are tied to `shop_id`. A user works in the context of the “current shop”.

## Stock and price

```json
// GET /v2/products
{
  "shop_measurement_values": [
    { "shop_id": "...", "shop_name": "Nike Store", "active_measurement_value": 5 }
  ],
  "shop_prices": [
    { "shop_id": "...", "shop_name": "Nike Store",
      "retail_price": 1000000, "retail_currency": "UZS",
      "supply_price": 600000, "supply_currency": "UZS",
      "promo_price": 500000, "promos": [ ] }
  ]
}
```

The structure depends on the method: in `POST /v2/product-search-with-filters`, instead of `active_measurement_value` there are `total_measurement_value`, `total_active_measurement_value`, `total_inactive_measurement_value`, `total_in_transfer_measurement_value` and others.

`promo_price` applies only when `promos` is not empty; `promos` contains only the promotion with the lowest price.

In `GET /v2/products` the `supply_price` and `supply_currency` fields are zeroed out (0 and `""`) if the key’s role has no permission to see the cost price. In the response this is indistinguishable from a real zero cost price.

## Customer

A buyer of the company: phone, cards, cashback, debts. The API has two adjacent sets of methods — `client` (creation and search, including by `chat_id` for bots) and `customer` (details, balance). The models are different.

Available to the integration key: `POST /v1/client`, `GET /v1/client`, `PUT /v1/client/:id`, `POST /v1/client-card`, `GET /v1/client-by-chat-id`, `GET /v1/customer/:id`, `PATCH /v1/customer/set-balance/:id`.

> **A customer card and a loyalty program are different entities**
>
> `POST /v1/client-card` creates a “name + code” pair for identifying the customer at the cash register: the card has neither an expiry date nor a link to a loyalty program. The loyalty program is tied to the company and to the customer.

The full list of structures with a description of every field is in the [API reference](/en/reference/api/).