Перейти к содержанию

Базовые понятия Planiqum

Иерархии

Иерархии в Planiqum представляют собой древовидные структуры данных, которые используются для организации и группировки информации. Каждая иерархия имеет:

  • Тип (Type) - определяет назначение иерархии (например, планирование, горизонт)
  • Уровни (Level) - определяют структуру иерархии (например, DPU, месяц)
  • Элементы (Item) - конкретные значения на каждом уровне (например, DPU1, январь)

Пример создания иерархии

# Создание типа иерархии
planning_type = Type.objects.create(
    key='planning',
    shortname='Planning',
    description='Planning Hierarchy Type'
)

# Создание уровня иерархии
dpu_level = HierarchyLevel.objects.create(
    type=planning_type,
    key='dpu',
    shortname='dpu',
    description='Demand Planning Unit'
)

# Создание элементов иерархии
dpu_item = HierarchyItem.objects.create(
    level=dpu_level,
    shortname='DPU1',
    description='Demand Planning Unit 1'
)

Пример инициализации через seed (hierarchy.yaml):

- model: hierarchy.type
  fields:
    key: planning
    shortname: Планирование

- model: hierarchy.level
  fields:
    type: [planning]
    key: dpu
    shortname: Объект планирования спроса
    description: ...
# и т.д. для всех уровней

Пример наполнения элементов (data/hierarchy/dpu.csv):

key,shortname,description
DPU1,Объект 1,Описание 1
DPU2,Объект 2,Описание 2

Работа с API

  • Получить список иерархий: GET /core/hierarchy/api/types/
  • Получить список уровней: GET /core/hierarchy/api/levels/
  • Получить список элементов: GET /core/hierarchy/api/items/

Подробнее: Работа с API Planiqum

Параметры

Параметры - это контейнеры для хранения данных. Каждый параметр:

  • Имеет уникальный ключ и название
  • Связан с определенными измерениями
  • Содержит одну или несколько мер
  • Имеет свою таблицу фактов в базе данных

Пример создания параметра

parameter = Parameter.objects.create(
    key='parameter1',
    shortname='Parameter 1',
    description='Parameter 1 Description'
)

# Добавление измерений
parameter.dimensions.add(dpu_dimension, month_dimension)

# Создание меры
measure = ParameterMeasure.objects.create(
    parameter=parameter,
    type=MEASURE_TYPES_ENUM.FLOAT,
    shortname='Qty Measure 1',
    description='Qty Measure 1 Description',
    key='qty1'
)

# Синхронизация параметра для создания таблицы фактов
parameter.sync()

> **Подробнее:** См. [Синхронизация параметров (для разработчиков)](parameters/sync.md)  технические детали реализации.

Пример инициализации через seed (parameters.yaml):

- model: parameters.dimension
  fields:
    key: dpu
    hierarchy_level: [dpu]
    shortname: Объект планирования спроса

- model: parameters.parameter
  fields:
    key: demand_plan
    app_name: demand
    shortname: План продаж
    dimensions:
      - [dpu]
      - [horizon__week]
# и т.д. для всех параметров и мер

Работа с API

  • Получить список параметров: GET /core/parameters/api/parameters/
  • Создать параметр: POST /core/parameters/api/parameters/

Измерения

Измерения (Dimensions) связывают параметры с иерархиями. Каждое измерение:

  • Связано с определенным уровнем иерархии
  • Используется для организации данных в таблице фактов
  • Имеет уникальный ключ и название

Пример создания измерения

dimension = ParameterDimension.objects.create(
    key='dpu',
    shortname='DPU',
    description='Demand Planning Unit Dimension',
    hierarchy_level=dpu_level
)

Пример инициализации через seed (parameters.yaml):

- model: parameters.dimension
  fields:
    key: dpu
    hierarchy_level: [dpu]
    shortname: Объект планирования спроса

Работа с API

  • Получить список измерений: GET /core/parameters/api/dimensions/
  • Создать измерение: POST /core/parameters/api/dimensions/

Меры

Меры (Measures) определяют типы данных, которые могут храниться в параметре. Каждая мера:

  • Имеет тип данных (FLOAT, INTEGER и т.д.)
  • Имеет уникальный ключ в рамках параметра
  • Представлена колонкой в таблице фактов
  • Может иметь дополнительные настройки (единицы измерения, валюта и т.д.)

Пример создания меры

measure = ParameterMeasure.objects.create(
    parameter=parameter,
    type=MEASURE_TYPES_ENUM.FLOAT,
    shortname='Quantity',
    description='Quantity Measure',
    key='qty'
)

Пример инициализации через seed (parameters.yaml):

- model: parameters.measure
  fields:
    key: demand_plan__qty
    parameter: [demand_plan]
    shortname: Кол-во

Работа с API

  • Получить список мер: GET /core/parameters/api/measures/
  • Создать меру: POST /core/parameters/api/measures/

Таблицы фактов

Каждый параметр имеет свою таблицу фактов в базе данных, которая:

  • Создается автоматически при синхронизации параметра
  • Содержит колонки для каждого измерения (dim_{key})
  • Содержит колонки для каждой меры (m_{key})
  • Может содержать дополнительные служебные колонки

Пример структуры таблицы фактов

CREATE TABLE fact_parameter1 (
    dim_dpu INTEGER,
    dim_month INTEGER,
    m_qty1 FLOAT,
    PRIMARY KEY (dim_dpu, dim_month)
);

Пример наполнения фактов (data/facts.csv):

parameter_key,level_dpu,level_month,qty
sales,DPU1,2024-01,100
sales,DPU1,2024-02,120

Работа с API

  • Получить данные фактов: GET /core/parameters/api/facts/
  • Загрузить данные: POST /core/parameters/api/facts/

Единицы измерения (UOM) и Валюты (Currency)

Общие понятия

  • UOM (единицы измерения) — позволяют хранить и отображать значения в разных физических единицах (кг, шт, паллеты и т.д.).
  • Currency (валюты) — позволяют хранить и отображать значения в разных денежных единицах (RUR, kRUR, mlnRUR и т.д.).

Модели и их поля

  1. Модель UOM (единицы измерения):

    class UOM(models.Model):
        code = models.CharField(max_length=10)  # Код единицы измерения (например, "kg", "pcs")
        name = models.CharField(max_length=255)  # Название единицы измерения
        description = models.TextField(blank=True)  # Описание
        is_active = models.BooleanField(default=True)  # Активна ли единица измерения
        created_at = models.DateTimeField(auto_now_add=True)  # Дата создания
        updated_at = models.DateTimeField(auto_now=True)  # Дата обновления
    

  2. Модель Currency (валюты):

    class Currency(models.Model):
        code = models.CharField(max_length=10)  # Код валюты (например, "RUR", "USD")
        name = models.CharField(max_length=255)  # Название валюты
        factor = models.DecimalField(max_digits=20, decimal_places=10)  # Коэффициент конвертации
        is_active = models.BooleanField(default=True)  # Активна ли валюта
        created_at = models.DateTimeField(auto_now_add=True)  # Дата создания
        updated_at = models.DateTimeField(auto_now=True)  # Дата обновления
    

  3. Модель ConversionFactor (коэффициенты конвертации):

    class ConversionFactor(models.Model):
        element = models.ForeignKey(Element, on_delete=models.CASCADE)  # Элемент иерархии
        uom = models.ForeignKey(UOM, on_delete=models.CASCADE)  # Единица измерения
        factor = models.DecimalField(max_digits=20, decimal_places=10)  # Коэффициент конвертации
        created_at = models.DateTimeField(auto_now_add=True)  # Дата создания
        updated_at = models.DateTimeField(auto_now=True)  # Дата обновления
    

Примеры использования

  1. Создание единиц измерения:

    # Создание единиц измерения
    kg = UOM.objects.create(
        code='kg',
        name='Килограмм',
        description='Единица измерения массы'
    )
    
    ton = UOM.objects.create(
        code='ton',
        name='Тонна',
        description='Единица измерения массы (1000 кг)'
    )
    

  2. Создание валют:

    # Создание валют
    rur = Currency.objects.create(
        code='RUR',
        name='Рубль',
        factor=1.0  # Базовая валюта
    )
    
    krur = Currency.objects.create(
        code='kRUR',
        name='Тысяча рублей',
        factor=0.001  # 1 RUR = 0.001 kRUR
    )
    

  3. Создание коэффициентов конвертации:

    # Создание коэффициентов конвертации для элемента иерархии
    ConversionFactor.objects.create(
        element=dpu_item,
        uom=kg,
        factor=1.0  # Базовая единица измерения
    )
    
    ConversionFactor.objects.create(
        element=dpu_item,
        uom=ton,
        factor=0.001  # 1 кг = 0.001 тонн
    )
    

  4. Использование в параметрах:

    # Создание параметра с поддержкой единиц измерения
    parameter = Parameter.objects.create(
        key='production',
        shortname='Производство',
        description='Параметр производства'
    )
    
    # Добавление измерения с поддержкой единиц измерения
    dpu_level.use_conversion_factors = True
    dpu_level.save()
    
    dimension = ParameterDimension.objects.create(
        parameter=parameter,
        hierarchy_level=dpu_level,
        key='dpu'
    )
    
    # Создание меры с поддержкой валют
    measure = ParameterMeasure.objects.create(
        parameter=parameter,
        type=MEASURE_TYPES_ENUM.FLOAT,
        shortname='Количество',
        description='Количество продукции',
        key='qty',
        supports_currency=True
    )
    

Особенности работы

  1. Порядок конвертации:
  2. Сначала применяется конвертация единиц измерения (UOM)
  3. Затем применяется конвертация валют (Currency)

  4. Коэффициенты конвертации:

  5. Для валют: коэффициент берется из модели Currency
  6. Для UOM: коэффициент берется из модели ConversionFactor для конкретного элемента иерархии

  7. Обработка ошибок:

  8. Если указана валюта для меры без поддержки валют, возвращается ошибка
  9. Если указана единица измерения для уровня без поддержки UOM, возвращается ошибка
  10. Если не найден коэффициент конвертации, возвращается ошибка

Уровни (Level) и поле is_hidden

  • Поле is_hidden определяет, скрыт ли уровень для обычных пользователей.
  • Только пользователи с разрешением view_hidden_levels видят скрытые уровни и поле is_hidden в ответе API.
  • Обычные пользователи не видят скрытые уровни и не получают поле is_hidden в выдаче.

Пример работы с is_hidden через API

Для пользователя с разрешением:

[
  { "id": 1, "shortname": "Видимый уровень", "is_hidden": false },
  { "id": 2, "shortname": "Скрытый уровень", "is_hidden": true }
]

Для обычного пользователя:

[
  { "id": 1, "shortname": "Видимый уровень" }
]


Подробная инструкция по работе с API: dev/api_usage.md

Важные моменты

  1. При работе с иерархиями важно правильно определить типы и уровни, так как они определяют структуру данных
  2. Параметры должны быть синхронизированы после создания или изменения их структуры
  3. Ключи мер в таблице фактов формируются как m_{measure.key}
  4. Ключи измерений в таблице фактов формируются как dim_{dimension.key}
  5. При работе с API ID меры всегда передается в виде строки
  6. Порядок измерений в ответе API может отличаться от порядка в таблице фактов

Работа с API: формирование URL-адресов

В проекте Planiqum используется многоуровневая маршрутизация для построения путей к API. Это важно учитывать при интеграции, написании тестов и разработке новых приложений.

Как формируется путь к endpoint

  1. В корневом urls.py проекта:
    path("core/", include("planiqum.core.urls")),
    
  2. В urls.py ядра:
    path("hierarchy/", include("planiqum.core.hierarchy.urls")),
    
  3. В urls.py приложения (например, hierarchy):
    path("api/", include(router.urls)),
    # router.register(r"levels", ...)
    

В результате, путь к endpoint для уровней будет:

/core/hierarchy/api/levels/

Аналогично формируются пути для других сущностей: - /core/hierarchy/api/items/ - /core/hierarchy/api/types/ - и т.д.

Важно: При написании тестов и интеграции с API всегда учитывайте все уровни вложенности в urls.py.

Примеры запросов

Получить список уровней:

GET /core/hierarchy/api/levels/

Получить список элементов:

GET /core/hierarchy/api/items/

Советы

  • Если endpoint не найден (404), проверьте структуру urls.py на всех уровнях.
  • Для DRF ViewSet-ов используйте router и обращайтесь по соответствующим путям.

Константы ключей стандартных уровней иерархии

Для избежания хардкода и унификации работы с уровнями иерархий в проекте используются константы, определённые в horizons.models:

from planiqum.core.horizons.models import (
    HORIZON_DAY_LEVEL_KEY,
    HORIZON_WEEK_LEVEL_KEY,
    HORIZON_MONTH_LEVEL_KEY,
    HORIZON_YEAR_LEVEL_KEY,
    HORIZON_QUARTER_LEVEL_KEY,
    HORIZON_HALF_YEAR_LEVEL_KEY,
)
  • HORIZON_DAY_LEVEL_KEY = "horizon__day"
  • HORIZON_WEEK_LEVEL_KEY = "horizon__week"
  • HORIZON_MONTH_LEVEL_KEY = "horizon__month"
  • HORIZON_YEAR_LEVEL_KEY = "horizon__year"
  • HORIZON_QUARTER_LEVEL_KEY = "horizon__quarter"
  • HORIZON_HALF_YEAR_LEVEL_KEY = "horizon__half_year"

Используйте эти константы при фильтрации, создании измерений, параметров и другой логике, связанной с уровнями иерархии.