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

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

Разработка в системе «1С:Предприятие 8» часто ставит перед начинающими специалистами задачи, которые кажутся интуитивно понятными, но при первой же попытке реализации вызывают массу трудностей. Типичный пример — необходимость перенести текстовый реквизит из строки табличной части документа-источника в аналогичную строку нового документа, создаваемого на его основании.

Стандартные учебники часто описывают лишь использование простого «Конструктора копирования», который работает без сбоев только в простейших учебных базах. На практике, работая с реальными конфигурациями (такими как «1С:Бухгалтерия предприятия» или «1С:ERP»), программисты сталкиваются с тем, что механизмы заполнения уже написаны вручную, используют Библиотеку стандартных подсистем (БСП) и требуют четкого понимания взаимодействия контекстов. Рассмотрим подробнее, почему возникают популярные ошибки и как правильно реализовать эту задачу несколькими способами.

Разбор критических ошибок начинающих разработчиков

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

  1. Написание логики заполнения в модуле формы. Это самая грубая архитектурная ошибка. Модуль формы предназначен исключительно для визуализации данных и интерактивного общения с пользователем. Процесс создания документа на основании (ввод на основании) — это бизнес-логика, которая должна выполняться на уровне базы данных. Документ может создаваться программно (например, регламентным заданием или внешней обработкой) вообще без открытия формы. В таком случае код, написанный в модуле формы, просто не выполнится.
  2. Использование запроса с параметром Объект.Ссылка. Автор пытался сделать запрос к базе данных, передавая в параметры ссылку на текущий создаваемый документ: Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка). Но в момент создания нового документа на основании другого, этот новый документ еще не записан в базу данных. Его ссылка в памяти компьютера пуста (точнее, представляет собой так называемую пустую ссылку данного типа). Соответственно, запрос к базе данных по пустой ссылке всегда будет возвращать нулевой результат.
  3. Попытка работы с табличной частью как с простым реквизитом. Конструкция вида Объект.ТоварыИУслуги.Содержание = ... вызывает синтаксическую ошибку выполнения. Поле ТоварыИУслуги — это не строка, а коллекция (объект типа ТабличнаяЧасть). У самой коллекции не существует свойства Содержание. Данное свойство принадлежит исключительно конкретным элементам этой коллекции — строкам (объектам типа СтрокаТабличнойЧасти). Чтобы присвоить значение, необходимо сначала спозиционироваться на нужной строке табличной части.
  4. Избыточные запросы к базе данных. Для получения данных документа-источника делать запрос не требуется. Платформа устроена так, что весь документ-источник со всеми его реквизитами шапки и табличными частями передается напрямую в обработчик заполнения в качестве параметра. Обращение к базе данных через запрос в данном случае является лишней нагрузкой на систему.

Где должна располагаться логика заполнения?

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

Давайте посмотрим на стандартный шаблон этого обработчика в модуле объекта:


Процедура ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
    
    // Переменная ДанныеЗаполнения — это и есть наш документ-источник.
    // Если документ вводится на основании другого, этот параметр содержит ссылку на документ-основание.
    
КонецПроцедуры

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


Процедура ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
    
    // Стандартные проверки БСП
    Если ДанныеЗаполнения = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Вызов типового заполнения реквизитов БСП
    ЗаполнениеДокументов.Заполнить(ЭтотОбъект, ДанныеЗаполнения);
    
    // НАШ КОД ДОЛЖЕН НАХОДИТЬСЯ ЗДЕСЬ
    // Мы пишем логику заполнения после того, как БСП уже создала строки табличной части,
    // но еще не заполнила наши нетиповые реквизиты.
    
КонецПроцедуры

Вариант 1. Построчный перенос по индексу (рекомендуемый для стандартных сценариев)

Рассмотрим самый простой и частый сценарий. Нам нужно заполнить реквизит Содержание в табличной части нового документа на основе соответствующей строки из табличной части исходного документа. При этом мы уверены, что структура и порядок строк в документах полностью идентичны (так как типовой механизм БСП уже скопировал строки «один в один»).

Для реализации этого сценария мы применим обход строк по индексу. Разберем этот пример кода:


Процедура ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
    
    // Сначала даем отработать базовому коду конфигурации
    ЗаполнениеДокументов.Заполнить(ЭтотОбъект, ДанныеЗаполнения);
    
    // Проверяем, что документ заполняется именно на основании Реализации товаров и услуг
    Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
        
        // Организуем цикл по строкам табличной части НАШЕГО нового документа
        Для Индекс = 0 По ЭтотОбъект.Товары.Количество() - 1 Цикл
            
            // На всякий случай проверяем, что в исходном документе есть строка с таким же индексом
            Если Индекс < ДанныеЗаполнения.Товары.Количество() Тогда
                
                // Получаем строку из документа-источника (Реализации)
                СтрокаИсточник = ДанныеЗаполнения.Товары[Индекс];
                
                // Получаем соответствующую строку в текущем документе
                СтрокаПриемник = ЭтотОбъект.Товары[Индекс];
                
                // Переносим значение нашего текстового реквизита "Содержание"
                СтрокаПриемник.Содержание = СтрокаИсточник.Содержание;
                
            КонецЕсли;
            
        КонецЦикла;
        
    КонецЕсли;
    
КонецПроцедуры

Вариант 2. Перенос с поиском по ключевому полю (номенклатуре)

Что делать, если в документе-получателе состав строк может отличаться от документа-источника? Например, пользователь при вводе на основании мог частично удалить строки или изменить их порядок. В таком случае метод поиска по индексу приведет к тому, что тексты скопируются некорректно или запишутся не в те товары.

Для надежного сопоставления строк проанализируем ситуацию и выполним поиск по ключевому полю — номенклатурной позиции (реквизиту Номенклатура):


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

Вариант 3. Использование механизма расширений (современный стандарт)

Поскольку автор исходного вопроса упомянул, что разработка ведется в типовой конфигурации «1С:Бухгалтерия», крайне не рекомендуется вносить изменения непосредственно в код основной конфигурации. Это усложнит процедуру последующих обновлений программы. Правильным и профессиональным подходом будет использование Расширений конфигурации.

Давайте разберем по шагам, как решить нашу задачу через механизм расширений:

  1. Создайте новое расширение в конфигураторе (меню Конфигурация — Расширения конфигурации — Добавить).
  2. Добавьте в расширение ваш целевой документ (нажмите правой кнопкой на документ в дереве основной конфигурации и выберите Добавить в расширение).
  3. В расширении откройте модуль объекта этого документа.
  4. Создайте перехватчик для процедуры ОбработкаЗаполнения с аннотацией &После. Это позволит выполнить ваш собственный код сразу после того, как отработают все стандартные типовые механизмы заполнения.

Пример программного кода для модуля объекта в расширении будет выглядеть следующим образом:


&После("ОбработкаЗаполнения")
Процедура Ип_ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
    
    // Нам не нужно вызывать метод Заполнить() вручную, БСП уже отработала до нас.
    // Наша задача — просто дописать заполнение реквизита "Содержание".
    
    Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
        
        Для Индекс = 0 По ЭтотОбъект.Товары.Количество() - 1 Цикл
            
            Если Индекс < ДанныеЗаполнения.Товары.Количество() Тогда
                
                СтрокаИсточник = ДанныеЗаполнения.Товары[Индекс];
                СтрокаПриемник = ЭтотОбъект.Товары[Индекс];
                
                // Переносим данные реквизита табличной части
                СтрокаПриемник.Содержание = СтрокаИсточник.Содержание;
                
            КонецЕсли;
            
        КонецЦикла;
        
    КонецЕсли;
    
КонецПроцедуры

Рекомендации по отладке кода заполнения

Чтобы понять, как ведут себя переменные в процессе выполнения программы, активно используйте встроенный отладчик 1С:

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

← На главную