Как правильно проверить реквизит справочника перед записью документа в 1С 8.3

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

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

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

Исходная проблема: почему не работал код автора?

Рассмотрим первоначальный вариант кода, который пытался использовать автор темы в модуле управляемой формы документа:


&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    ПроверкаГруппыПоставщикаНаСервере();
КонецПроцедуры

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

В данном коде присутствуют сразу несколько критических ошибок, из-за которых проверка либо вообще не работала, либо работала некорректно:

  1. Область видимости переменной Отказ: Переменная Отказ объявлена в контексте клиентской процедуры ПередЗаписью. Попытка присвоить значение Отказ = Истина на сервере внутри процедуры ПроверкаГруппыПоставщикаНаСервере не имеет никакого смысла, так как серверная процедура "не знает" о существовании клиентской переменной, если она не была передана в качестве параметра.
  2. Использование процедуры вместо функции: Чтобы передать результат проверки с сервера на клиент, правильнее использовать функцию, возвращающую логическое значение (Истина или Ложь), либо передавать переменную Отказ по ссылке в качестве параметра серверной процедуры.
  3. Затирание признака отказа: Если в форме выполняется несколько различных проверок, простое присвоение Отказ = ПроверкаНаСервере() может сбросить ранее установленный признак Отказ = Истина. Правильно использовать конструкцию Если ПроверкаНаСервере() Тогда Отказ = Истина; КонецЕсли;.

Выясним, как пошагово исправить эти ошибки и сделать код работоспособным.

Шаг 1. Исправление логики клиент-серверного взаимодействия

Разберем рабочий вариант реализации проверки непосредственно в модуле формы. Перепишем код так, чтобы результат серверной проверки корректно возвращался на клиент и влиял на флаг отмены записи.

Для этого мы преобразуем серверную процедуру в функцию:


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

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

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

Архитектурные проблемы «быстрого» решения

Хотя предложенный выше код решает непосредственную задачу автора, с точки зрения профессиональной разработки в среде 1С он содержит серьезные архитектурные изъяны. Проанализируем их подробнее.

Проблема 1. Жесткое кодирование (Hardcoding)

Поиск группы по наименованию (НайтиПоНаименованию("Другие услуги")) или по коду (НайтиПоКоду("0000006")) крайне ненадежен. Если в режиме «1С:Предприятие» пользователь случайно переименует группу (например, исправит на "Другие услуги (архив)") или при переносе базы данных изменятся коды, этот код перестанет работать. Документ запишется без ошибок, нарушая бизнес-логику.

Как сделать правильно:

Проблема 2. Производительность и обращение «через точку»

На форумах часто советуют упростить код и сравнивать наименование напрямую:

Если Объект.ГруппаПоставщика.Наименование = "Другие услуги" Тогда

В чем опасность: Обращение к свойствам ссылочного реквизита через точку (ГруппаПоставщика.Наименование) заставляет платформу выполнять неявный SQL-запрос к базе данных, полностью считывая объект справочника со всеми его многочисленными реквизитами и табличными частями. Если в справочнике много полей, это приведет к серьезной потере производительности сервера приложений при каждой записи документа.

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

Пример использования БСП:


НаименованиеГруппы = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Объект.ГруппаПоставщика, "Наименование");
Если НаименованиеГруппы = "Другие услуги" Тогда
    // Действие при совпадении
КонецЕсли;

Этот метод считывает из базы данных исключительно поле "Наименование", минимизируя трафик между СУБД и сервером 1С.

Проблема 3. Выбор правильного места для проверки (Форма vs Объект)

Разработчик реализовал проверку в модуле формы. На первый взгляд это удобно, но таит в себе скрытую угрозу. Для анализа порядка срабатывания обработчиков формы и объекта можно применить инструмент для изучения событий платформы 1С.

В чем опасность: Код модуля формы выполняется только при интерактивных действиях пользователя (когда человек нажимает кнопку "Провести и закрыть"). Если документ будет создаваться или изменяться программно (например, при обмене данными, через групповую обработку, при загрузке с сайта или из мобильного приложения), код модуля формы вообще не выполнится! Проверка будет проигнорирована, и некорректные данные попадут в базу.

Как сделать правильно: Всю ключевую бизнес-логику, без которой документ считается невалидным, необходимо переносить в Модуль объекта документа, в предопределенный обработчик ПередЗаписью.

Рассмотрим пример реализации проверки в модуле объекта документа:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Исключаем проверку при обмене данными, если это необходимо
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Проверяем группу поставщика
    Если ЗначениеЗаполнено(ГруппаПоставщика) Тогда
        
        НаименованиеГруппы = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ГруппаПоставщика, "Наименование");
        Если НаименованиеГруппы = "Другие услуги" Тогда
            
            Отказ = Истина;
            
            // Используем стандарты вывода сообщений БСП
            ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
                "Для данного документа недопустимо выбирать группу поставщика 'Другие услуги'!",
                ЭтотОбъект,
                "ГруппаПоставщика"
            );
            
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Проблема 4. Дружелюбный интерфейс (Юзабилити)

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

Согласно стандартам разработки фирмы "1С", для вывода ошибок валидации необходимо использовать объект СообщениеПользователю. Он позволяет не только вывести понятный текст, но и визуально "привязать" его к конкретному элементу на форме (поле подсветится красной рамкой).

Рассмотрим пример создания сообщения с привязкой к реквизиту формы:


Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Пожалуйста, выберите другую группу поставщика. Группа 'Другие услуги' заблокирована для данного типа документов.";
Сообщение.Поле = "ГруппаПоставщика"; // Имя реквизита на форме
Сообщение.ПутьКДанным = "Объект";      // Путь к структуре данных формы
Сообщение.Сообщить();

При таком подходе пользователь сразу увидит, где именно он совершил ошибку, и сможет оперативно ее исправить.

Резюме

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

  1. Переносите критически важные проверки бизнес-логики в модуль объекта, чтобы они отрабатывали как при интерактивной, так и при программной записи.
  2. Избегайте жесткого кодирования строк и кодов в конфигурации. Используйте предопределенные элементы или настройки в константах и регистрах.
  3. Не злоупотребляйте обращениями через точку (Объект.Реквизит.Свойство) во избежание снижения производительности базы данных. Пользуйтесь методами БСП или явными запросами.
  4. Оформляйте вывод ошибок пользователю с помощью объекта СообщениеПользователю, привязывая сообщение к конкретным реквизитам формы.
← На главную