Почему в событии ПередЗаписью запрос не видит данные табличной части и как правильно проверить их заполнение?

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

При разработке бизнес-логики в 1С часто возникает необходимость проверить заполненность реквизитов табличной части перед тем, как документ будет сохранен в базу данных. Однако начинающие разработчики сталкиваются с проблемой: если выполнить запрос к табличной части документа в процедуре ПередЗаписью, запрос возвращает либо пустую выборку, либо старые данные, которые были в базе до начала редактирования.

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

Причина поведения платформы 1С

Выясним, почему запрос не показывает текущие изменения из формы или объекта. В платформе 1С существует четкое разделение между данными в оперативной памяти и данными, записанными на физический диск (в СУБД):

  1. Данные в памяти: Процедура ПередЗаписью вызывается в момент, когда изменения еще находятся в оперативной памяти объекта ЭтотОбъект. На этом этапе данные еще не записаны в таблицы базы данных.
  2. Работа запроса: Любой объект Запрос обращается напрямую к таблицам СУБД. Он не знает о том, какие строки были добавлены, изменены или удалены в памяти прямо сейчас.
  3. Новый документ: Если документ создается впервые и еще ни разу не сохранялся (ЭтоНовый() = Истина), его физических записей в таблицах СУБД нет вообще, поэтому запрос всегда вернет пустой результат.
  4. Существующий документ: Если документ редактируется, запрос прочитает из базы данных ту версию табличной части, которая была сохранена при предыдущей записи.

Решение 1. Прямой обход табличной части в цикле

Если задача заключается в простой проверке значений в строках табличной части, самым простым и эффективным решением будет прямой обход коллекции ЭтотОбъект.ТабличнаяЧасть с помощью цикла Для каждого.

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


Для каждого Стр Из ЭтотОбъект.Услуги Цикл
    Если Не ЗначениеЗаполнено(Стр.Услуга) И Стр.КоличествоПройдено <> 0 Тогда
        Сообщить("В строке " + Строка(Стр.НомерСтроки) + " не заполнено название процедуры!");
        Отказ = Истина;
    КонецЕсли;
КонецЦикла;

В этом случае проверка выполняется над объектами в оперативной памяти, поэтому платформа корректно видит все только что введенные пользователем данные.

Решение 2. Использование запроса через временную таблицу

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

Проанализируем по шагам, как передать незаписанную табличную часть в запрос:

  1. Выгрузим табличную часть объекта в таблицу значений с помощью метода Выгрузить().
  2. Передадим полученную таблицу значений в параметр запроса и поместим ее во временную таблицу.
  3. Выполним необходимые соединения и условия в пакете запроса.

Посмотрим на пример реализации данного подхода:


Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТЗ.Услуга КАК Услуга,
    |	ТЗ.КоличествоПройдено КАК КоличествоПройдено,
    |	ТЗ.НомерСтроки КАК НомерСтроки
    |ПОМЕСТИТЬ ВТ_Услуги
    |ИЗ
    |	&ТЗ КАК ТЗ
    |;
    |ВЫБРАТЬ
    |	ВТ.НомерСтроки КАК НомерСтроки
    |ИЗ
    |	ВТ_Услуги КАК ВТ
    |ГДЕ
    |	ВТ.Услуга = ЗНАЧЕНИЕ(Справочник.Услуги.ПустаяСсылка)
    |	И ВТ.КоличествоПройдено <> 0";

Запрос.УстановитьПараметр("ТЗ", ЭтотОбъект.Услуги.Выгрузить());
РезультатЗапроса = Запрос.Выполнить();

Если Не РезультатЗапроса.Пустой() Тогда
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        Сообщить("В строке " + Строка(Выборка.НомерСтроки) + " не заполнено название процедуры!");
        Отказ = Истина;
    КонецЦикла;
КонецЕсли;

Решение 3. Использование системного события ОбработкаПроверкиЗаполнения

По стандартам разработки 1С проверку обязательных полей рекомендуется выносить из процедуры ПередЗаписью в специализированное событие модуля объекта — ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты).

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


Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
    
    // Если требуется условная проверка реквизита табличной части:
    Для каждого Стр Из Услуги Цикл
        Если Стр.КоличествоПройдено <> 0 И Не ЗначениеЗаполнено(Стр.Услуга) Тогда
            
            Сообщение = Новый СообщениеПользователю;
            Сообщение.Текст = "Не заполнена услуга в строке " + Строка(Стр.НомерСтроки);
            Сообщение.Поле = "Услуги[" + Строка(Стр.НомерСтроки - 1) + "].Услуга";
            Сообщение.УстановитьДанные(ЭтотОбъект);
            Сообщение.Сообщить();
            
            Отказ = Истина;
        КонецЕсли;
    КонецЦикла;

КонецПроцедуры

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

← На главную