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