При разработке ограничений прав доступа в системе 1С:Предприятие 8.3 начинающие программисты часто сталкиваются с ситуацией, когда добавленные в расширение реквизиты или табличные части «не видны» программно в модулях объектов. Типичный сценарий: вы добавили табличную часть к справочнику Пользователи, чтобы хранить список разрешенных складов, но при попытке проверить склад в документе ЗаказКлиента система выдает ошибку «Неверно указан реквизит». Рассмотрим подробно, почему это происходит и как правильно реализовать подобную проверку.
Проанализируем ситуацию: когда вы находитесь в модуле объекта документа (например, ЗаказКлиента), контекст ЭтотОбъект относится именно к документу. Чтобы получить данные из справочника Пользователи, недостаточно просто обратиться к нему по имени. Программа выдает ошибку доступа к реквизиту, потому что вы пытаетесь работать с метаданными или методами поиска, не определив тип данных и само значение ссылки.
Основная ошибка заключается в неправильном использовании метода Найти(). Разберем по шагам: если в качестве критерия поиска вы передаете строку "Склад", а в табличной части реквизит имеет тип СправочникСсылка.Склады, система никогда не найдет совпадение. Типы данных должны строго совпадать. Кроме того, поиск должен выполняться по значению, которое пользователь уже выбрал в документе.
Этот способ кажется наиболее простым, но он требует четкого понимания работы с типами. Если мы решили хранить список разрешенных складов в табличной части справочника Пользователи, нам нужно сначала получить ссылку на текущего пользователя, затем получить его объект (чтобы получить доступ к ТЧ) и выполнить поиск. Посмотрим на пример кода, который следует разместить в модуле объекта документа в процедуре ПередЗаписью:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// 1. Проверяем, что это проведение, а не просто сохранение
Если Не РежимПроведения = РежимПроведенияДокумента.Проведение Тогда
Возврат;
КонецЕсли;
// 2. Получаем ссылку на текущего пользователя
ТекущийПользовательСсылка = ПараметрыСеанса.ТекущийПользователь;
// 3. Получаем объект пользователя для доступа к табличной части
// Внимание: ПолучитьОбъект() - ресурсоемкая операция
ПользовательОбъект = ТекущийПользовательСсылка.ПолучитьОбъект();
// 4. Ищем склад из документа в табличной части пользователя "РазрешенныеСклады"
// Предположим, колонка в ТЧ называется "Склад"
СтрокаПоиска = ПользовательОбъект.РазрешенныеСклады.Найти(ЭтотОбъект.Склад, "Склад");
// 5. Если метод Найти() вернул Неопределено, значит склада в списке нет
Если СтрокаПоиска = Неопределено Тогда
ТекстСообщения = "У вас нет прав на проведение заказа по складу: " + ЭтотОбъект.Склад;
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения, , "Объект.Склад");
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Важный нюанс: В методе Найти(ЭтотОбъект.Склад, "Склад") первым параметром мы передаем именно ссылку на склад из текущего документа, а вторым — имя колонки табличной части в кавычках. Только при совпадении типов поиск будет успешным.
Рассмотрим более профессиональный подход. Использование ПолучитьОбъект() в модуле проведения документа крайне нежелательно, так как это заставляет сервер считывать все данные пользователя (включая контактную информацию, настройки и т.д.) ради одной проверки. Проанализируем ситуацию: нам нужно лишь узнать, существует ли запись в базе данных. Для этого идеально подходит объект Запрос.
Выполним поиск значения в табличной части через запрос. Это работает быстрее и потребляет меньше оперативной памяти:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ПользователиРазрешенныеСклады.Склад КАК Склад
|ИЗ
| Справочник.Пользователи.РазрешенныеСклады КАК ПользователиРазрешенныеСклады
|ГДЕ
| ПользователиРазрешенныеСклады.Ссылка = &ТекущийПользователь
| И ПользователиРазрешенныеСклады.Склад = &ВыбранныйСклад";
Запрос.УстановитьПараметр("ТекущийПользователь", ПараметрыСеанса.ТекущийПользователь);
Запрос.УстановитьПараметр("ВыбранныйСклад", ЭтотОбъект.Склад);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
// Запрещаем проведение
Отказ = Истина;
// Вывод сообщения пользователю...
КонецЕсли;
Этот метод считается лучшей практикой в разработке на платформе 1С:Предприятие 8. Мы обращаемся напрямую к таблице базы данных, минуя загрузку тяжелых объектов в память сервера.
Если в вашей системе ЗаказКлиента проводится часто, постоянные запросы к базе данных могут создать лишнюю нагрузку. Выясним, как это оптимизировать. Мы можем один раз при входе пользователя в программу считать все его доступные склады и сохранить их в ПараметрыСеанса.
ДоступныеСкладыПользователя с типом ФиксированныйМассив.УстановкаПараметровСеанса, заполним этот массив.Проанализируем пример проверки в модуле документа при использовании кэша:
Если ПараметрыСеанса.ДоступныеСкладыПользователя.Найти(ЭтотОбъект.Склад) = Неопределено Тогда
// Если склада нет в массиве, значит доступ запрещен
Отказ = Истина;
КонецЕсли;
Преимущество этого метода в том, что проверка выполняется мгновенно в оперативной памяти без обращения к дисковой подсистеме сервера базы данных.
Хотя автор темы выбрал хранение в табличной части справочника Пользователи, с точки зрения архитектуры 1С, более правильным решением будет создание Регистра сведений. Разберем преимущества:
Период).Пользователи в расширении, что снижает риск конфликтов при обновлениях УТ 11.Для проверки по регистру сведений используется метод РегистрыСведений.Название.Получить() или запрос, что работает эффективнее, чем чтение табличных частей справочников.
1. Исключение для администраторов. Не забудьте предусмотреть «черный ход» для пользователей с полными правами. В противном случае администратор не сможет исправить документ, если склад вдруг будет выведен из эксплуатации. Добавьте в начало проверки условие:
Если РольДоступна("ПолныеПрава") Тогда Возврат; КонецЕсли;
2. Вынос логики в Общий модуль. Проанализируем перспективу развития базы: сегодня вы ограничиваете склад в Заказе клиента, а завтра потребуется то же самое в Реализации товаров. Чтобы не дублировать код, создайте серверный общий модуль (например, _РаботаСДоступомСервер) и вынесите проверку туда.
3. Использование типового RLS. В конфигурации УТ 11.4 (Управление торговлей) уже встроен мощный механизм RLS (Access Restriction). Прежде чем писать код, убедитесь, что типовые «Группы доступа» не позволяют решить вашу задачу стандартными средствами. Программные доработки поверх RLS могут привести к труднодиагностируемым ошибкам прав доступа.
Подводя итог: чтобы система «увидела» реквизит табличной части, необходимо обращаться к нему через Запрос или предварительно полученный Объект ссылки. Передача строковых названий вместо ссылок на объекты — самая частая причина ошибок начинающих разработчиков. Используйте типизацию данных правильно, и ваши расширения будут работать стабильно.