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