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