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