Сквозные ДП¶
Концепция¶
Сквозной ДП (дополнительный параметр) — это специальный тип ExtParam, который автоматически "протягивает" значение из связанной задачи через цепочку лукапов (LookUpField). Вместо хранения собственного значения он отображает значение из задачи-источника.
Бизнес-сценарий¶
Предположим, у вас есть связанные задачи: - Счёт на оплату → связан с Договором → связан с Контрагентом - У контрагента есть поле ИНН - Вам нужно видеть ИНН контрагента прямо в карточке счёта
Традиционный подход: Создать ДП "ИНН контрагента" в счёте и вручную копировать значение. Проблема: При изменении ИНН в контрагенте нужно обновлять все счета.
Решение через Through: Создать сквозной ДП, который автоматически показывает актуальное значение ИНН из связанного контрагента.
Пример цепочки¶
Задача "Счёт" Задача "Договор" Задача "Контрагент"
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ [Договор] │───►│ [Контрагент] │───►│ [ИНН] │
│ (LookUpField) │ │ (LookUpField) │ │ (Text) │
│ ID=200 │ │ ID=300 │ │ ID=100 │
│ OrderID=1 │ │ OrderID=2 │ │ OrderID=3 │
│ │ │ │ │ = "7701234567" │
│ [ИНН контр.] │ └─────────────────┘ └─────────────────┘
│ (Through) │ ▲
│ ID=500 ═════════╪══════════════════════════════════════╝
└─────────────────┘ значение автоматически копируется
Структура данных¶
Таблица dbo.ExtParamTroughSettings¶
Хранит настройки цепочки для каждого сквозного ДП.
| Поле | Описание |
|---|---|
ExtParamID |
ID сквозного ДП (тип 'Through') |
TargetExtParamID |
ID ДП на очередном шаге цепочки |
OrderID |
Номер шага (1 = первый лукап от целевой задачи, max = источник данных) |
Пример настройки для сквозного ДП "ИНН контрагента" (ID=500):
| ExtParamID | TargetExtParamID | OrderID | Описание |
|---|---|---|---|
| 500 | 200 (Договор) | 1 | Шаг 1: первый лукап от счёта |
| 500 | 300 (Контрагент) | 2 | Шаг 2: второй лукап |
| 500 | 100 (ИНН) | 3 | Шаг 3: источник данных |
Таблица dbo.ExtParams¶
Справочник всех дополнительных параметров.
Типы ExtParam:
- Through — сквозной ДП (протягивает значение по цепочке)
- LookUpField — обычный лукап (один к одному)
- MultiSlctSubcatTasks — мультилукап (один ко многим)
- Text, Checkbox, NumericValue, Money, Date, Datetime — простые поля
Таблицы значений¶
- ExtParamValues — основные значения ДП (текст, числа, даты, bool)
- ExtParamValueSelectedTasks — выбранные задачи (для лукапов и мультилукапов)
- ExtParamSelectUsersValues — выбранные пользователи
- ExtParamSelectUsersValuesGroups — выбранные группы
- ExtParamSelectUsersValuesOrgUnits — выбранные оргединицы
- FileStorageFileToExtParamLinks — прикреплённые файлы к ДП
Правила валидности цепочки¶
Обязательные требования¶
- Минимум 2 записи в
ExtParamTroughSettingsдля каждого сквозного ДП - Максимум 3 записи (поддерживаются только 2-х и 3-х уровневые цепочки)
OrderIDдолжны быть непрерывны от 1 до count (без пропусков)- Все шаги кроме последнего (
OrderID < max) должны быть типаLookUpField - Последний шаг (
OrderID = max) — источник данных (любой тип, включая LookUpField) - Источник не может быть типа
Through(сквозной от сквозного) - Лукапы в цепочке НЕ должны быть мультилукапами (только
LookUpField, неMultiSlctSubcatTasks) - Запрещено: несколько сквозных в одной категории с одним
SourceExtParamID, но разной длиной цепочки
Примеры валидных конфигураций¶
2-уровневая цепочка:
Счёт [Договор] → Договор [Сумма]
OrderID: 1=Договор, 2=Сумма
3-уровневая цепочка:
Счёт [Договор] → Договор [Контрагент] → Контрагент [ИНН]
OrderID: 1=Договор, 2=Контрагент, 3=ИНН
Примеры НЕвалидных конфигураций¶
❌ Мультилукап в цепочке:
Счёт [Договоры (multi)] → Договор [Контрагент]
Проблема: недетерминированность — какой из договоров выбрать?
❌ Разные длины к одному источнику:
В категории "Счета":
- Сквозной ДП #1: Договор → ИНН (2 уровня)
- Сквозной ДП #2: Договор → Контрагент → ИНН (3 уровня)
Проблема: конфликт путей к одному источнику
❌ Пропуск в OrderID:
OrderID: 1, 3 (нет 2)
Проблема: невозможно построить полную цепочку
Основные процедуры¶
1. dbo.PullThroughExtParam¶
Назначение: Синхронизация значений сквозных ДП — копирование данных из задачи-источника в целевую задачу.
Параметры:
| Параметр | Описание |
|---|---|
@StartTaskID |
ID задачи-источника (откуда брать данные) |
@StartExtParamID |
ID ДП-источника |
@TargetTaskID |
ID целевой задачи (куда копировать) |
@TargetExtParamID |
ID сквозного ДП |
@DenormalizeF |
Вызывать ли денормализацию после синхронизации |
@DebugF |
Режим отладки |
Режимы работы:
| Входные параметры | Поведение |
|---|---|
@StartTaskID + @StartExtParamID указаны |
От конкретной задачи-источника вниз по цепочке |
@TargetTaskID + @TargetExtParamID указаны |
Только конкретная целевая пара |
| Все NULL | Полная пересинхронизация всех сквозных ДП |
Алгоритм работы:
- Построить список связей
(DstTaskID, DstExtParamID) → (SrcTaskID, SrcExtParamID)с помощью функцииTaskThroughPath - Дедуплицировать (если несколько путей ведут к одной цели, выбрать один детерминированно)
- Для каждой таблицы выполнить MERGE:
ExtParamSelectUsersValues— выбранные пользователиExtParamSelectUsersValuesGroups— выбранные группыExtParamSelectUsersValuesOrgUnits— выбранные оргединицыExtParamValueSelectedTasks— выбранные задачи (лукапы/мультилукапы)FileStorageFileToExtParamLinks— прикреплённые файлыExtParamValues— основные значения (текст, числа, даты)- Вызвать денормализацию изменённых задач (если
@DenormalizeF = 1)
Пример вызова:
-- Пересинхронизировать все сквозные ДП для конкретной задачи
EXEC dbo.PullThroughExtParam
@TargetTaskID = 12345,
@DenormalizeF = 1;
-- Обновить все зависимые задачи при изменении источника
EXEC dbo.PullThroughExtParam
@StartTaskID = 67890,
@StartExtParamID = 100,
@DenormalizeF = 1;
2. dbo.ExtParamTroughSettingsValidate¶
Назначение: Проверка корректности настроек сквозных ДП.
Параметры:
| Параметр | Описание |
|---|---|
@ExtParamID |
ID конкретного сквозного ДП (NULL = проверить все) |
@FixOrderIDGapsF |
Автоматически исправить пропуски в OrderID |
Проверяемые ошибки:
| Код | Severity | Описание |
|---|---|---|
NO_SETTINGS |
ERROR | Нет настроек цепочки |
CHAIN_TOO_SHORT |
ERROR | Меньше 2 элементов |
CHAIN_TOO_LONG |
ERROR | Больше 3 элементов |
ORDERID_NOT_FROM_1 |
ERROR | Цепочка не начинается с 1 |
ORDERID_GAPS |
ERROR | Пропуски в OrderID |
DUPLICATE_ORDERID |
ERROR | Дублирование OrderID |
TARGET_EP_NOT_FOUND |
ERROR | TargetExtParamID не существует |
INTERMEDIATE_NOT_LOOKUP |
ERROR | Промежуточный элемент не LookUpField |
MULTILOOKUP_IN_CHAIN |
ERROR | Мультилукап в цепочке |
SUBCAT_MISMATCH |
ERROR | Несогласованность категорий |
SELF_REFERENCE |
ERROR | Ссылка на себя |
MIXED_CHAIN_LENGTHS |
ERROR | Разные длины к одному источнику |
TARGET_IS_THROUGH |
WARNING | Источник тоже сквозной |
Примеры:
-- Проверить все сквозные ДП
EXEC dbo.ExtParamTroughSettingsValidate;
-- Проверить конкретный ДП
EXEC dbo.ExtParamTroughSettingsValidate @ExtParamID = 500;
-- Автоматически исправить пропуски в OrderID
EXEC dbo.ExtParamTroughSettingsValidate @FixOrderIDGapsF = 1;
3. dbo.ExtParamTroughSettingsSet¶
Назначение: Создание/изменение/удаление настроек сквозного ДП.
Параметры:
| Параметр | Описание |
|---|---|
@TargetExtParamID |
ID сквозного ДП (обязательный) |
@PathExtParamIDs |
Таблица с цепочкой ExtParamID (тип dbo.id_tbl_type) |
@ValidateOnlyF |
Только проверить без сохранения |
@DebugF |
Режим отладки |
Примеры:
-- Создать 2-уровневую цепочку: Договор → ИНН
DECLARE @path dbo.id_tbl_type;
INSERT INTO @path (ID) VALUES (200), (100);
EXEC dbo.ExtParamTroughSettingsSet
@TargetExtParamID = 500,
@PathExtParamIDs = @path;
-- Создать 3-уровневую цепочку: Договор → Контрагент → ИНН
DECLARE @path dbo.id_tbl_type;
INSERT INTO @path (ID) VALUES (200), (300), (100);
EXEC dbo.ExtParamTroughSettingsSet
@TargetExtParamID = 500,
@PathExtParamIDs = @path;
-- Удалить настройки (передать пустой путь)
DECLARE @path dbo.id_tbl_type;
EXEC dbo.ExtParamTroughSettingsSet
@TargetExtParamID = 500,
@PathExtParamIDs = @path;
-- Только проверить без сохранения
EXEC dbo.ExtParamTroughSettingsSet
@TargetExtParamID = 500,
@PathExtParamIDs = @path,
@ValidateOnlyF = 1;
4. dbo.TaskThroughPath (функция)¶
Назначение: Разворачивание цепочки связей для построения путей от задачи-источника к целевым задачам.
Возвращает: Таблицу с парами (DstTaskID, DstExtParamID) → (SrcTaskID, SrcExtParamID)
Алгоритм:
- Найти все сквозные ДП (ExtParamType = 'Through')
- Для каждого проверить валидность цепочки в ExtParamTroughSettings
- Рекурсивно пройти по цепочке:
- Начало: задача с целевым сквозным ДП + первый лукап (OrderID=1)
- Рекурсия:
- Взять
TargetExtParamIDдля текущегоOrderID - Найти в
ExtParamValueSelectedTasksзадачу, на которую ссылается этот лукап - Перейти к
OrderID + 1
- Взять
- Конец:
OrderID = max, найден источник данных
Используется: Внутри PullThroughExtParam для построения списка связей.
Настройка доступа¶
Доступ пользователя к сквозному ДП определяется как доступ к обычному полю: чтение, чтение+запись или нет доступа.
Параметр «Учитывать настройки доступа по целевому ДП»¶
Когда включен — доступ рассчитывается как логическое И настроек сквозного и целевого ДП (обе должны разрешать):
| Доступ к сквозному | Доступ к целевому | Результат |
|---|---|---|
| нет доступа | чтение+запись | не видит |
| чтение+запись | чтение+запись | чтение+запись |
| чтение+запись | нет доступа | не видит |
Когда выключен — чтение определяется только по сквозному ДП, но при отсутствии прав на целевой поле становится read-only:
| Доступ к сквозному | Доступ к целевому | Результат |
|---|---|---|
| нет доступа | чтение+запись | не видит |
| чтение+запись | чтение+запись | чтение+запись |
| чтение+запись | нет доступа | только чтение |
Подробнее: admin.md
Сквозной ДП типа «Таблица» — поведение UI (v2.267+)¶
Когда сквозной ДП ссылается на ДП-Таблицу в другой категории, UI-компонент таблицы загружает настройки через GET api/ep/tableSettings/{extParamId}/subcat/{subcatId}/{taskId}, передавая extParamId сквозного ДП (не ДП-источника). Это гарантирует, что бэкенд возвращает права в правильном контексте.
Ожидаемое поведение:
| Доступ к сквозному ДП | Результат в UI |
|---|---|
| Только чтение | Кнопки «Добавить строку», «Удалить», «Редактировать» скрыты |
| Чтение + запись | Кнопки отображаются |
До v2.267: фронт передавал ID ДП-источника → права игнорировались → кнопки отображались даже при read-only, вызывая ошибку доступа при попытке действия.
Сценарии использования¶
Создание новой цепочки¶
-- 1. Убедитесь что все ДП созданы:
-- - Счёт имеет ДП "Договор" (ID=200, тип LookUpField)
-- - Договор имеет ДП "Контрагент" (ID=300, тип LookUpField)
-- - Контрагент имеет ДП "ИНН" (ID=100, тип Text)
-- - Счёт имеет ДП "ИНН контрагента" (ID=500, тип Through)
-- 2. Создайте настройки цепочки
DECLARE @path dbo.id_tbl_type;
INSERT INTO @path (ID) VALUES
(200), -- OrderID=1: Договор
(300), -- OrderID=2: Контрагент
(100); -- OrderID=3: ИНН (источник)
EXEC dbo.ExtParamTroughSettingsSet
@TargetExtParamID = 500,
@PathExtParamIDs = @path;
-- 3. Проверьте настройки
EXEC dbo.ExtParamTroughSettingsValidate @ExtParamID = 500;
-- 4. Выполните первоначальную синхронизацию
EXEC dbo.PullThroughExtParam @DenormalizeF = 1;
Обработка изменений¶
Когда вызывать PullThroughExtParam:
-
При изменении лукапа в цепочке:
-- Пользователь изменил договор в счёте EXEC dbo.PullThroughExtParam @TargetTaskID = @TaskID_Счёт, @TargetExtParamID = 500, -- ИНН контрагента @DenormalizeF = 1; -
При изменении значения в источнике:
-- Изменили ИНН в контрагенте → обновить все зависимые счета EXEC dbo.PullThroughExtParam @StartTaskID = @TaskID_Контрагент, @StartExtParamID = 100, -- ИНН @DenormalizeF = 1; -
При создании новой задачи с лукапами:
-- Создали новый счёт и выбрали договор EXEC dbo.PullThroughExtParam @TargetTaskID = @NewTaskID, @DenormalizeF = 1;
Типичные ошибки и решения¶
Ошибка: MULTILOOKUP_IN_CHAIN¶
Причина: В цепочке используется мультилукап вместо обычного.
Пример проблемы:
Счёт [Договоры (multi)] → Договор [Контрагент] → Контрагент [ИНН]
Почему это плохо:
- Если счёт связан с 3 договорами, непонятно какой из них выбрать
- row_number() дедупликация даст случайный результат
- Значение сквозного ДП будет "прыгать" при каждом запуске
Решение: Заменить мультилукап на обычный лукап (один к одному) или изменить бизнес-логику.
Ошибка: MIXED_CHAIN_LENGTHS¶
Причина: Несколько сквозных ДП в одной категории ведут к одному источнику, но через цепочки разной длины.
Пример проблемы:
В категории "Счета":
- Сквозной ДП "ИНН-1": Договор → ИНН (2 уровня)
- Сквозной ДП "ИНН-2": Договор → Контрагент → ИНН (3 уровня)
Почему это плохо: - Путь к одному источнику должен быть единственным - Разные пути могут привести к конфликтам при синхронизации
Решение: Унифицировать длину цепочек — все сквозные ДП к одному источнику должны идти через одинаковый путь.
Производительность: слишком частые вызовы PullThroughExtParam¶
Проблема: Вызов PullThroughExtParam на каждое изменение может быть дорогим.
Решение:
- Отключить @DenormalizeF для единичных изменений
- Накапливать изменения и вызывать денормализацию пакетом
- Использовать фоновый job для периодической синхронизации
Диагностика¶
Проверка всех сквозных ДП¶
-- Найти все сквозные ДП с ошибками
EXEC dbo.ExtParamTroughSettingsValidate;
-- Результат покажет:
-- - ExtParamID
-- - ExtParamName
-- - ErrorCode
-- - ErrorMessage
-- - Severity (ERROR/WARNING)
Проверка конкретной цепочки¶
-- Посмотреть настройки для конкретного сквозного ДП
SELECT
epts.ExtParamID,
ep_through.ExtParamName AS ThroughParamName,
epts.OrderID,
epts.TargetExtParamID,
ep_target.ExtParamName AS TargetParamName,
ep_target.ExtParamType AS TargetParamType
FROM dbo.ExtParamTroughSettings epts
JOIN dbo.ExtParams ep_through ON epts.ExtParamID = ep_through.ExtParamID
JOIN dbo.ExtParams ep_target ON epts.TargetExtParamID = ep_target.ExtParamID
WHERE epts.ExtParamID = 500
ORDER BY epts.OrderID;
Отладка синхронизации¶
-- Запустить с @DebugF = 1 для просмотра промежуточных результатов
EXEC dbo.PullThroughExtParam
@TargetTaskID = 12345,
@DebugF = 1;
-- Увидите:
-- - Список построенных связей (DstTaskID → SrcTaskID)
-- - Количество обновлённых записей в каждой таблице
-- - Список задач для денормализации
Совместимость¶
- MS SQL Server: 2016+
- Использует: CTE, MERGE, table variables, window functions
Версия документа: 1.0
Дата: 2026-02-07