При работе с конфигурациями на базе БСП (Библиотека стандартных подсистем), такими как ERP 2.4, Комплексная автоматизация или Управление торговлей 11, разработчики часто сталкиваются с проблемой: программно добавленное значение в табличную часть ДополнительныеРеквизиты исчезает после повторного нажатия кнопки "Записать" или при определенных действиях на форме. Разберем, почему это происходит и как реализовать задачу правильно.
Проанализируем ситуацию: когда вы открываете форму объекта, подсистема "Свойства" считывает данные из базы и кэширует их в памяти формы (в специальных реквизитах формы или коллекциях). Если вы используете процедуру ПередЗаписьюНаСервере в расширении формы и напрямую модифицируете ТекущийОбъект.ДополнительныеРеквизиты, данные попадают в объект и записываются в базу. Однако внутреннее состояние подсистемы свойств в памяти формы не обновляется.
В результате, если пользователь нажимает кнопку "Записать" второй раз, не закрывая форму, механизм БСП снова берет данные из своего "старого" кэша (где ваш реквизит еще пустой) и перезаписывает ими объект перед сохранением. Именно поэтому при открытии формы данные видны (они считались из базы), а после второго сохранения "внутри" текущей сессии формы — пропадают.
Самый надежный и архитектурно верный способ — вынести заполнение даты создания в модуль объекта (через расширение). Это гарантирует, что код сработает при записи объекта из любого места: из формы, из обработки или при загрузке через API.
Рассмотрим пример реализации в расширении модуля объекта ДоговорыКонтрагентов:
&После("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ, Замещение, ПараметрыЗаписи)
// Проверяем, что это новый объект (еще не записан в базу)
Если ЭтоНовый() Тогда
// Поиск свойства по наименованию (лучше использовать предопределенный идентификатор, если возможно)
СвойствоДатаСоздания = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ДатаСоздания");
Если Не СвойствоДатаСоздания.Пустая() Тогда
// Ищем, нет ли уже такой строки (на случай повторного вызова)
СтрокаРеквизита = ДополнительныеРеквизиты.Найти(СвойствоДатаСоздания, "Свойство");
Если СтрокаРеквизита = Неопределено Тогда
НовыйРеквизит = ДополнительныеРеквизиты.Добавить();
НовыйРеквизит.Свойство = СвойствоДатаСоздания;
НовыйРеквизит.Значение = ТекущаяДата();
Иначе
// Если строка уже есть, но значение пустое — заполняем
Если Не ЗначениеЗаполнено(СтрокаРеквизита.Значение) Тогда
СтрокаРеквизита.Значение = ТекущаяДата();
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Использование метода ЭтоНовый() внутри модуля объекта работает стабильно и корректно определяет момент первого создания договора.
Если вам необходимо выполнить запись именно в контексте формы или из внешней обработки, лучше использовать штатные функции БСП. Это позволит избежать прямого обращения к табличным частям и минимизирует риск ошибок синхронизации данных.
Разберем использование метода УправлениеСвойствами.ЗаписатьСвойствоУОбъекта. Этот метод принудительно записывает значение в базу данных.
Пример кода:
// Определяем ссылку на свойство
СвойствоОбъекта = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ДатаСоздания");
// Записываем значение напрямую в базу для конкретного объекта
Если ЗначениеЗаполнено(СвойствоОбъекта) Тогда
УправлениеСвойствами.ЗаписатьСвойствоУОбъекта(СсылкаНаОбъект, СвойствоОбъекта, ТекущаяДата());
КонецЕсли;
Важный момент: этот метод подходит, когда у вас уже есть ссылка (объект записан). Если же объект только создается, используйте Решение 1.
Если логику необходимо оставить в ПередЗаписьюНаСервере на форме, нужно учитывать, что форма управляет отображением доп. реквизитов через специальные динамические реквизиты. Чтобы данные не затирались, после программного изменения ТекущийОбъект.ДополнительныеРеквизиты, необходимо обновить коллекцию свойств в параметрах формы.
Проанализируем шаги, которые помогут сохранить стабильность:
НайтиПоНаименованию может вернуть дубликат из другого справочника.ДополнительныеРеквизиты перед добавлением, чтобы не плодить одинаковые строки.Рассмотрим правильный код для формы (с проверкой существующей записи):
&После("ПередЗаписьюНаСервере")
Процедура Расш1_ПередЗаписьюНаСервереПосле(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Проверка на новый объект в контексте формы
Если ТекущийОбъект.Ссылка.Пустая() Тогда
СвойствоСсылка = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ДатаСоздания");
Если ЗначениеЗаполнено(СвойствоСсылка) Тогда
// Пытаемся найти существующую строку, чтобы не дублировать
Отбор = Новый Структура("Свойство", СвойствоСсылка);
НайденныеСтроки = ТекущийОбъект.ДополнительныеРеквизиты.НайтиСтроки(Отбор);
Если НайденныеСтроки.Количество() = 0 Тогда
НовыйРеквизит = ТекущийОбъект.ДополнительныеРеквизиты.Добавить();
НовыйРеквизит.Свойство = СвойствоСсылка;
НовыйРеквизит.Значение = ТекущаяДата();
Иначе
// Если строка уже создана в этом сеансе, просто обновляем значение
НайденныеСтроки[0].Значение = ТекущаяДата();
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Выясним причину, почему один метод лучше другого. В 1С:ERP работа со свойствами сильно завязана на подсистему УправлениеСвойствами. Прямая правка табличной части — это крайняя мера.
Если вам нужно просто логировать дату создания, возможно, стоит рассмотреть использование Дополнительных сведений вместо Дополнительных реквизитов. Сведения хранятся в отдельном регистре, не раздувают таблицу справочника и не конфликтуют с экранными формами так остро, как реквизиты.
Основные выводы:
ЗначениеЗаполнено() для найденных свойств, чтобы избежать ошибок с пустыми ссылками ПланыВидовХарактеристик.Соблюдение этих правил позволит вам создавать надежные расширения для ERP, которые не будут "терять" данные при работе пользователей.