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