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