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