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

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

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

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

Разбираемся с параметром Источник

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

Рассмотрим ситуацию: когда пользователь нажимает кнопку «Записать», данные из экранной формы переносятся в объект в оперативной памяти. В этот момент данные еще не записаны в базу данных. Параметр Источник дает нам прямой доступ к этому объекту «в памяти». Если мы попытаемся обратиться к Источник.Ссылка, мы получим ссылку на версию документа, которая уже лежит в базе (старое состояние). Если документ создается впервые, ссылка будет пустой или указывать на несуществующую запись.

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

Как правильно перебирать строки табличной части

Для того чтобы вытащить наименование номенклатуры или саму ссылку на номенклатуру из табличной части, нам не обязательно использовать механизм запросов. Более того, использование запроса в ПередЗаписью к табличной части этого же документа — плохая практика, так как запрос пойдет в базу данных и не «увидит» те изменения, которые пользователь внес в форму, но еще не сохранил.

Проанализируем самый эффективный способ — прямой обход коллекции в цикле. Предположим, наша табличная часть называется Товары, а колонка с номенклатурой — Номенклатура.


Для каждого СтрокаТЧ Из Источник.Товары Цикл
    // Получаем ссылку на номенклатуру
    ТекущаяНоменклатура = СтрокаТЧ.Номенклатура;
    
    // Если нужно именно наименование (строкой)
    НаименованиеДляЗаписи = Строка(ТекущаяНоменклатура); 
    
    // Здесь выполняем логику записи в Регистр Сведений
    // ...
КонецЦикла;

В этом примере Источник.Товары — это коллекция всех строк табличной части. Переменная СтрокаТЧ на каждой итерации цикла становится конкретной строкой, из которой мы можем извлечь значения любых реквизитов (Количество, Цена, Сумма и т.д.).

Почему возникает ошибка «Поле не обнаружено»?

Разберем типичную ошибку, с которой сталкиваются новички: попытка обратиться к колонке сразу через коллекцию, например: Источник.Товары.Номенклатура. С точки зрения системы 1С, Товары — это вся таблица целиком (объект типа РезультатТабличнойЧасти), и у таблицы как у единого целого нет поля Номенклатура. Это поле есть только у конкретной строки этой таблицы.

Запомним правило: к реквизитам табличной части можно обращаться только внутри цикла по строкам или используя специальные методы поиска (например, НайтиСтроки()).

Также важно убедиться в правильности имен. Имя табличной части и имена колонок в коде должны строго соответствовать именам, заданным в Конфигураторе (имя объекта), а не заголовкам, которые видит пользователь в интерфейсе.

Использование запросов: когда это нужно и как делать правильно

Иногда использование запроса оправдано, если нужно соединить данные документа с данными из других таблиц. Однако в сообщении автора темы была допущена критическая ошибка в логике запроса. Рассмотрим, как нельзя писать запрос в подписке:


// ПЛОХОЙ ПРИМЕР
"ВЫБРАТЬ
|   ПриходнаяНакладнаяНоменклатура.Номенклатура
|ИЗ
|   Документ.ПриходнаяНакладная.Товары КАК ПриходнаяНакладнаяНоменклатура
|ГДЕ
|   ПриходнаяНакладнаяНоменклатура.Номенклатура.Наименование = &Наименование"

Проанализируем ошибки этого кода:

  1. Отсутствие фильтра по ссылке: такой запрос начнет перебирать товары во всех существующих в базе приходных накладных. При наличии тысяч документов система просто «зависнет».
  2. Поиск по наименованию: сравнение Наименование = &Наименование — это крайне медленная операция. Всегда нужно работать со ссылками (Уникальными Идентификаторами).
  3. Работа с базой, а не с объектом: как уже упоминалось, запрос не увидит строки, которые еще не записаны.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ТЧ.Номенклатура,
    |   ТЧ.Количество
    |ИЗ
    |   Документ.ПриходнаяНакладная.Товары КАК ТЧ
    |ГДЕ
    |   ТЧ.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", Источник.Ссылка);
Результат = Запрос.Выполнить();

Запись в Регистр Сведений из подписки

Выясним, как правильно передать полученную номенклатуру в Регистр Сведений (РС). Поскольку мы находимся в событии ПередЗаписью, мы работаем в рамках одной транзакции. Если запись в РС не удастся, запись документа тоже будет отменена, что гарантирует целостность данных.

Для записи в независимый регистр сведений (где документ не является регистратором) используем менеджер записи или набор записей:


Для каждого СтрокаТЧ Из Источник.Товары Цикл
    МенеджерЗаписи = РегистрыСведений.ИмяВашегоРегистра.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.Период = Источник.Дата;
    МенеджерЗаписи.Номенклатура = СтрокаТЧ.Номенклатура;
    // Заполняем остальные измерения и ресурсы
    МенеджерЗаписи.Записать();
КонецЦикла;

Важный нюанс: если вы записываете данные в регистр, который подчинен документу-регистратору, лучше делать это в событии ПриЗаписи или через движения самого документа, используя коллекцию Движения. В ПередЗаписью ссылка на новый документ может быть еще невалидной для регистрации движений.

Практические советы для отладки

Для того чтобы визуально понять, что находится внутри объекта Источник, используйте инструмент «Вычислить выражение» (Shift+F9) в режиме отладки на точке останова:

  1. Поставьте точку останова в начале процедуры подписки.
  2. Когда программа остановится, выделите слово Источник и нажмите Shift+F9.
  3. Раскройте дерево свойств. Найдите вашу табличную часть.
  4. Внутри табличной части вы увидите строки. Это поможет вам точно определить программные имена колонок и убедиться, что данные присутствуют в памяти.

Разберем ситуацию с порядком строк. Автор темы упомянул, что «нужно было поменять местами пару строк». Вероятно, речь шла о том, что обращение к данным происходило до того, как они были инициализированы или очищены. Всегда следите за тем, чтобы запись в регистр происходила после того, как все необходимые реквизиты объекта (например, Дата или Контрагент) уже заполнены в объекте Источник.

Заключение

Работа с табличными частями в подписках на события требует четкого понимания объектной модели 1С. Помните, что Источник в ПередЗаписью — это живой объект, с которым вы можете взаимодействовать напрямую через циклы Для каждого. Избегайте лишних запросов к базе данных в этой точке и всегда проверяйте соответствие имен реквизитов тем, что заданы в метаданных конфигурации. Эти простые правила помогут вам писать быстрый и надежный код.

← На главную