Как программно перезаполнить табличную часть связанного документа в 1С 8.3?

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

При разработке в среде 1С:Предприятие 8.3 часто возникает задача синхронизации данных между документами. Одной из классических задач является автоматическое обновление табличной части Товары в документе Таможенная декларация на импорт при изменении базового документа Поступление товаров и услуг. В этой статье мы подробно разберем, как правильно реализовать этот механизм, избежав типичных ошибок новичков, связанных с обходом выборки и логикой связей.

Анализ типичных ошибок при работе с запросами и выборками

Рассмотрим ситуацию, с которой сталкиваются многие начинающие разработчики. При попытке программно перенести данные из одного документа в другой часто допускаются следующие ошибки:

  1. Неправильный обход выборки: Использование цикла Для каждого для объекта ВыборкаИзРезультатаЗапроса. В 1С объекты выборки не являются коллекциями в привычном понимании, их нужно обходить методом Следующий().
  2. Попытка изменения данных в самой выборке: Разработчики иногда пытаются присвоить значения полям Выборка.ИмяПоля, что технически невозможно, так как выборка предназначена только для чтения.
  3. Логика связи документов: Ошибка в условии ГДЕ запроса, когда пытаются найти связанный документ по ссылке текущего документа в неверном поле.

Проанализируем пример некорректного кода, который часто встречается на практике:


// ОШИБОЧНЫЙ КОД
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать(); 
Для каждого ТекСтрока ИЗ ВыборкаДетальныеЗаписи Цикл 
    ТекСтрока.Номенклатура = ВыборкаДетальныеЗаписи.Номенклатура; // Это не будет работать
КонецЦикла;

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

Правильный алгоритм поиска связанного документа

Прежде чем что-то заполнять, нам нужно найти документ ТаможеннаяДекларацияИмпорт, который ссылается на наше ПоступлениеТоваровУслуг. Разберем, как составить правильный запрос для получения данных из ТЧ связанного документа.

Для начала исправим условие запроса. Нам нужно искать декларацию, у которой в реквизите (например, ДокументОснование или ПоступлениеТоваров) указана ссылка на наше поступление.


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	ТД.Ссылка КАК СсылкаНаДекларацию
|ИЗ
|	Документ.ТаможеннаяДекларацияИмпорт КАК ТД
|ГДЕ
|	ТД.ПоступлениеТоваров = &ПоступлениеТоваров
|	И ТД.Проведен = ИСТИНА";

Запрос.УстановитьПараметр("ПоступлениеТоваров", ТекущийОбъект.Ссылка);
РезультатЗапроса = Запрос.Выполнить();

Обратите внимание, что мы ищем именно Ссылку на документ-объект. Работа с данными в 1С всегда разделяется на работу со ссылками (для чтения и запросов) и работу с объектами (для записи и изменения).

Программное изменение табличной части: пошаговое руководство

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

Рассмотрим подробнее процесс обновления в коде:


Выборка = РезультатЗапроса.Выбрать();

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

Использование метода ЗаполнитьЗначенияСвойств

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

Важный нюанс: метод копирует только те свойства, имена которых полностью совпадают и доступны для записи. Если в ТД колонка называется Количество, а в Поступлении — КоличествоУпаковок, их придется сопоставлять вручную.

Архитектурные рекомендации: Модуль формы или Модуль объекта?

Посмотрим на ситуацию со стороны надежности системы. Автор исходного вопроса писал код в Модуле формы в процедуре ПередЗаписьюНаСервере. Мы настоятельно рекомендуем переносить такую логику в Модуль объекта, в процедуру ПриЗаписи.

Почему это важно:

  1. Если документ будет изменен программно (например, обработкой или при обмене данными), код в форме просто не сработает.
  2. Логика данных должна быть отделена от интерфейса. Форма — это лишь визуальное представление.
  3. В модуле объекта у вас есть доступ к ДополнительныеСвойства, что позволяет гибко управлять процессом записи.

Обработка блокировок и транзакций

При программном изменении другого документа (ТД) крайне важно учитывать возможность того, что этот документ в данный момент редактируется другим пользователем. Если документ заблокирован, метод ПолучитьОбъект() или Записать() вызовет исключение.

Рекомендуем использовать следующую конструкцию блокировки:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Документ.ТаможеннаяДекларацияИмпорт");
ЭлементБлокировки.УстановитьПараметр("Ссылка", Выборка.СсылкаНаДекларацию);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;

Попытка
    Блокировка.Заблокировать();
    // Далее код изменения объекта...
Исключение
    Сообщить("Документ заблокирован другим пользователем. Попробуйте позже.");
КонецПопытки;

Заключение

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

← На главную