При разработке в среде 1С:Предприятие одной из наиболее частых задач является перехват момента записи документа для выполнения дополнительных действий, например, для трансляции данных в регистры сведений или проверки корректности заполнения. Подписки на события — это гибкий инструмент, позволяющий внедрять логику, не изменяя типовую конфигурацию. Однако у начинающих разработчиков часто возникают трудности с доступом к данным табличных частей через параметр Источник.
В этой статье мы подробно разберем, как правильно извлекать данные из табличной части документа внутри подписки на событие ПередЗаписью, почему запросы не всегда являются лучшим решением в этой точке и как избежать распространенных ошибок вроде «Поле не обнаружено».
Первое и самое важное, что нам необходимо усвоить: в подписке на событие ПередЗаписью для документа параметр Источник представляет собой сам объект (например, ДокументОбъект.ПриходнаяНакладная), а не ссылку на него. Это принципиальный момент для понимания работы системы.
Рассмотрим ситуацию: когда пользователь нажимает кнопку «Записать», данные из экранной формы переносятся в объект в оперативной памяти. В этот момент данные еще не записаны в базу данных. Параметр Источник дает нам прямой доступ к этому объекту «в памяти». Если мы попытаемся обратиться к Источник.Ссылка, мы получим ссылку на версию документа, которая уже лежит в базе (старое состояние). Если документ создается впервые, ссылка будет пустой или указывать на несуществующую запись.
Поэтому, если нам нужно получить товары, которые пользователь только что ввел в таблицу, мы должны обращаться напрямую к коллекции табличной части объекта Источник.
Для того чтобы вытащить наименование номенклатуры или саму ссылку на номенклатуру из табличной части, нам не обязательно использовать механизм запросов. Более того, использование запроса в ПередЗаписью к табличной части этого же документа — плохая практика, так как запрос пойдет в базу данных и не «увидит» те изменения, которые пользователь внес в форму, но еще не сохранил.
Проанализируем самый эффективный способ — прямой обход коллекции в цикле. Предположим, наша табличная часть называется Товары, а колонка с номенклатурой — Номенклатура.
Для каждого СтрокаТЧ Из Источник.Товары Цикл
// Получаем ссылку на номенклатуру
ТекущаяНоменклатура = СтрокаТЧ.Номенклатура;
// Если нужно именно наименование (строкой)
НаименованиеДляЗаписи = Строка(ТекущаяНоменклатура);
// Здесь выполняем логику записи в Регистр Сведений
// ...
КонецЦикла;
В этом примере Источник.Товары — это коллекция всех строк табличной части. Переменная СтрокаТЧ на каждой итерации цикла становится конкретной строкой, из которой мы можем извлечь значения любых реквизитов (Количество, Цена, Сумма и т.д.).
Разберем типичную ошибку, с которой сталкиваются новички: попытка обратиться к колонке сразу через коллекцию, например: Источник.Товары.Номенклатура. С точки зрения системы 1С, Товары — это вся таблица целиком (объект типа РезультатТабличнойЧасти), и у таблицы как у единого целого нет поля Номенклатура. Это поле есть только у конкретной строки этой таблицы.
Запомним правило: к реквизитам табличной части можно обращаться только внутри цикла по строкам или используя специальные методы поиска (например, НайтиСтроки()).
Также важно убедиться в правильности имен. Имя табличной части и имена колонок в коде должны строго соответствовать именам, заданным в Конфигураторе (имя объекта), а не заголовкам, которые видит пользователь в интерфейсе.
Иногда использование запроса оправдано, если нужно соединить данные документа с данными из других таблиц. Однако в сообщении автора темы была допущена критическая ошибка в логике запроса. Рассмотрим, как нельзя писать запрос в подписке:
// ПЛОХОЙ ПРИМЕР
"ВЫБРАТЬ
| ПриходнаяНакладнаяНоменклатура.Номенклатура
|ИЗ
| Документ.ПриходнаяНакладная.Товары КАК ПриходнаяНакладнаяНоменклатура
|ГДЕ
| ПриходнаяНакладнаяНоменклатура.Номенклатура.Наименование = &Наименование"
Проанализируем ошибки этого кода:
Наименование = &Наименование — это крайне медленная операция. Всегда нужно работать со ссылками (Уникальными Идентификаторами).Если вам все же необходим запрос для анализа уже записанных данных, обязательно передавайте ссылку на текущий документ в параметры:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЧ.Номенклатура,
| ТЧ.Количество
|ИЗ
| Документ.ПриходнаяНакладная.Товары КАК ТЧ
|ГДЕ
| ТЧ.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Источник.Ссылка);
Результат = Запрос.Выполнить();
Выясним, как правильно передать полученную номенклатуру в Регистр Сведений (РС). Поскольку мы находимся в событии ПередЗаписью, мы работаем в рамках одной транзакции. Если запись в РС не удастся, запись документа тоже будет отменена, что гарантирует целостность данных.
Для записи в независимый регистр сведений (где документ не является регистратором) используем менеджер записи или набор записей:
Для каждого СтрокаТЧ Из Источник.Товары Цикл
МенеджерЗаписи = РегистрыСведений.ИмяВашегоРегистра.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Период = Источник.Дата;
МенеджерЗаписи.Номенклатура = СтрокаТЧ.Номенклатура;
// Заполняем остальные измерения и ресурсы
МенеджерЗаписи.Записать();
КонецЦикла;
Важный нюанс: если вы записываете данные в регистр, который подчинен документу-регистратору, лучше делать это в событии ПриЗаписи или через движения самого документа, используя коллекцию Движения. В ПередЗаписью ссылка на новый документ может быть еще невалидной для регистрации движений.
Для того чтобы визуально понять, что находится внутри объекта Источник, используйте инструмент «Вычислить выражение» (Shift+F9) в режиме отладки на точке останова:
Источник и нажмите Shift+F9.Разберем ситуацию с порядком строк. Автор темы упомянул, что «нужно было поменять местами пару строк». Вероятно, речь шла о том, что обращение к данным происходило до того, как они были инициализированы или очищены. Всегда следите за тем, чтобы запись в регистр происходила после того, как все необходимые реквизиты объекта (например, Дата или Контрагент) уже заполнены в объекте Источник.
Работа с табличными частями в подписках на события требует четкого понимания объектной модели 1С. Помните, что Источник в ПередЗаписью — это живой объект, с которым вы можете взаимодействовать напрямую через циклы Для каждого. Избегайте лишних запросов к базе данных в этой точке и всегда проверяйте соответствие имен реквизитов тем, что заданы в метаданных конфигурации. Эти простые правила помогут вам писать быстрый и надежный код.