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