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