Как реализовать заполнение документа на основании нескольких документов в 1С на обычных формах

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

В практике разработчика 1С часто возникает задача автоматизации заполнения документов. Стандартный механизм "Заполнить на основании" в типовых конфигурациях обычно рассчитан на одного родителя. Однако бизнес-процессы часто требуют объединения данных из нескольких документов-оснований в один итоговый. Рассмотрим, как реализовать такую задачу для конфигураций на Обычных формах (например, УТ 10.3, УПП 1.3 или БП 2.0) с помощью внешней обработки заполнения табличной части.

Постановка задачи и выбор архитектуры

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

  1. Внешняя обработка с типом "Заполнение объекта".
  2. Форма обработки, на которой пользователь сможет управлять списком выбранных документов.
  3. Алгоритм сбора данных, использующий запросы к табличным частям документов-источников.
  4. Механизм переноса данных в объект назначения.

Шаг 1. Создание структуры внешней обработки

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

Если ваша конфигурация поддерживает механизм дополнительных отчетов и обработок, в модуле объекта необходимо прописать функцию регистрации. Рассмотрим пример кода:


Функция СведенияОВнешнейОбработке() Экспорт
    ПараметрыРегистрации = Новый Структура;
    Назначения = Новый Массив;
    Назначения.Добавить("Документ.ЗаказПокупателя"); // Указываем, для какого документа доступна обработка
    
    ПараметрыРегистрации.Вставить("Вид", "ЗаполнениеОбъекта");
    ПараметрыРегистрации.Вставить("Назначение", Назначения);
    ПараметрыРегистрации.Вставить("Наименование", "Заполнение заказа из нескольких возвратов");
    ПараметрыРегистрации.Вставить("Версия", "1.0");
    
    Команды = Новый ТаблицаЗначений;
    Команды.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка"));
    Команды.Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("Строка"));
    Команды.Колонки.Добавить("Использование", Новый ОписаниеТипов("Строка"));
    
    НоваяКоманда = Команды.Добавить();
    НоваяКоманда.Представление = "Заполнить из нескольких возвратов...";
    НоваяКоманда.Идентификатор = "ЗаполнитьИзВозвратов";
    НоваяКоманда.Использование = "ВызовКлиентскогоМетода";
    
    ПараметрыРегистрации.Вставить("Команды", Команды);
    Возврат ПараметрыРегистрации;
КонецФункции

Шаг 2. Реализация интерфейса выбора документов

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

Выясним, как программно вызвать форму выбора с предустановленным фильтром и множественным выбором:


Процедура Инициализировать(Объект, ИмяТабличнойЧасти, ТабличноеПолеОбъекта) Экспорт
    // Получаем форму обработки
    Форма = ПолучитьФорму("Форма");
    Форма.ДокументОбъект = Объект; // Передаем ссылку на заполняемый документ
    Форма.ОткрытьМодально();
КонецПроцедуры

На самой форме обработки создадим кнопку "Добавить документы". Проанализируем код, который позволит пользователю выбрать сразу несколько строк из списка:


Процедура КнопкаДобавитьНажатие(Элемент)
    ФормаВыбора = Документы.ВозвратТоваровОтПокупателя.ПолучитьФормуВыбора();
    ФормаВыбора.МножественныйВыбор = Истина;
    
    // Устанавливаем отбор по нужному реквизиту
    ФормаВыбора.Отбор.ИспользоватьДляЗаказа.Установить(Истина);
    
    ВыбранныеДанные = ФормаВыбора.ОткрытьМодально();
    
    Если ТипЗнч(ВыбранныеДанные) = Тип("Массив") Тогда
        Для Каждого СсылкаИзСписка Из ВыбранныеДанные Цикл
            НоваяСтрока = СписокДокументов.Добавить();
            НоваяСтрока.ДокументСсылка = СсылкаИзСписка;
            НоваяСтрока.Пометка = Истина;
        КонецЦикла;
    ИначеЕсли ЗначениеЗаполнено(ВыбранныеДанные) Тогда
        НоваяСтрока = СписокДокументов.Добавить();
        НоваяСтрока.ДокументСсылка = ВыбранныеДанные;
        НоваяСтрока.Пометка = Истина;
    КонецЕсли;
КонецПроцедуры

Шаг 3. Сбор и обработка данных из документов-оснований

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


Функция ПолучитьДанныеДляЗаполнения()
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |   ВозвратТовары.Номенклатура КАК Номенклатура,
    |   ВозвратТовары.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    |   СУММА(ВозвратТовары.Количество) КАК Количество,
    |   СРЕДНЕЕ(ВозвратТовары.Цена) КАК Цена,
    |   СУММА(ВозвратТовары.Сумма) КАК Сумма
    |ИЗ
    |   Документ.ВозвратТоваровОтПокупателя.Товары КАК ВозвратТовары
    |ГДЕ
    |   ВозвратТовары.Ссылка В(&МассивСсылок)
    |СГРУППИРОВАТЬ ПО
    |   ВозвратТовары.Номенклатура,
    |   ВозвратТовары.ЕдиницаИзмерения";
    
    МассивСсылок = Новый Массив;
    Для Каждого Стр Из СписокДокументов Цикл
        Если Стр.Пометка Тогда
            МассивСсылок.Добавить(Стр.ДокументСсылка);
        КонецЕсли;
    КонецЦикла;
    
    Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
    Возврат Запрос.Выполнить().Выгрузить();
КонецФункции

Шаг 4. Заполнение итогового документа

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


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

Важные нюансы и советы по отладке

Рассмотрим подробнее проблемы, с которыми сталкиваются разработчики при реализации подобных обработок:

1. Ошибка типов данных

Частая ошибка — несоответствие типов в колонках табличной части обработки и документа. Проверьте, что в вашей ТЧ реквизит Номенклатура имеет тип СправочникСсылка.Номенклатура, а не Строка. Как показывает практика, использование строки вместо ссылки — одна из самых частых причин, по которым запрос возвращает пустой результат или данные не загружаются в документ.

2. Работа с сериями и характеристиками

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

3. Очистка перед заполнением

Перед началом цикла добавления новых строк рекомендуется спросить пользователя: "Очистить существующую табличную часть?". Если ответ положительный, используйте метод ДокументОбъект.Товары.Очистить(). Это предотвратит дублирование данных при повторном запуске обработки.

4. Использование отладки

Программирование без отладки в обычных формах крайне затруднительно. Используйте "Точку останова" в процедуре сбора данных. Проанализируйте содержимое переменной МассивСсылок и результат выполнения запроса. Если запрос ничего не находит, проверьте параметры ГДЕ. Возможно, в документах "Возврат" не установлены флаги, по которым вы фильтруете данные.

5. Автоматическое заполнение цен

При переносе данных из нескольких документов цены могут различаться. В примере выше мы использовали СРЕДНЕЕ(Цена), однако в реальных задачах может потребоваться пересчет цен по актуальному прайс-листу. В таком случае после загрузки номенклатуры стоит вызвать типовую процедуру конфигурации ПриИзмененииНоменклатуры для каждой строки, чтобы подтянулись актуальные цены, счета учета и ставки НДС.

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

← На главную