Как изменить ресурсы в существующих записях регистра сведений при добавлении новых данных

Программист 1С v8.3 (Управляемые формы)
← На главную

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

Разберем подробно ситуацию, когда при добавлении новой записи о «Банкротстве» необходимо изменить «Код обещания» в предыдущих записях того же объекта с реквизитом «Срок». Проанализируем причины ошибок и рассмотрим несколько вариантов правильного решения этой задачи.

Причина 1: Неправильное использование Менеджера Записи

Рассмотрим код, который часто приводит к неудаче. Попытка использовать РегистрыСведений.[Имя].СоздатьМенеджерЗаписи() эффективна только тогда, когда вам известен полный набор измерений, составляющих ключ записи. В 1С ключ записи — это совокупность всех измерений регистра.

Если в вашем регистре есть измерения Объект, НомерЗаписи и ИД, то для того, чтобы метод Прочитать() отработал успешно, вы обязаны заполнить все три поля. Если вы заполняете только Объект и Реквизит (который в примере автора и вовсе является ресурсом), менеджер записи не сможет идентифицировать конкретную строку в базе данных. В результате Запись.Выбран() всегда будет возвращать Ложь.

Как правильно: Для работы с группой записей или при поиске записей по неполному ключу следует использовать НаборЗаписей.


// Пример правильного поиска и изменения записей через набор
Набор = РегистрыСведений.Активность.СоздатьНаборЗаписей();
Набор.Отбор.Объект.Установить(ТекущийОбъект);
Набор.Отбор.Реквизит.Установить("Срок"); // Если Реквизит — это измерение
Набор.Прочитать();

Для Каждого Запись Из Набор Цикл
    // Проводим необходимые изменения в ресурсах
    Запись.Значение = НовоеЗначение;
КонецЦикла;

Набор.Записать();

Причина 2: Попытка изменения реквизита через ссылку («Ошибка точки»)

Проанализируем критическую ошибку в строке: Запись.Объект.Обещание.Код = Справочники.Обещания.НайтиПоКоду("..."). Здесь совершаются сразу две технические ошибки:

  1. Неизменяемость ссылок: В 1С Объект (если это ссылка на справочник или документ) является ссылочным типом данных. Вы не можете менять реквизиты объекта «через точку» напрямую в базе данных. Чтобы изменить данные в самом справочнике «Обещания», необходимо сначала получить объектную форму ссылки методом ПолучитьОбъект().
  2. Типизация: Метод НайтиПоКоду() возвращает Ссылку на элемент справочника, а реквизит Код — это обычно Строка. Присвоение ссылки строковому полю не приведет к ошибке сразу, но запишет в поле пустую строку или строковое представление ссылки, что нарушит логику программы.

Если ваша задача — изменить данные в связанном справочнике, это должно выглядеть так:


// Изменение реквизита в связанном справочнике
СсылкаНаОбещание = Справочники.Обещания.НайтиПоКоду("000000019");
Если НЕ СсылкаНаОбещание.Пустая() Тогда
    ОбъектОбещание = СсылкаНаОбещание.ПолучитьОбъект();
    ОбъектОбещание.Наименование = "Новое наименование"; 
    ОбъектОбещание.Записать();
КонецЕсли;

Важно: Менять сам Код у элемента справочника крайне не рекомендуется, так как на нем может строиться логика обмена данными или поиска в других частях системы.

Решение 3: Переход к периодическому регистру сведений

Если корень проблемы заключается в том, что вам нужно отсекать «старые» записи при появлении «новых», то использование непериодического регистра — это архитектурная ошибка. Вы пытаетесь вручную имитировать механизм, который в 1С уже реализован «из коробки».

Выясним преимущества периодического регистра сведений:

В запросе это позволит избавиться от сложных условий ПОДОБНО и ручного поиска максимальных ID. Вы просто выбираете данные на текущую дату, и система сама вернет только последнюю актуальную запись для каждого объекта.

Решение 4: Оптимизация запроса для непериодического регистра

Если по каким-то причинам вы не можете изменить структуру регистра на периодическую, проанализируем, как можно «отсечь» лишние записи в запросе. Для этого используем коррелированный подзапрос или временные таблицы, чтобы найти самую актуальную запись для каждого объекта.


ВЫБРАТЬ
    Активность.Объект КАК Объект,
    МАКСИМУМ(Активность.НомерЗаписи) КАК МаксНомер
ПОМЕСТИТЬ ВТ_АктуальныеЗаписи
ИЗ
    РегистрСведений.Активность КАК Активность
СГРУППИРОВАТЬ ПО
    Активность.Объект;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Активность.Объект,
    Активность.Значение КАК Дата,
    Активность.Реквизит
ИЗ
    РегистрСведений.Активность КАК Активность
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_АктуальныеЗаписи КАК Актуальные
    ПО Активность.Объект = Актуальные.Объект
    И Активность.НомерЗаписи = Актуальные.МаксНомер
ГДЕ
    Активность.Реквизит = "Срок"
    И ВЫРАЗИТЬ(Активность.Объект КАК Документ.АнкетаКлиента).Обещание.Код = "000000077"

Такой подход гарантирует, что если у объекта появилась более новая запись (с большим номером или ID), старая запись с типом «Срок» просто не попадет в выборку, и вам не придется физически менять её ресурсы в базе данных.

Рекомендации по именованию метаданных

Посмотрим на структуру регистра автора: Объект, Реквизит, Значение. Использование таких имен крайне нежелательно. Эти слова являются зарезервированными или часто используемыми в языке 1С. Например, Значение — это встроенная функция в запросах, а Объект — стандартное имя основного реквизита формы.

Проанализируем последствия: при написании запросов вам придется всегда использовать псевдонимы таблиц (как в примере выше: Активность.Значение), иначе система выдаст ошибку синтаксиса. Лучше использовать более специфичные имена: ДокументОснование, ТипАктивности, СодержимоеАктивности.

Итоги: алгоритм действий

Подводя итог, при возникновении необходимости массового изменения или фильтрации записей регистра, придерживайтесь следующего плана:

  1. Определите, не является ли ваша задача классическим случаем периодического регистра. Если важна история — используйте его.
  2. Если нужно программно изменить записи, используйте НаборЗаписей с установкой отбора по измерениям.
  3. Никогда не пытайтесь изменить данные через Запись.СсылкаНаОбъект.Поле = .... Сначала вызывайте ПолучитьОбъект().
  4. Проверяйте типы данных: Код — это строка, Ссылка — это уникальный идентификатор. Они не взаимозаменяемы.
  5. Если вы меняете данные в момент записи новой строки, лучше всего поместить этот код в Модуль набора записей регистра в обработчик ПередЗаписью. Это обеспечит целостность данных независимо от того, как вносится запись — через форму или программно.
← На главную