При разработке обработок в «1С:Управление производственным предприятием 1.3» часто возникает необходимость программно отредактировать строки в документе Корректировка записей регистров. Начинающие разработчики часто пытаются найти привычную табличную часть с движениями, однако этот документ устроен иначе. В этой статье разберем особенности структуры документа, выясним причины распространенных ошибок при удалении строк и рассмотрим правильные алгоритмы работы с наборами записей.
Рассмотрим, как документ хранит данные. В документе Корректировка записей регистров отображаемые на форме таблицы регистров не являются стандартными табличными частями документа. Сам документ содержит лишь служебную табличную часть со списком задействованных регистров, а все строки с данными — это непосредственные движения регистров накопления или сведений, зарегистрированные данным документом-регистратором.
Для доступа к этим данным используется коллекция Движения объекта документа.
Проанализируем базовый порядок обращения к движениям конкретного регистра (например, регистра накопления НДСсАвансов):
ПолучитьОбъект().Движения по его имени.Прочитать().Посмотрим на базовый пример кода:
ДокументОбъект = СсылкаНаОбъект.ПолучитьОбъект();
НаборЗаписи = ДокументОбъект.Движения["НДСсАвансов"];
// Загружаем текущие движения документа из базы
НаборЗаписи.Прочитать();
// Выполняем действия с записями...
// Сохраняем движения
НаборЗаписи.Записать();
Часто при попытке профильтровать и удалить строки разработчики сталкиваются с ошибкой Недопустимое значение параметра (параметр номер '1') при вызове метода Удалить(). Выясним причину этой проблемы.
Ошибка возникает из-за использования вложенных циклов без флага прерывания. Если во внутреннем цикле условие срабатывает несколько раз, один и тот же объект записи добавляется в массив на удаление повторно. При повторной попытке удалить из набора уже удаленный элемент платформа 1С вызывает исключение.
Также обратим внимание на сравнение ссылочных типов. Использование конструкции СокрЛП(Строка(Ссылка1)) = СокрЛП(Строка(Ссылка2)) является критической ошибкой. Приведение ссылок к строкам существенно снижает производительность и может давать ложные результаты из-за совпадения наименований разных объектов. Ссылки 1С следует сравнивать напрямую через оператор =.
Разберем по шагам правильный алгоритм обхода коллекций с использованием флага совпадения. Мы собираем только уникальные записи на удаление и затем удаляем их из коллекции.
ДокументОбъект = СсылкаНаОбъект.ПолучитьОбъект();
ДвиженияРегистра = ДокументОбъект.Движения["НДСсАвансов"];
ДвиженияРегистра.Прочитать();
МассивУдаляемых = Новый Массив;
// Внешний цикл по движениям документа
Для Каждого ЗаписьДвижения Из ДвиженияРегистра Цикл
НужноУдалить = Истина;
// Внутренний цикл по таблице условий (реестру)
Для Каждого СтрокаРеестра Из ТаблицаРеестра Цикл
// Прямое сравнение ссылочных типов без приведения к строке
Если ЗаписьДвижения.СчетФактура = СтрокаРеестра.Документ
И ЗаписьДвижения.Покупатель = СтрокаРеестра.Покупатель Тогда
// Если запись найдена в реестре, оставляем её
НужноУдалить = Ложь;
Прервать; // Выходим из внутреннего цикла
КонецЕсли;
КонецЦикла;
Если НужноУдалить Тогда
МассивУдаляемых.Добавить(ЗаписьДвижения);
КонецЕсли;
КонецЦикла;
// Удаляем собранные элементы
Для Каждого ЭлементНаУдаление Из МассивУдаляемых Цикл
ДвиженияРегистра.Удалить(ЭлементНаУдаление);
КонецЦикла;
// Взводим флаг записи и сохраняем изменения
ДвиженияРегистра.Записывать = Истина;
ДокументОбъект.Записать();
Рассмотрим более производительный и надежный подход. Чтобы избежать проблем со сдвигом индексов и повторными удалениями, набор записей выгружается в объект ТаблицаЗначений, обрабатывается там, после чего набор очищается и загружается заново.
Посмотрим на реализацию этого способа:
ДокументОбъект = СсылкаНаОбъект.ПолучитьОбъект();
НаборДвижений = ДокументОбъект.Движения["НДСсАвансов"];
НаборДвижений.Прочитать();
// Выгружаем движения в Таблицу Значений
ТЗДвижений = НаборДвижений.Выгрузить();
// Добавляем временное служебное поле для отметки
ТЗДвижений.Колонки.Добавить("Удалить", Новый ОписаниеТипов("Булево"));
Для Каждого СтрокаДвижения Из ТЗДвижений Цикл
// Ищем соответствие в таблице реестра через метод НайтиСтроки
Отбор = Новый Структура;
Отбор.Вставить("Документ", СтрокаДвижения.СчетФактура);
Отбор.Вставить("Покупатель", СтрокаДвижения.Покупатель);
НайденныеСтроки = ТаблицаРеестра.НайтиСтроки(Отбор);
// Если в реестре совпадений нет — помечаем на удаление
Если НайденныеСтроки.Количество() = 0 Тогда
СтрокаДвижения.Удалить = Истина;
КонецЕсли;
КонецЦикла;
// Отбираем и удаляем помеченные строки
ПараметрыОтбора = Новый Структура("Удалить", Истина);
УдаляемыеСтроки = ТЗДвижений.НайтиСтроки(ПараметрыОтбора);
Для Каждого СтрокаДляУдаления Из УдаляемыеСтроки Цикл
ТЗДвижений.Строки.Удалить(СтрокаДляУдаления);
КонецЦикла;
// Удаляем служебную колонку перед загрузкой
ТЗДвижений.Колонки.Удалить("Удалить");
// Очищаем набор и загружаем отфильтрованные данные
НаборДвижений.Очистить();
НаборДвижений.Загрузить(ТЗДвижений);
НаборДвижений.Записывать = Истина;
ДокументОбъект.Записать();
ДокументОбъект.Записать() всегда явно указывать НаборДвижений.Записывать = Истина;.Это гарантирует, что платформа корректно сбросит изменения в базу данных.