Как программно изменить коэффициент или нормы-часы в табличной части документа Заказ-наряд

Программист 1С v8.3 (Обычные формы) Управленческий учет Сфера услуг, туризм и социальный сектор
← На главную

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

Разбор типичной ошибки: Пересечение имен переменных в циклах

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


Пока Выборка.Следующий() Цикл 
    Объекта = Выборка.Ссылка.ПолучитьОбъект();
    Для каждого Выборка из Объекта.Работы Цикл
        Если Выборка.Коэффициент = 0.200 Тогда 
            Объекта.Коэффициент = 1.000;
        КонецЕсли;    
    КонецЦикла;
    Объекта.Записать();
КонецЦикла;

Проблема: Обратите внимание на переменную Выборка. Сначала она используется для обхода результата запроса (внешний цикл), а затем внутри нее же объявляется цикл по строкам табличной части с тем же именем переменной Для каждого Выборка.... В 1С это приводит к тому, что значение внешнего итератора перетирается внутренним, и при попытке перехода на следующую итерацию внешнего цикла система выдает ошибку "Итератор не определен" или ведет себя непредсказуемо.

Решение: Всегда используйте разные имена для переменных обхода. Для внешнего цикла подойдет ВыборкаИзЗапроса, а для внутреннего — ТекущаяСтрока или Стр.

Оптимизированный подход: Запрос с итогами и обращение по индексу

Просто перебирать все документы и в каждом из них перебирать все строки — это крайне неэффективно, особенно если в базе тысячи документов. Рассмотрим более профессиональный подход, который позволяет обращаться только к тем строкам, которые действительно требуют изменений.

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

  1. Создание запроса: Мы выбираем не только ссылки на документы, но и НомерСтроки табличной части.
  2. Использование группировок: Добавляем ИТОГИ ПО Ссылка. Это создаст иерархическую структуру в результате запроса.
  3. Обход по группировкам: Сначала мы получаем объект документа один раз, а затем внутри обходим только те строки, которые вернул запрос.

Проанализируем пример кода, реализующий этот алгоритм:


Процедура КнопкаВыполнитьНажатие(Кнопка)
    // 1. Подготовка списка работ
    Работа = Новый СписокЗначений;         
    Работа.Добавить(Справочники.Автоработы.НайтиПоКоду("ЦБ00021506"));

    // 2. Формирование запроса с итогами
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    ЗаказНарядРаботы.Ссылка КАК Ссылка,
    |    ЗаказНарядРаботы.НомерСтроки КАК НомерСтроки
    |ИЗ
    |    Документ.ЗаказНаряд.Работы КАК ЗаказНарядРаботы
    |ГДЕ
    |    ЗаказНарядРаботы.Работа В(&Работа) 
    |    И ЗаказНарядРаботы.Коэффициент = 0.5
    |ИТОГИ ПО
    |    Ссылка";
    
    Запрос.УстановитьПараметр("Работа", Работа);
    Результат = Запрос.Выполнить();
    
    // 3. Обход результатов по документам (верхний уровень итогов)
    ВыборкаДокумент = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    
    Пока ВыборкаДокумент.Следующий() Цикл    
        // Получаем объект документа для редактирования
        ОбъектДок = ВыборкаДокумент.Ссылка.ПолучитьОбъект();
        
        // 4. Обход конкретных строк внутри документа
        ВыборкаСтрок = ВыборкаДокумент.Выбрать(ОбходРезультатаЗапроса.Прямой);
        Пока ВыборкаСтрок.Следующий() Цикл
            // В табличной части индекс начинается с 0, а НомерСтроки с 1
            ИндексСтроки = ВыборкаСтрок.НомерСтроки - 1;
            ТекущаяСтрока = ОбъектДок.Работы[ИндексСтроки];
            
            // Устанавливаем новое значение
            ТекущаяСтрока.Коэффициент = 0.600;
        КонецЦикла;
        
        // 5. Запись документа
        Попытка
            ОбъектДок.Записать();
            Сообщить("Документ " + ОбъектДок + " успешно обновлен.");
        Исключение
            Сообщить("Не удалось записать документ " + ОбъектДок + ". Ошибка: " + КраткоеОписаниеОшибки(ИнформацияОбОшибке()));
        КонецПопытки;
    КонецЦикла;
КонецПроцедуры

Важные аспекты при программном изменении табличных частей

Рассматривая приведенный выше код, мы должны помнить о нескольких критических нюансах, которые часто упускаются из виду:

  1. Пересчет итогов документа: В типовых конфигурациях простое изменение реквизита строки (например, Коэффициент) не вызывает автоматического пересчета суммы строки и общей суммы документа. После изменения коэффициента программно, рекомендуется вызвать процедуру пересчета, которая обычно находится в модуле объекта документа. Например: ОбъектДок.РассчитатьСуммуСтроки(ТекущаяСтрока).
  2. Индексация строк: Мы использовали конструкцию ИндексСтроки = ВыборкаСтрок.НомерСтроки - 1. Это работает корректно только в том случае, если с момента выполнения запроса до момента записи объекта состав строк документа не изменился (например, другой пользователь не удалил строку). В высоконагруженных системах лучше использовать поиск строки по идентификатору или проверять соответствие работы в строке перед изменением.
  3. Блокировки данных: При массовом изменении документов мы рекомендуем использовать транзакции. Обернув цикл записи в транзакцию, вы значительно ускорите процесс и обеспечите целостность данных.

Использование стандартных инструментов (без программирования)

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

Рассмотрим, как выполнить нашу задачу с помощью этого инструмента:

Преимущество стандартной обработки в том, что она чаще всего корректно отрабатывает все внутренние зависимости документа (пересчеты сумм, налогов и т.д.), что минимизирует риск появления "битых" данных.

Заключение

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

← На главную