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