Как создать документ на основании нескольких выделенных объектов и передать их структуру?

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

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

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

Анализ стандартного поведения платформы

Давайте сначала разберемся, как работает платформа "из коробки". Когда вы указываете в свойствах объекта, что он вводится на основании другого документа, платформа автоматически добавляет команду "Создать на основании" в командную панель формы списка документа-основания.

Если пользователь выделит в списке несколько строк (например, 3 счета) и нажмет эту кнопку, платформа выполнит следующие действия:

  1. Определит список выделенных ссылок.
  2. Для каждой ссылки по очереди создаст новый документ в памяти.
  3. Для каждого нового документа вызовет событие ОбработкаЗаполнения, передав туда одну конкретную ссылку из списка выделенных.
  4. Откроет формы новых документов (получится 3 открытых окна).

Именно поэтому, когда вы пытаетесь отловить массив ссылок внутри процедуры ОбработкаЗаполнения, вы видите там только ДокументСсылка. Это стандартное поведение платформы: "Один документ основания — один создаваемый документ".

Постановка задачи

Наша цель — изменить это поведение. Мы хотим, чтобы при выделении нескольких документов открывалась одна форма нового документа, а табличная часть этого документа заполнялась данными из всех выбранных оснований.

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

Шаг 1. Создание пользовательской команды

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

Алгоритм действий:

  1. В дереве метаданных находим документ-основание (например, "СчетПокупателю").
  2. Создаем команду (например, СоздатьСпецификациюНаОсновании).
  3. Размещаем эту команду в командной панели формы списка, в группе "Создать на основании", чтобы для пользователя интерфейс остался привычным.
  4. Устанавливаем свойство "Изменяет данные" в значение Ложь (обычно для создания на основании это не требуется).
  5. Тип параметра команды можно задать как Массив (состоящий из ссылок на документ-основание), чтобы команда была доступна при множественном выделении.

Шаг 2. Реализация клиентской логики (Сбор данных)

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

Рассмотрим код модуля команды:


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

КонецПроцедуры

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

Также можно передать структуру внутри структуры, если вам нужно передать дополнительные параметры:


СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("Основание", МассивОснований);
СтруктураДанных.Вставить("ДополнительныйПараметр", Истина);

ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", СтруктураДанных);

Шаг 3. Обработка данных на сервере

Теперь перейдем в модуль объекта создаваемого документа (в нашем примере это "Спецификация"). Нам нужно адаптировать процедуру ОбработкаЗаполнения, чтобы она умела работать как с одиночной ссылкой (если документ создается программно или стандартно), так и с массивом/структурой, который мы передаем из нашей команды.

Разберем пример кода модуля объекта:


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

Важные нюансы реализации

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

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

Резюме

Для передачи структуры или массива выделенных документов при создании на основании необходимо:

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

← На главную