Как программно изменить строковое поле в табличной части документа в 1С 8.3?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

При разработке внешних обработок для конфигурации «1С:Бухгалтерия предприятия 3.0» нередко возникает необходимость массово скорректировать данные в уже существующих документах. Классический сценарий — изменение колонки Представление в табличной части документа АктСверкиВзаиморасчетов, если в сверку попали ручные операции (документы Операция), и требуется уточнить их описание для контрагента.

Однако при программной модификации табличных частей разработчики часто допускают типичные ошибки, из-за которых код либо вызывает исключение, либо отрабатывает вхолостую без сохранения результата. Разберем по шагам, почему возникает проблема, как исправить исходный алгоритм и как переписать его оптимальным способом.

Анализ типичных ошибок при работе с табличными частями

Рассмотрим фрагмент кода, в котором была допущена ошибка:


КоличествоСтрок = ЭтотОбъект.ПоДаннымОрганизации.Количество;
Если КоличествоСтрок <> 0 Тогда
    Для Каждого стр из ЭтотОбъект.ПоДаннымОрганизации Цикл
        ТекКомментарий = стр.Документ.Комментарий;
        Если Найти(ТекКомментарий, "№") > 0 Тогда
            ЭтотОбъект.ПоДаннымОрганизации.Представление = ТекКомментарий;
            ЭтотОбъект.Записать(РежимЗаписиДокумента.Запись);
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

Проанализируем основные недочеты этой конструкции:

  1. Ошибочное обращение к свойству строки через коллекцию: конструкция вида ЭтотОбъект.ПоДаннымОрганизации.Представление = ТекКомментарий; синтаксически неверна. Свойство Представление принадлежит конкретной строке (переменной стр), а не всей табличной части. Попытка установить значение напрямую в коллекцию вызовет ошибку обращения к несуществующему свойству. Правильное обращение: стр.Представление = ТекКомментарий;.
  2. Запись объекта внутри цикла обхода строк: вызов метода Записать() на каждой итерации обхода табличной части приводит к колоссальным просадкам производительности и потенциальным блокировкам. Запись объекта документа должна выполняться ровно один раз — после того, как все строки успешно обработаны, и только при наличии реальных изменений.
  3. Использование служебных имен для переменных: присваивание полученного объекта переменной ЭтотОбъект является антипаттерном. Имя ЭтотОбъект зарезервировано платформой 1С в контексте модулей объектов и форм. Для полученного через ПолучитьОбъект() экземпляра следует использовать нейтральные имена, например АктОбъект или ДокОбъект.
  4. Лишняя проверка на количество строк: оператор Для Каждого автоматически пропускает тело цикла, если коллекция пуста. Конструкция Если КоличествоСтрок <> 0 Тогда является избыточной.
  5. Ограничение длины строки: реквизит Представление в типовом акте сверки имеет фиксированную длину (как правило, 100 символов). Реквизит Комментарий в документе «Операция» — неограниченной длины. При записи платформа автоматически обрежет текст, что важно учитывать при конкатенации строк.

Вариант 1. Исправление кода выборки объектов

Скорректируем логику без использования тяжелых конструкций. Добавим флаг модифицированности, изменим обращение к строке и перенесем вызов метода Записать() за пределы цикла строк.


&НаСервере
Процедура Заполнить()
    // Задаем границы периода строго с учетом начала и конца дня
    ВыборкаДокументов = Документы.АктСверкиВзаиморасчетов.Выбрать(НачалоДня(ДатаНачала), КонецДня(ДатаКонца));
    
    Пока ВыборкаДокументов.Следующий() Цикл
        АктОбъект = ВыборкаДокументов.ПолучитьОбъект();
        НужноЗаписать = Ложь;
        
        Для Каждого Стр Из АктОбъект.ПоДаннымОрганизации Цикл
            // Проверяем заполненность ссылки на документ-регистратор
            Если Не ЗначениеЗаполнено(Стр.Документ) Тогда
                Продолжить;
            КонецЕсли;
            
            ТекКомментарий = Стр.Документ.Комментарий;
            
            Если Найти(ТекКомментарий, "№") > 0 Тогда
                Стр.Представление = ТекКомментарий;
                НужноЗаписать = Истина;
            КонецЕсли;
        КонецЦикла;
        
        // Записываем только если в табличной части были изменения
        Если НужноЗаписать Тогда
            АктОбъект.Записать(РежимЗаписиДокумента.Запись);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Вариант 2. Оптимальное решение через запрос

Метод Выбрать() у менеджера документов считывает все документы подряд. Если в базе тысячи актов сверки, обход каждого объекта вызовет чрезмерную нагрузку на сервер. Намного эффективнее использовать запрос: отобрать только те документы, у которых в строках действительно присутствуют нужные комментарии, сгруппировать их по ссылкам и обновить данные точечно.


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

Почему документы могут не попадать в выборку

Если код выполняется без ошибок, но результат не изменяется, обратим внимание на особенности прикладного решения:

  1. Путаница между датой документа и периодом сверки: метод Выбрать() и условие Ссылка.Дата фильтруют данные исключительно по реквизиту Дата (момент создания документа). При этом сам документ «Акт сверки» содержит отдельные реквизиты шапки: ДатаНачала и ДатаОкончания. Если акт был создан в текущем месяце, а сверка проводилась за прошлый квартал, отбор по дате создания может не захватить нужные документы. В таких случаях в тексте запроса фильтрацию следует строить по полям Ссылка.ДатаНачала и Ссылка.ДатаОкончания.
  2. Ограничение прав на уровне записей (RLS): если обработка запускается под пользователем с ограниченными правами, документы чужих организаций или подразделений не попадут в выборку.
  3. Перезапись данных типовым алгоритмом: помните, что при повторном нажатии стандартной кнопки «Заполнить» в форме акта сверки типовой механизм конфигурации пересчитает строки и сотрет внесенные обработкой изменения в поле Представление.

Методические рекомендации

Использование документов вида ОперацияБух (ручные проводки) для взаиморасчетов с контрагентами на счетах 60 и 62 крайне нежелательно. Ручные операции не двигают вспомогательные регистры подсистем учета НДС и взаиморасчетов, что приводит к разрыву аналитики по субконто «Документы расчетов с контрагентом» и зависанию сальдо. Методически правильным решением является отражение взаимозачетов и корректировок через специализированный документ КорректировкаДолга.

Если же задача заключается исключительно в модификации печатной формы, безопаснее использовать внешнюю печатную форму или механизм расширений конфигурации. Это избавит от необходимости физической перезаписи данных в базе и защитит внесенные правки от случайной перезаписи пользователями.

← На главную