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

Атрибут типа «Запись». Объединение и фильтрация иерархических коллекций с помощью N3

Введение

Атрибут типа «Запись» хранит ID записей в связанном шаблоне (ссылки на них).

Зачастую требуется предоставлять пользователю для выбора списки записей, объединённые из нескольких шаблонов и отфильтрованные по сложным правилам. Например, когда данные находятся в иерархически связанных справочниках.

Здесь представлен пример формирования с помощью N3 списка поставщиков на основе трехуровневой иерархии товарных категорий и его фильтрации по данным текущего пользователя.

Прикладная задача

Менеджер по закупкам подбирает поставщиков товаров. Для этого он создаёт запрос с указанием необходимой товарной категории.

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

Предусмотрено два типа категорий: общие — регламентированные для поставщика, внутренние — регламентированные для менеджера по закупкам.

Для каждой внутренней категории указывается общая категория. Несколько внутренних категорий могут ссылаться на одну общую категорию.

Внутренние категории классифицируются по трём иерархическим уровням. Если для третьего уровня не нашлось поставщиков, поиск должен идти по второму уровню, а затем — по первому.

Кроме того, к запроса от менеджера должны иметь доступ только сотрудники поставщиков, указанных в запросе.

Исходные данные

Имеются шаблоны: Категории, Поставщики, Контакты, Запросы.

В шаблоне записи «Категории» хранится справочник всех категорий.

Атрибуты в шаблоне:

  • Общая категория
    • Описание: Ссылка на запись общей категории
    • Тип данных: запись
    • Связанный шаблон: Категории
    • Хранить несколько значений: флажок снят

В шаблоне записи «Поставщики» хранится карточка клиента или партнера.

Атрибуты в шаблоне:

  • Товарные категории
    • Описание: Коллекция общих категорий, с которыми работает поставщик
    • Тип данных: запись
    • Связанный шаблон: Категории
    • Хранить несколько значений: флажок установлен
  • Контакты
    • Описание: Коллекция контактных лиц поставщика
    • Тип данных: запись
    • Связанный шаблон: Контакты
    • Хранить несколько значений: флажок установлен

В шаблоне записи «Контакты» хранится контактное лицо в организации.

Атрибуты в шаблоне:

  • Сотрудник
    • Описание: Ссылка на аккаунт сотрудника
    • Тип данных: аккаунт
    • Связанный шаблон: Сотрудники
    • Хранить несколько значений: флажок снят
  • Ответственный
    • Описание: True, если сотрудник является ответственным менеджером для поставщика
    • Тип данных: логический

В шаблоне записи «Запросы» выполняются основные вычисления.

Атрибуты в шаблоне:

  • Категория ур. 1, Категория ур. 2, Категория ур. 3
    • Описание: Ссылка на категорию уровня 1, 2 или 3 для потребности
    • Тип данных: запись
    • Связанный шаблон: Категории
    • Хранить несколько значений: флажок снят
  • Доступные поставщики
    • Описание: Список доступных поставщиков
    • Тип данных: запись
    • Связанный шаблон: Поставщик
    • Хранить несколько значений: флажок установлен

Настройка вычислений

Логику можно разделить на две части:

  1. Сбор всех поставщиков по трём уровням категорий.
  2. Фильтрация полученного списка по текущему пользователю для настройки прав доступа.

1. Вычисление списка поставщиков

  1. Откройте для редактирования форму шаблона «Запросы».
  2. Поместите на форму атрибут «Доступные поставщики».
  3. Настройте свойства поля «Доступные поставщики»:

    • Представление: раскрывающийся список
    • Доступ: разрешить ввод
    • Фильтр: N3
    # Импортируем префиксы для работы со списками и объектами 
    @prefix object: <http://comindware.com/ontology/object#>.
    @prefix list: <http://www.w3.org/2000/10/swap/list#>.

    {
    # Убедитесь, что системные имена соответствуют именам в вашем приложении.
    # Получаем атрибуты для дальнейших вычислений
    ("Поставщики" "Товарныекатегории") object:findAttribute ?CounterpartyCategoriesAttribute.
    ("Запросы" "Категорияур3") object:findAttribute ?RequestCategoryLevel3Attribute.
    ("Запросы" "Категорияур2") object:findAttribute ?RequestCategoryLevel2Attribute.
    ("Запросы" "Категорияур1") object:findAttribute ?RequestCategoryLevel1Attribute.
    ("Категории" "Общаякатегория") object:findAttribute ?CommonCategoryAttribute.

    # Собираем коллекцию поставщиков, работающих с требуемой категорией третьего уровня
    # для текущей потребности (?item).
    from {
    ?item ?RequestCategoryLevel3Attribute ?RequestCategoryLevel3.
    # Получаем общую категорию для выбранной категории уровня 3.
    ?RequestCategoryLevel3 ?CommonCategoryAttribute ?CommonCategory3.
    # Ищем всех поставщиков (?Counterparties3), у которых товарная категория
    # совпадает с общей категорией (?CommonCategory3).
    ?Counterparties3 ?CounterpartyCategoriesAttribute ?CommonCategory3.
    }
    select ?Counterparties3 -> ?Counterparties3List.

    # Собираем коллекцию поставщиков, работающих с требуемой категорией второго уровня.
    from {
    ?item ?RequestCategoryLevel2Attribute ?RequestCategoryLevel2.
    ?RequestCategoryLevel2 ?CommonCategoryAttribute ?CommonCategory2.
    ?Counterparties2 ?CounterpartyCategoriesAttribute ?CommonCategory2.
    }
    select ?Counterparties2 -> ?Counterparties2List.

    # Собираем коллекцию поставщиков, работающих с требуемой категорией первого уровня.
    from {
    ?item ?RequestCategoryLevel1Attribute ?RequestCategoryLevel1.
    ?RequestCategoryLevel1 ?CommonCategoryAttribute ?CommonCategory1.
    ?Counterparties1 ?CounterpartyCategoriesAttribute ?CommonCategory1.
    }
    select ?Counterparties1 -> ?Counterparties1List.

    # Объединяем коллекции поставщиков категорий второго и третьего уровней.
    (?Counterparties3List ?Counterparties2List) list:append ?Counterparties32List.

    # Объединяем коллекции поставщиков категорий первого, второго и третьего уровней.
    (?Counterparties32List ?Counterparties1List) list:append ?RequestCounterpartiesList.

    # Возвращаем итоговую коллекцию категорий
    ?RequestCounterpartiesList list:member ?value.
    }

2. Фильтрация для роли

Чтобы предоставить доступ к запросам только ответственным сотрудникам указанных в запросе поставщиков в роль добавить тройку в фильтр аккаунтов для шаблона Запросы.

# Импортируем префиксы для работы со списками и объектами 
@prefix object: <http://comindware.com/ontology/object#>.
@prefix list: <http://www.w3.org/2000/10/swap/list#>.
{
# Убедитесь, что системные имена соответствуют именам в вашем приложении.
# Получаем атрибут «Доступные поставщики» из предыдущего вычисления
("Запросы" "Доступныепоставщики") object:findAttribute ?RequestCounterpartiesList.

# Получаем атрибуты для проверки связи поставщиков с текущим пользователем.
("Поставщики" "Контакты") object:findAttribute ?ContactsAttribute.
("Контакт" "Ответственный") object:findAttribute ?IsResponsibleAttribute.
("Контакт" "Сотрудник") object:findAttribute ?EmployeeAttribute.

# Получаем аккаунт текущего пользователя
cmw:securityContext cmw:currentUser ?user.

# Отбираем поставщиков,
# у которых текущий пользователь является ответственным
# по текущему запросу.
?item ?RequestCounterpartiesList ?RequestCounterparties.
?RequestCounterparties ?ContactsAttribute ?Contacts.
?Contacts ?IsResponsibleAttribute true.
?Contacts ?EmployeeAttribute ?user.
# Возвращаем текущего пользователя,
# если ему разрешено работать с текущим запросом.
?user -> ?value.
}

Тестирование

  1. Создайте тестовые записи в справочниках: Категории (с иерархией уровней), Поставщики, Контакты и несколько аккаунтов.
  2. Для нескольких поставщиков укажите общие категории и добавьте в Контакты аккаунты сотрудников. Для некоторых сотрудников установите флажок Ответственный и укажите свой аккаунт в поле Сотрудник.
  3. Создайте Запрос.
  4. В запросе укажите категории уровней 1—3.
  5. В поле Доступные поставщики должен появиться список поставщиков, работающих по выбранным категориям.
  6. Выберите поставщиков и сохраните запрос.
  7. Выйдите из системы и войдите с аккаунтом, который не указан в качестве ответственного лица ни у одного из поставщиков, выбранных на шаге 6.
  8. Откройте список всех запросов. Вы не должны видеть созданный ранее запрос.

  9. Создайте несколько тестовых аккаунтов.

  10. Создайте записи в шаблоне «Категории»

    - создайте общие категории; 
    - создайте категории уровней 1–3, указав для них разные **общие** категории.
  11. Создайте записи в шаблоне «Контакты»:

    • добавьте тестовые аккаунты в поле «Сотрудник».
    • для некоторых контактов установите флажок «Ответственный» и укажите свой аккаунт в поле «Сотрудник».
  12. Создайте записи в шаблоне «Поставщики»:

    • укажите в поле «Товарные категории» разные общие категории;
    • в «Контакты» добавьте записи из шаблона «Контакты».
  13. Создайте запись в шаблоне «Запрос».

  14. В запросе выберите категории уровней 1, 2 и 3.
  15. Откройте раскрывающийся список «Доступные поставщики». В нём должны отображается поставщики, работающие по выбранным категориям.
  16. Выберите поставщиков и сохраните запрос.
  17. Выйдите из системы и войдите под аккаунтом, который не указан как ответственное лицо ни у одного из поставщиков, выбранных в созданном ранее запросе.
  18. Откройте список всех «Запросов». Созданный ранее «Запрос» не должен отображаться для вас, так как ваш тестовый аккаунт не является ответственным лицом какого-либо поставщика по запросу.
К началу