Почему проверка заполнения реквизитов в 1С срабатывает только при записи из формы и как это исправить?

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

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

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

Анализ проблемы: почему "чистый" код игнорирует проверку заполнения?

Рассмотрим классическую ситуацию, с которой столкнулся автор темы на форуме. В конфигурации есть справочник Контрагенты. Для реквизита ОГРН установлено свойство проверки заполнения — «Выдавать ошибку».

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

Давайте посмотрим на программный код создания контрагента, который привел к появлению дублей без заполненного ОГРН:


Контр = Справочники.Контрагенты.СоздатьЭлемент();

Контр.НаименованиеПолное = НаименованиеПолное;
Контр.СМО = Истина;
Контр.СМО_ОМС = Истина;

Если ПустаяСтрока(Контр.Наименование) Тогда
    Контр.Наименование = НаименованиеПолное;
КонецЕсли;

Контр.Регион = Справочники.АдминистративныеТерриторииРФИЗарубежья.НайтиПоКоду(Территория, Ложь);

// Запись происходит без ошибок, несмотря на пустой ОГРН!
Контр.Записать();

Почему платформа пропустила эту запись? Ответ кроется в синтакс-помощнике и базовой архитектуре управляемого приложения.

Философия платформы: интерактивный ввод против программной обработки

Разработчики платформы «1С:Предприятие» изначально разделили логику работы с данными на интерактивную (пользовательскую) и программную (технологическую):

  1. Интерактивный ввод данных. Когда пользователь работает с формой элемента, платформа должна максимально помогать ему избегать ошибок. Именно для этого предназначено свойство реквизита «Проверка заполнения» = «Выдавать ошибку». Запуск этой проверки инициируется расширением управляемой формы при попытке интерактивной записи.
  2. Программная обработка данных. Метод Записать() объекта предназначен для непосредственной записи данных в СУБД. На этом уровне платформа по умолчанию не делает проверок заполнения. Представьте себе фоновый обмен данными, загрузку контрагентов из клиент-банка или импорт "грязной" базы данных из другой учетной системы. Если бы метод Записать() спотыкался на каждом незаполненном реквизите, интеграция систем превратилась бы в кошмар, а групповые обработки зависали бы на полпути.

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

Разбор работы события "ОбработкаПроверкиЗаполнения"

Для более гибкого управления проверками в модуле объекта предусмотрен предопределенный обработчик ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты). Рассмотрим пример кода из модуля объекта справочника, который динамически управляет проверкой реквизита ОГРН:


Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
    
    МассивНепроверяемыхРеквизитов = Новый Массив;
    
    // Если контрагент не относится к медицинским или страховым организациям,
    // ОГРН проверять не требуется
    Если Не СМО И Не СМО_ОМС И Не СМО_ДМС И Не МО И Не РЖД Тогда
        МассивНепроверяемыхРеквизитов.Добавить("ОГРН");
    КонецЕсли;
    
    // Удаляем реквизит из списка проверяемых платформой
    МИС_ЗаполнениеДокументов.УдалитьНепроверяемыеРеквизитыИзМассива(ПроверяемыеРеквизиты, МассивНепроверяемыхРеквизитов);
    
КонецПроцедуры

Этот обработчик работает великолепно при записи из формы. Но важно понимать: событие ОбработкаПроверкиЗаполнения не вызывается само по себе при вызове метода Записать()! Оно срабатывает только тогда, когда запись инициирована из формы элемента, либо когда в коде явно вызван метод проверки.

Решение №1: Как запустить проверку заполнения программно?

Если вам необходимо программно создать объект и при этом гарантировать, что все реквизиты заполнены в соответствии с правилами, настроенными в метаданных и модуле объекта, вам следует явно вызвать метод ПроверитьЗаполнение() перед записью.

Давайте перепишем наш программный код создания контрагента правильно:


Контр = Справочники.Контрагенты.СоздатьЭлемент();

Контр.НаименованиеПолное = НаименованиеПолное;
Контр.СМО = Истина;
Контр.СМО_ОМС = Истина;

Если ПустаяСтрока(Контр.Наименование) Тогда
    Контр.Наименование = НаименованиеПолное;
КонецЕсли;

Контр.Регион = Справочники.АдминистративныеТерриторииРФИЗарубежья.НайтиПоКоду(Территория, Ложь);

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

При вызове метода ПроверитьЗаполнение() платформа выполнит следующие действия:

Решение №2: Жесткие проверки в транзакции записи (событие ПередЗаписью)

Метод с ПроверитьЗаполнение() отлично подходит, когда мы контролируем создание объектов в своем собственном коде. Но что делать, если объекты могут изменяться сторонними обработками, загружаться через стандартные планы обмена или изменяться групповым изменением реквизитов в списке?

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

Проанализируем пример реализации жесткой проверки заполнения ОГРН напрямую в модуле объекта:


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

Преимущества этого подхода:

Почему снятие пометки удаления в списке не вызывает стандартную проверку?

Автор темы также заметил странность: когда пользователь снимает пометку удаления непосредственно из списка справочника, система делает это "без вопросов", даже если ОГРН не заполнен. Выясним причину этого поведения.

Когда мы нажимаем кнопку «Установить/Снять пометку удаления» в динамическом списке, платформа выполняет оптимизированную операцию. Под капотом вызывается метод объекта УстановитьПометкуУдаления(Ложь). Этот метод производит запись объекта непосредственно в базу данных без открытия его формы.

Поскольку форма элемента не открывается:

  1. Расширение формы элемента не подключается.
  2. Метод ПроверитьЗаполнение() платформой автоматически не вызывается.
  3. Событие ОбработкаПроверкиЗаполнения игнорируется.

Единственный способ предотвратить запись некорректного объекта при снятии пометки удаления — это опять же использование события ПередЗаписью(Отказ) в модуле объекта, так как оно вызывается всегда, независимо от того, откуда пришла команда на запись.

Особенности проверки заполнения для документов (Проведение vs Запись)

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

Резюме и лучшие практики для разработчика

Подведем итог и сформулируем основные правила работы с проверкой заполнения реквизитов в 1С:

  1. Для интерактивных форм используйте стандартное свойство реквизита «Проверка заполнения» = «Выдавать ошибку» и предопределенное событие модуля объекта ОбработкаПроверкиЗаполнения для динамического управления составом проверяемых полей.
  2. При написании любого программного кода создания или изменения объектов всегда вызывайте метод Объект.ПроверитьЗаполнение() перед вызовом Объект.Записать(), если хотите быть уверены в корректности заполнения обязательных реквизитов.
  3. Если бизнес-логика требует критически жесткого контроля данных (например, ИНН, ОГРН, уникальные коды), который нельзя обходить ни при каких условиях (включая групповые обработки и обмены), переносите логику проверки в процедуру ПередЗаписью(Отказ) модуля объекта. Не забывайте ставить проверку на ОбменДанными.Загрузка, чтобы не заблокировать стандартные механизмы интеграции.
← На главную