Контекстно-зависимые наименования объектов в 1С: как реализовать поддержку «диалектов» без потери производительности

Программист 1С v8.3 (Управляемые формы) 1С:Документооборот Управленческий учет IT и автоматизация бизнеса
← На главную

В практике разработки крупных корпоративных систем на платформе 1С:Предприятие 8.3 часто возникает задача локализации данных. Стандартная мультиязычность платформы отлично справляется, когда нужно показать наименование справочника на русском или английском языке в зависимости от локали интерфейса пользователя. Однако бизнес-заказчики часто ставят более сложные задачи: показывать разные наименования одного и того же объекта (например, номенклатуры или контрагента) в зависимости от контекста — организации, проекта, склада или местоположения. Такие контекстные переводы внутри одного языка разработчики называют «диалектами».

Рассмотрим классический пример: в документе поступления материалов для Организации №1 товар должен отображаться как «Table», для Организации №2 — как «Desk», а для Организации №3 — как «Platen». При этом физически это один и тот же элемент справочника Номенклатура. В данной статье мы подробно разберем, почему стандартные механизмы получения представлений пасуют перед этой задачей, и рассмотрим эффективные архитектурные решения этой проблемы.

Почему стандартный механизм получения представлений не работает «в лоб»?

Разработчики часто пытаются решить задачу контекстных наименований с помощью стандартных событий менеджера объекта: ОбработкаПолученияПолейПредставления и ОбработкаПолученияПредставления. Давайте проанализируем, почему этот путь ведет в тупик.

Во-первых, процедура ОбработкаПолученияПолейПредставления работает строго в контексте реквизитов самого объекта. Платформа не позволяет добавить в список полей представления реквизиты, которых физически нет в метаданных справочника (например, данные из независимого регистра сведений или реквизиты документа-владельца).

Во-вторых, существует критическое ограничение производительности. Согласно стандартам разработки фирмы «1С» (стандарт #std746), в обработчике ОбработкаПолученияПредставления категорически запрещено выполнять запросы к базе данных или обращаться к реквизитам ссылочных типов через точку (что приводит к неявному считыванию всего объекта из базы).

Поскольку система вызывает этот обработчик непрерывно (при выводе каждой строки динамического списка, при автоподборе в полях ввода, в отчетах и журналах), выполнение даже простого запроса к регистру сведений внутри этой процедуры моментально «парализует» работу СУБД при увеличении объема данных.

Ниже мы разберем несколько профессиональных сценариев решения этой задачи без ущерба для быстродействия системы.

Решение 1. Кастомизация динамических списков через произвольный запрос (Best Practice для форм)

Если контекстное наименование («диалект») необходимо отображать в формах списков документов или формах выбора справочников, наиболее элегантным решением является обход стандартного представления ссылки с помощью настройки динамического списка.

Разберем этот метод по шагам:

  1. Создадим независимый регистр сведений ра_ДиалектыНаименований со следующей структурой:
    • Измерения: Объект (ЛюбаяСсылка), Контекст (например, СправочникСсылка.Организации).
    • Ресурс: Представление (Строка).
  2. В форме выбора номенклатуры или форме списка переведем основной динамический список в режим произвольного запроса.
  3. Напишем запрос, который соединяет основную таблицу справочника с нашим регистром сведений по левому соединению с учетом контекста:

ВЫБРАТЬ
    СправочникНоменклатура.Ссылка КАК Ссылка,
    СправочникНоменклатура.Код КАК Код,
    ВЫБОР
        КОГДА ра_ДиалектыНаименований.Представление ЕСТЬ НЕ NULL 
            ТОГДА ра_ДиалектыНаименований.Представление
        ИНАЧЕ СправочникНоменклатура.Наименование
    КОНЕЦ КАК ПредставлениеНоменклатуры
ИЗ
    Справочник.Номенклатура КАК СправочникНоменклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ра_ДиалектыНаименований КАК ра_ДиалектыНаименований
        ПО ра_ДиалектыНаименований.Объект = СправочникНоменклатура.Ссылка
        И ра_ДиалектыНаименований.Контекст = &ТекущийКонтекстДиалекта

Теперь нам необходимо передать контекст (например, организацию из документа) в параметры динамического списка при открытии формы выбора. Реализуем это в модуле формы документа, откуда вызывается выбор:


&НаКлиенте
Процедура НоменклатураНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("ТекущийКонтекстДиалекта", Объект.Организация);
    
    ОткрытьФорму("Справочник.Номенклатура.ФормаВыбора", ПараметрыФормы, Элемент);
    
КонецПроцедуры

В модуле формы выбора справочника Номенклатура примем этот параметр и установим его в динамический список:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    Контекст = Неопределено;
    Если Параметры.Свойство("ТекущийКонтекстДиалекта", Контекст) Тогда
        Список.Параметры.УстановитьЗначениеПараметра("ТекущийКонтекстДиалекта", Контекст);
    Иначе
        // Если контекст не передан, устанавливаем пустую ссылку для избежания ошибок в запросе
        Список.Параметры.УстановитьЗначениеПараметра("ТекущийКонтекстДиалекта", Справочники.Организации.ПустаяСсылка());
    КонецЕсли;
    
КонецПроцедуры

В свойствах элемента формы списка (таблицы) снимем видимость со стандартного поля Ссылка (или Наименование) и выведем наше вычисляемое поле ПредставлениеНоменклатуры. Этот подход работает чрезвычайно быстро, так как соединение таблиц происходит на уровне СУБД за один шаг выполнения запроса динамического списка.

Решение 2. Обход ограничений через параметры сеанса и кеширование данных

Что делать, если контекстное наименование должно выводиться абсолютно во всех полях ввода системы, включая платформенный автоподбор (при вводе строки)? В этом случае нам придется задействовать обработчик получения представления, но обойти ограничения производительности с помощью Параметров сеанса и кеширования данных в оперативной памяти.

Рассмотрим по шагам реализацию этого механизма:

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

Создадим общий модуль ра_ЛокализацияПовтИсп со свойством возвращаемых значений «На время вызова» или «На время сеанса». Напишем функцию получения кэша:


Функция ПолучитьТаблицуДиалектов(ТекущийКонтекст) Экспорт
    
    ТаблицаСоответствий = Новый Соответствие;
    
    Если Не ЗначениеЗаполнено(ТекущийКонтекст) Тогда
        Возврат ТаблицаСоответствий;
    КонецЕсли;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ра_ДиалектыНаименований.Объект КАК Объект,
        |	ра_ДиалектыНаименований.Представление КАК Представление
        |ИЗ
        |	РегистрСведений.ра_ДиалектыНаименований КАК ра_ДиалектыНаименований
        |ГДЕ
        |	ра_ДиалектыНаименований.Контекст = &Контекст";
    
    Запрос.УстановитьПараметр("Контекст", ТекущийКонтекст);
    РезультатЗапроса = Запрос.Выполнить();
    
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Пока Выборка.Следующий() Цикл
            ТаблицаСоответствий.Вставить(Выборка.Объект, Выборка.Представление);
        КонецЦикла;
    КонецЕсли;
    
    Возврат ТаблицаСоответствий;
    
КонецФункции

Теперь в модуле менеджера справочника Номенклатура пропишем безопасную логику получения представлений:


Процедура ОбработкаПолученияПолейПредставления(Поля, СтандартнаяОбработка)
    
    // Оставляем стандартные поля, так как само представление мы сформируем динамически
    СтандартнаяОбработка = Истина;
    
КонецПроцедуры

Процедура ОбработкаПолученияПредставления(Данные, Представление, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // Получаем контекст из параметров сеанса без запроса к БД
    ТекущийКонтекст = ПараметрыСеанса.ра_ТекущийКонтекстДиалекта;
    
    // Извлекаем кэшированную таблицу соответствий для текущего контекста
    ТаблицаКэша = ра_ЛокализацияПовтИсп.ПолучитьТаблицуДиалектов(ТекущийКонтекст);
    
    СсылкаНаОбъект = Данные.Ссылка;
    НайденноеПредставление = ТаблицаКэша.Получить(СсылкаНаОбъект);
    
    Если НайденноеПредставление <> Неопределено Тогда
        Представление = НайденноеПредставление;
    Иначе
        // Если для данного контекста нет перевода, выводим стандартное наименование
        Представление = Данные.Наименование;
    КонецЕсли;
    
КонецПроцедуры

Важное предупреждение: Использование параметров сеанса требует аккуратности. При переключении пользователя между вкладками разных документов параметр сеанса может перезаписываться. Данный метод идеально подходит для специализированных рабочих мест (АРМ) или в тех конфигурациях, где контекст диалекта жестко привязан к текущему пользователю или его подразделению.

Решение 3. Архитектурный паттерн «Номенклатура контрагентов» (Подход ERP)

Давайте обратим внимание на то, как аналогичная задача решена разработчиками фирмы «1С» в крупных тиражных решениях (например, в 1С:ERP Управление предприятием или 1С:Управление торговлей). Там часто требуется сопоставлять внутреннюю номенклатуру предприятия с наименованиями поставщиков или покупателей.

Вместо изменения глобального механизма получения представлений ссылок применяется схема дублирования данных на уровне документов:

Этот подход гарантирует абсолютную стабильность системы, независимость от кэширования параметров сеанса и максимальную скорость работы СУБД, так как данные хранятся непосредственно в табличной части документа и не требуют динамических вычислений «на лету» при каждом выводе строки.

Решение 4. Вывод контекстных наименований в отчетах СКД

Отдельного внимания заслуживает построение аналитической отчетности по документам с использованием контекстных представлений. Если строить отчет в Системе компоновки данных (СКД) и полагаться на стандартные представления ссылок, то при использовании ОбработкаПолученияПредставления отчет на десятках тысяч строк будет формироваться неприемлемо долго.

Для СКД правильным и самым быстрым решением является перенос логики соединения таблиц непосредственно в сам текст запроса схемы компоновки данных. Проанализируем пример запроса для СКД:


ВЫБРАТЬ
    МатериалыОбороты.Организация КАК Организация,
    МатериалыОбороты.Номенклатура КАК Номенклатура,
    ВЫБОР
        КОГДА ра_ДиалектыНаименований.Представление ЕСТЬ НЕ NULL 
            ТОГДА ра_ДиалектыНаименований.Представление
        ИНАЧЕ МатериалыОбороты.Номенклатура.Наименование
    КОНЕЦ КАК НоменклатураПредставление,
    МатериалыОбороты.КоличествоОборот КАК Количество
ИЗ
    РегистрНакопления.ра_Материалы.Обороты КАК МатериалыОбороты
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ра_ДиалектыНаименований КАК ра_ДиалектыНаименований
        ПО ра_ДиалектыНаименований.Объект = МатериалыОбороты.Номенклатура
        И ра_ДиалектыНаименований.Контекст = МатериалыОбороты.Организация

В настройках СКД на вкладке «Наборы данных» для поля Номенклатура мы можем указать выражение представления как НоменклатураПредставление. В результате пользователи получат быстрый отчет, группировки которого будут отображаться с учетом контекста организации, а СУБД выполнит левое соединение мгновенно.

Подведем итоги

Выбор конкретного метода реализации контекстно-зависимых наименований зависит от архитектуры вашей базы данных и требований к интерфейсу:

← На главную