Как программно обновить данные в связанном документе 1С

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие часто возникает задача, когда при изменении одного документа (например, Служебной записки) необходимо автоматически обновить данные в другом, связанном с ним документе (например, во Внутреннем счете). Начинающие разработчики часто сталкиваются с трудностями при попытке изменить реквизиты через ссылку, не понимая разницы между ссылочным типом данных и объектом.

В данной статье мы подробно разберем, как правильно реализовать обновление данных связанного документа, как оптимизировать этот процесс и избежать типичных ошибок, приводящих к падению системы или потере производительности.

Различие между Ссылкой и Объектом

Прежде всего, проанализируем ситуацию: у нас есть основной объект (например, ДокументОбъект.СлужебнаяЗаписка), в реквизите которого хранится ссылка на другой документ (ВнутреннийСчет). Попытка напрямую присвоить значение реквизиту через точку от ссылки, например Объект.ВнутреннийСчет.СрокДействия = Дата, не приведет к результату. Ссылка — это лишь указатель на запись в базе данных, она доступна только для чтения. Для внесения изменений нам необходимо получить Объект.

Рассмотрим правильный алгоритм действий:

  1. Проверить, заполнена ли ссылка на связанный документ.
  2. Получить объект документа из базы данных с помощью метода ПолучитьОбъект().
  3. Внести необходимые изменения в реквизиты объекта.
  4. Записать измененный объект.

Реализация обновления через ПолучитьОбъект()

Давайте разберем по шагам код, который позволяет обновить данные. Предположим, что данные мы получаем из какой-то функции или из текущего контекста основного документа. Самый надежный способ сделать это на сервере выглядит следующим образом:


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

Обратите внимание на использование блока Попытка...Исключение. Это критически важно при записи связанных документов, так как объект может быть заблокирован другим пользователем или не пройти проверку заполнения, что вызовет ошибку выполнения кода.

Оптимизация кода и работа с переменными

Проанализируем распространенную ошибку, когда разработчик обращается к одной и той же функции или свойству многократно. В исходном обсуждении было замечено, что вызов функции КопВнутренийСчет() происходил в каждой строке при присвоении значений. Это крайне неэффективно, так как каждый вызов функции (особенно если внутри нее запрос) создает лишнюю нагрузку на сервер.

Выясним, как сделать код чище и быстрее. Мы можем один раз получить данные в переменную (структуру или выборку запроса) и один раз получить объект для записи. Посмотрим на оптимизированный пример:


// 1. Получаем данные один раз в переменную
ДанныеДляЗаполнения = ПолучитьДанныеДляСчета(Объект.Ссылка); 

// 2. Кэшируем объект счета в переменную, чтобы не обращаться к базе многократно
СвязанныйДокумент = Объект.ВнутреннийСчет.ПолучитьОбъект();

Если СвязанныйДокумент <> Неопределено Тогда
    // 3. Используем локальную переменную для заполнения
    СвязанныйДокумент.Наименование = ДанныеДляЗаполнения.Наименование;
    СвязанныйДокумент.Заказчик     = ДанныеДляЗаполнения.Заказчик;
    СвязанныйДокумент.Подразделение = ДанныеДляЗаполнения.Подразделение;
    
    // 4. Записываем
    СвязанныйДокумент.Записать();
КонецЕсли;

Такой подход сокращает количество обращений к метаданным и ускоряет работу скрипта, особенно если реквизитов для обновления десятки.

Использование системной функции ЗаполнитьЗначенияСвойств()

Если имена реквизитов в вашей Служебной записке (или в структуре, которую возвращает ваш запрос) совпадают с именами реквизитов во Внутреннем счете, мы можем значительно сократить код, используя встроенный метод ЗаполнитьЗначенияСвойств().

Рассмотрим, как это упрощает жизнь программисту:


ОбъектСчета = Объект.ВнутреннийСчет.ПолучитьОбъект();
Если ОбъектСчета <> Неопределено Тогда
    // Копируем все совпадающие поля из текущего объекта в объект счета
    ЗаполнитьЗначенияСвойств(ОбъектСчета, Объект);
    
    // Если есть специфические поля, которые называются иначе, дозаполняем их вручную
    ОбъектСчета.СуммаДокумента = Объект.ОбщийИтог;
    
    ОбъектСчета.Записать();
КонецЕсли;

Этот метод не только делает код более читаемым, но и предотвращает ошибки, связанные с опечатками в названиях переменных при ручном копировании каждого поля.

Нюансы контекста выполнения: Клиент и Сервер

Важно помнить, что метод ПолучитьОбъект() доступен исключительно на сервере. Если вы пытаетесь обновить связанный документ из формы, убедитесь, что процедура имеет префикс &НаСервере или &НаСервереБезКонтекста.

Проанализируем ситуацию: если пользователь нажимает кнопку на форме «Обновить данные счета», последовательность должна быть такой:

  1. Вызывается клиентская процедура (обработчик команды кнопки).
  2. Из нее вызывается серверная процедура, куда передается ссылка или весь объект.
  3. На сервере происходит получение объекта связанного документа, его модификация и запись.
  4. После возврата на клиент может потребоваться обновить данные текущей формы, если ссылка на счет отображается в динамическом списке или как часть данных.

Для того чтобы система «узнала» об изменениях в базе данных и обновила списки у других пользователей, рекомендуется использовать метод ОповеститьОбИзменении(Объект.ВнутреннийСчет) после завершения серверного вызова.

Транзакционность и безопасность данных

При обновлении данных в другом документе мы всегда должны думать о целостности. Если основной документ не удалось записать, то и связанные данные во втором документе, скорее всего, не должны обновляться. Если вы выполняете этот код внутри подписки на событие ПриЗаписи или в модуле объекта основного документа, 1С автоматически обернет все действия в одну транзакцию.

Однако, если обновление происходит «по кнопке», следует учитывать риск блокировок. Если в момент выполнения вашего кода другой пользователь открыл тот самый «Внутренний счет» и начал его редактировать, вы получите ошибку блокировки. Использование метода ПолучитьОбъект() устанавливает так называемую оптимистическую блокировку, которая проверяет версию объекта перед записью.

Подведем итог: для успешного обновления данных документа необходимо работать именно с Объектом, полученным по ссылке, минимизировать количество вызовов функций за счет использования переменных и не забывать про обработку исключений при вызове метода Записать(). Использование ЗаполнитьЗначенияСвойств() является хорошим тоном и упрощает поддержку кода в будущем.

← На главную