При ведении многофирменного учета в одной информационной базе 1С:Управление производственным предприятием (УПП) ред. 1.3 часто прибегают к разделению справочника Номенклатура по иерархическому признаку — создают корневые папки под каждую организацию холдинга. Однако на практике этот подход порождает серьезные проблемы. Когда менеджеры оформляют документы закупки или продажи, они пользуются полнотекстовым поиском или поиском по подстроке с отключенной иерархией. В результате в список выбора попадают одноименные позиции из чужих папок. Пользователи ошибаются, выбирают не те элементы, а бухгалтерам в конце месяца приходится вручную исправлять пересортицу, чтобы закрыть регистры и распределить затраты.
Попытки решить эту проблему «в лоб» через стандартные механизмы отборов 1С часто приводят к критическому снижению производительности системы. В этой статье мы подробно разберем, почему стандартные решения тормозят на больших базах данных, и рассмотрим эффективные архитектурные и программные методы, которые помогут ограничить выбор номенклатуры без потери скорости работы системы.
---
Выясним причину, по которой классическая установка отбора по списку папок в динамическом списке формы выбора работает медленно. Когда мы пишем условие вида Ссылка В ИЕРАРХИИ (&СписокПапок), платформа 1С транслирует этот запрос в СУБД. На уровне базы данных SQL-сервер вынужден выполнять сложные рекурсивные вычисления, обходя дерево иерархии справочника снизу вверх для каждой записи. Если справочник Номенклатура содержит десятки или сотни тысяч элементов, а глубина вложенности папок составляет более 3–4 уровней, СУБД не может эффективно использовать индексы. Время ожидания отклика формы выбора или подбора может вырасти до нескольких десятков секунд, что делает работу пользователей невыносимой.
Для решения этой проблемы нам необходимо уйти от использования ресурсоемкого оператора СУБД В ИЕРАРХИИ в динамических списках и заменить его быстрыми индексными запросами. Ниже мы детально разберем четыре практических способа реализации этой задачи.
---
Суть метода заключается в том, чтобы перенести тяжелую вычислительную задачу по обходу дерева групп с СУБД на сервер приложений 1С. Вместо того чтобы заставлять форму выбора динамически вычислять иерархию при каждой активизации строки, мы один раз при открытии формы сформируем плоский массив всех разрешенных элементов и передадим его в отбор.
Проанализируем пошаговый алгоритм реализации этого метода:
СписокЗначений.Ссылка ВСписке &ПлоскийСписок.Рассмотрим пример кода, который необходимо разместить в модуле формы подбора номенклатуры:
// Процедура инициализирует быстрый отбор по плоскому списку элементов
Процедура УстановитьОптимальныйОтборПоОрганизации(ТекущаяОрганизация) Экспорт
// Шаг 1. Находим корневую папку организации
РодительскаяПапка = Справочники.Номенклатура.НайтиПоРеквизиту("ОрганизацияВладелец", ТекущаяОрганизация);
Если РодительскаяПапка.Пустая() Тогда
Возврат;
КонецЕсли;
// Шаг 2. Запросом разворачиваем иерархию папки в плоский список элементов
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В ИЕРАРХИИ(&РодительскаяПапка)
| И Номенклатура.ЭтоГруппа = ЛОЖЬ";
Запрос.УстановитьПараметр("РодительскаяПапка", РодительскаяПапка);
ПлоскийСписокСсылок = Новый СписокЗначений;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ПлоскийСписокСсылок.Добавить(Выборка.Ссылка);
КонецЦикла;
// Шаг 3. Устанавливаем быстрый отбор по списку значений в динамическом списке формы
Отбор = Список.Отбор;
ЭлементОтбора = Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
ЭлементОтбора.ПравоеЗначение = ПлоскийСписокСсылок;
ЭлементОтбора.Использование = Истина;
КонецПроцедуры
Для СУБД поиск по условию IN (Список_Идентификаторов) является базовой и крайне быстрой операцией, так как поиск идет напрямую по первичному кластеризованному индексу таблицы номенклатуры. Скорость открытия подбора при этом увеличивается в разы.
---
Если справочник номенклатуры содержит сотни тысяч элементов, то плоский список значений в оперативной памяти может получиться избыточно большим, что тоже начнет снижать производительность. В этом случае идеальным решением будет создание облегченного вспомогательного регистра сведений.
Разберем по шагам этот архитектурный прием:
НоменклатураОрганизаций.Номенклатура (тип СправочникСсылка.Номенклатура, обязательно устанавливаем свойство Индексировать) и измерение Организация (тип СправочникСсылка.Организации).Номенклатура в обработчике события ПриЗаписи прописываем автоматическое определение папки-родителя и запись связи элемента с конкретной организацией в этот регистр.Посмотрим на пример кода автоматического заполнения регистра при сохранении карточки номенклатуры:
Процедура ПередЗаписью(Отказ)
// Определение организации-владельца по иерархии папок
ТекущийРодитель = ЭтотОбъект.Родитель;
ОрганизацияПапки = Справочники.Организации.ПустаяСсылка();
// Поднимаемся по дереву групп вверх до самого корня
Пока НЕ ТекущийРодитель.Пустая() Цикл
// Проверяем, привязана ли папка к конкретной организации (например, по имени или реквизиту)
Если ТекущийРодитель.Наименование ПОДОБНО "%ООО Ромашка%" Тогда
ОрганизацияПапки = Справочники.Организации.НайтиПоНаименованию("ООО Ромашка");
Прервать;
КонецЕсли;
ТекущийРодитель = ТекущийРодитель.Родитель;
КонецЦикла;
ЭтотОбъект.ДополнительныеСвойства.Вставить("ОрганизацияВладелец", ОрганизацияПапки);
КонецПроцедуры
Процедура ПриЗаписи(Отказ)
Если ЭтотОбъект.ЭтоГруппа Тогда
Возврат;
КонецЕсли;
ОрганизацияВладелец = Неопределено;
Если ЭтотОбъект.ДополнительныеСвойства.Свойство("ОрганизацияВладелец", ОрганизацияВладелец) И ЗначениеЗаполнено(ОрганизацияВладелец) Тогда
// Записываем связь в регистр сведений
МенеджерЗаписи = РегистрыСведений.НоменклатураОрганизаций.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Номенклатура = ЭтотОбъект.Ссылка;
МенеджерЗаписи.Организация = ОрганизацияВладелец;
МенеджерЗаписи.Записать(Истина);
КонецЕсли;
КонецПроцедуры
Теперь в динамическом списке формы подбора или выбора достаточно изменить текст запроса. Мы делаем левое соединение справочника с регистром сведений НоменклатураОрганизаций и накладываем простейшее условие равенства по индексированному полю Организация. СУБД выполнит такое соединение мгновенно.
---
Часто пользователи не открывают форму подбора, а начинают вводить первые буквы наименования прямо в ячейку табличной части документа. В этот момент срабатывает стандартный системный поиск по всей базе. Чтобы ограничить этот ввод на уровне платформы 1С 8.3, задействуем обработчик в модуле менеджера справочника.
Проанализируем пример перехвата ввода по строке в модуле менеджера справочника Номенклатура:
Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)
// Проверяем, передан ли контекст организации из документа через параметры выбора
ОрганизацияДокумента = Неопределено;
Если Параметры.Свойство("Отбор") И Параметры.Отбор.Свойство("ОрганизацияВладелец", ОрганизацияДокумента) Тогда
// Отключаем стандартный поиск платформы
СтандартнаяОбработка = Ложь;
// Сформируем собственный оптимизированный запрос для поиска по подстроке с учетом папки организации
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 30
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НоменклатураОрганизаций КАК СвязиОрганизаций
| ПО Номенклатура.Ссылка = СвязиОрганизаций.Номенклатура
|ГДЕ
| СвязиОрганизаций.Организация = &ОрганизацияДокумента
| И Номенклатура.Наименование ПОДОБНО &СтрокаПоиска
| И Номенклатура.ЭтоГруппа = ЛОЖЬ";
Запрос.УстановитьПараметр("ОрганизацияДокумента", ОрганизацияДокумента);
// Добавляем к поисковому слову маску поиска по подстроке
Запрос.УстановитьПараметр("СтрокаПоиска", "%" + Параметры.СтрокаПоиска + "%");
СписокВыбора = Новый СписокЗначений;
Выборка = Запрос.Target = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СписокВыбора.Добавить(Выборка.Ссылка, Выборка.Наименование);
КонецЦикла;
ДанныеВыбора = СписокВыбора;
КонецЕсли;
КонецПроцедуры
Для того чтобы данный механизм работал автоматически, необходимо настроить связь реквизитов формы. В свойствах реквизита Номенклатура табличной части документа найдите пункт «Связи параметров выбора» и добавьте строчку: Отбор.ОрганизацияВладелец(Объект.Организация). Теперь платформа сама будет передавать выбранную в документе организацию во все обработчики выбора номенклатуры.
---
Если требуется стопроцентное исключение человеческого фактора на уровне прав доступа платформы, то самым надежным решением станет настройка механизма RLS (Row Level Security). При правильном проектировании шаблона ограничений производительность не пострадает.
Рассмотрим шаблон текста ограничения для роли «Менеджер по продажам» на чтение справочника Номенклатура:
Номенклатура ИЗ Справочник.Номенклатура КАК Номенклатура
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НоменклатураОрганизаций КАК СвязиОрганизаций
ПО Номенклатура.Ссылка = СвязиОрганизаций.Номенклатура
ГДЕ
СвязиОрганизаций.Организация В (&ДоступныеОрганизацииПользователя)
При использовании этого шаблона СУБД будет аппаратно отсекать все неразрешенные элементы на уровне любого запроса, включая отчеты, списки выбора и формы подбора. Пользователь физически не сможет увидеть номенклатуру чужой организации, даже если попытается найти ее по прямому коду.
---
В случаях, когда жесткие блокировки мешают оперативной работе пользователей, мы можем применить механизм выдачи предупреждений без блокировки ввода. Для этого при формировании списка выбора в процедуре ОбработкаПолученияДанныхВыбора мы можем воспользоваться свойством Предупреждение элементов возвращаемого списка.
Посмотрим на пример реализации предупреждения:
ЭлементСписка = СписокВыбора.Добавить(Выборка.Ссылка, Выборка.Наименование);
Если Выборка.ОрганизацияНоменклатуры <> ОрганизацияДокумента Тогда
ЭлементСписка.Предупреждение = "Внимание! Эта номенклатура принадлежит организации "
+ Выборка.ОрганизацияНоменклатуры.Наименование + "! Вы уверены, что хотите выбрать её?";
КонецЕсли;
При попытке выбрать такую позицию из выпадающего меню ввода по строке, платформа покажет пользователю интерактивное диалоговое окно с вопросом. Это предотвращает случайные ошибочные клики, но оставляет возможность провести документ в экстренных ситуациях.
---
Мы детально проанализировали проблему путаницы номенклатуры при многофирменном учете в 1С:УПП 1.3 и разобрали технические способы ее решения. В качестве временной меры (до момента глобального проведения проекта по нормализации нормативно-справочной информации силами подрядчиков) мы рекомендуем:
ОбработкаПолученияДанныхВыбора в модуле менеджера справочника для исключения ошибок при ручном вводе символов с клавиатуры в табличные части документов.