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