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