При разработке в крупных конфигурациях на платформе 1С:Предприятие 8.3, таких как 1С:ERP Управление предприятием или 1С:Комплексная автоматизация, программисты часто сталкиваются с необходимостью автоматизации ввода одних документов на основании других. Стандартный механизм платформы отлично справляется с задачей, когда один документ создается на основании другого единичного документа. Но что делать, если перед нами стоит задача создать один итоговый документ (например, ЗаказПоставщику) на основании целого массива документов-источников (например, нескольких документов ПланЗакупок)?
В этой статье мы подробно разберем, как решить две ключевые проблемы, возникающие при реализации такой задачи через расширение конфигурации:
Стандартный реквизит ДокументОснование, присутствующий во многих типовых документах, имеет простой ссылочный тип. Это значит, что в него физически невозможно записать более одного документа-источника. Если мы создаем один ЗаказПоставщику на основании пяти документов ПланЗакупок, запись ссылки только на один из них в этот реквизит приведет к потере связи с остальными четырьмя. В отчете «Связанные документы» мы увидим только одну цепочку.
Для решения этой проблемы мы применим подход с использованием табличной части документа-приемника. Рассмотрим этот процесс по шагам.
Шаг 1. Создание табличной части в расширении
В расширении конфигурации добавим в документ ЗаказПоставщику новую табличную часть, назовем ее, к примеру, ДокументыОснования. Внутри этой табличной части создадим реквизит ДокументОснование со значением типа ОпределяемыйТип.ИсточникОснованиеЗаказаПоставщику или просто укажем составной тип данных, включающий ДокументСсылка.ПланЗакупок и другие необходимые типы документов.
Шаг 2. Добавление реквизита в критерий отбора «СвязанныеДокументы»
Связи между документами для отчета «Структура подчиненности» в типовых конфигурациях на БСП (Библиотеки стандартных подсистем) строятся на основе специального объекта метаданных — критерия отбора СвязанныеДокументы. Если мы просто запишем документы в нашу новую табличную часть, отчет о них ничего не узнает. Нам нужно заимствовать критерий отбора в расширение и доработать его:
СвязанныеДокументы и добавим его в наше расширение.ЗаказПоставщику, развернем его табличные части, найдем созданную нами ТЧ ДокументыОснования и установим флажок напротив реквизита ДокументОснование.Теперь платформа при формировании дерева связанных документов будет анализировать не только стандартный реквизит шапки, но и строки нашей новой табличной части. Все указанные там документы ПланЗакупок станут полноправными участниками структуры подчиненности.
Шаг 3. Регистрация документа в подсистеме БСП
Чтобы кнопка вызова отчета связанных документов отображалась на форме нашего документа, он должен быть зарегистрирован в БСП. Для этого мы переопределим процедуру в общем модуле СозданиеНаОснованииПереопределяемый. Добавим в расширение этот модуль и найдем процедуру ПриОпределенииОбъектовСКомандамиСозданияНаОсновании. Код будет выглядеть следующим образом:
&После("ПриОпределенииОбъектовСКомандамиСозданияНаОсновании")
Процедура хх_ПриОпределенииОбъектовСКомандамиСозданияНаОсновании(Объекты)
// Добавляем наш документ в список объектов, поддерживающих параметризованное создание на основании
Если Объекты.Найти(Метаданные.Документы.ЗаказПоставщику) = Неопределено Тогда
Объекты.Добавить(Метаданные.Документы.ЗаказПоставщику);
КонецЕсли;
КонецПроцедуры
Частой ошибкой разработчиков является создание обычной Общей команды в дереве метаданных расширения и ручная настройка ее свойства «Группа» в значение «Командная панель формы.Создать на основании». В БСП меню «Создать на основании» формируется динамически на клиенте при создании формы. Физическое добавление команды в метаданных в эту группу приведет к тому, что кнопка отобразится отдельно на командной панели, нарушая интерфейсные стандарты решений фирмы 1С.
Правильный путь — зарегистрировать команду программно в модуле менеджера документа-источника (в нашем случае это ПланЗакупок).
Рассмотрим, как это реализовать через расширение. Добавим в расширение модуль менеджера документа ПланЗакупок и переопределим процедуру ДобавитьКомандыСозданияНаОсновании:
&После("ДобавитьКомандыСозданияНаОсновании")
Процедура хх_ДобавитьКомандыСозданияНаОсновании(КомандыСозданияНаОсновании, Параметры) Экспорт
// Проверяем права пользователя на создание документа-приемника
Если ПравоДоступа("Добавление", Метаданные.Документы.ЗаказПоставщику) Тогда
// Создаем новое описание команды
Команда = КомандыСозданияНаОсновании.Добавить();
Команда.Идентификатор = "ВводНаОснованииПлановЗакупок";
Команда.Представление = "Заказ поставщику (на основании нескольких планов)";
// Указываем, что обработка будет выполняться через наш клиентский общий модуль
Команда.Менеджер = "ОбщийМодуль.хх_РаботаСДокументамиКлиент";
Команда.Обработчик = "СоздатьЗаказНаОснованииПланов";
// Очень важный флаг для работы с массивом документов!
Команда.МножественныйВыбор = Истина;
// Указываем режим проведения для создаваемого документа (необязательно)
Команда.РежимЗаписи = "Записать";
КонецЕсли;
КонецПроцедуры
Разберем ключевые свойства программной команды:
Менеджер — строковое имя общего клиентского модуля, в котором расположен код вызова формы. Этот модуль должен иметь установленный флаг «Клиент (управляемое приложение)».Обработчик — имя экспортной процедуры в указанном модуле, которая будет запущена при нажатии кнопки в меню.МножественныйВыбор — значение Истина позволяет пользователю выделить в списке несколько документов ПланЗакупок и запустить генерацию одного заказа для всех выделенных позиций.Теперь нам необходимо создать общий модуль в расширении. Назовем его хх_РаботаСДокументамиКлиент, установим у него свойства «Клиент (управляемое приложение)». Внутри этого модуля напишем экспортную процедуру-обработчик:
Процедура СоздатьЗаказНаОснованииПланов(ПараметрыВыполнения, ДополнительныеПараметры) Экспорт
// БСП передает в ПараметрыВыполнения структуру.
// ПараметрыВыполнения.ПолучателиПараметровВыполнения содержит массив ссылок выделенных документов-источников.
МассивПланов = ПараметрыВыполнения.ПолучателиПараметровВыполнения;
Если МассивПланов.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Не выбраны документы-основания для создания Заказа поставщику!");
Возврат;
КонецЕсли;
// Формируем параметры для открытия формы нового документа Заказ поставщику
ПараметрыФормы = Новый Структура;
// Передаем весь массив документов-оснований в параметр "Основание"
ПараметрыФормы.Вставить("Основание", МассивПланов);
// Открываем форму нового документа Заказ поставщику
ОткрытьФорму("Документ.ЗаказПоставщику.ФормаОбъекта", ПараметрыФормы, ПараметрыВыполнения.Источник);
КонецПроцедуры
В этом коде мы извлекаем массив выделенных планов закупок из свойства ПолучателиПараметровВыполнения, упаковываем его в структуру параметров формы под ключом "Основание" и открываем форму нового документа ЗаказПоставщику. В качестве владельца формы передаем ПараметрыВыполнения.Источник (это форма списка или форма документа, откуда была вызвана команда).
Когда форма нового документа открывается, платформа автоматически инициирует создание нового объекта на сервере и вызывает событие ОбработкаЗаполнения в модуле объекта документа ЗаказПоставщику. Нам необходимо перехватить это событие в расширении и заполнить как табличную часть связей, так и товарную часть.
Добавим в расширение модуль объекта документа ЗаказПоставщику и напишем обработчик:
&Перед("ОбработкаЗаполнения")
Процедура хх_ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
// Проверяем, что в качестве данных заполнения пришел массив
Если ТипЗнч(ДанныеЗаполнения) = Тип("Массив") Тогда
// Отключаем стандартную обработку платформы, так как мы полностью берем заполнение на себя
СтандартнаяОбработка = Ложь;
Для Каждого СсылкаОснование Из ДанныеЗаполнения Цикл
// Убедимся, что элемент массива имеет нужный нам тип
Если ТипЗнч(СсылкаОснование) = Тип("ДокументСсылка.ПланЗакупок") Тогда
// 1. Фиксируем связь в созданной нами табличной части для структуры подчиненности
НоваяСтрокаСвязи = ДокументыОснования.Добавить();
НоваяСтрокаСвязи.ДокументОснование = СсылкаОснование;
// 2. Вызываем процедуру заполнения товаров по данному плану закупок
ЗаполнитьТоварыПоПлануЗакупок(СсылкаОснование);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Вспомогательный метод ЗаполнитьТоварыПоПлануЗакупок может выглядеть следующим образом (в зависимости от ваших бизнес-требований к переносу табличных частей):
Процедура ЗаполнитьТоварыПоПлануЗакупок(ПланЗакупокСсылка)
// Пример запроса для получения спецификации или товаров из Плана закупок
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПланЗакупокТовары.Номенклатура КАК Номенклатура,
| ПланЗакупокТовары.Количество КАК Количество,
| ПланЗакупокТовары.Цена КАК Цена
|ИЗ
| Документ.ПланЗакупок.Товары КАК ПланЗакупокТовары
|ГДЕ
| ПланЗакупокТовары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ПланЗакупокСсылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Ищем, нет ли уже такой номенклатуры в заказе, чтобы объединить строки
ПараметрыПоиска = Новый Структура("Номенклатура", Выборка.Номенклатура);
НайденныеСтроки = Товары.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
СтрокаТовара = НайденныеСтроки[0];
СтрокаТовара.Количество = СтрокаТовара.Количество + Выборка.Количество;
Иначе
СтрокаТовара = Товары.Добавить();
СтрокаТовара.Номенклатура = Выборка.Номенклатура;
СтрокаТовара.Количество = Выборка.Количество;
СтрокаТовара.Цена = Выборка.Цена;
КонецЕсли;
// Рассчитываем сумму и другие зависимые реквизиты строки
СтрокаТовара.Сумма = СтрокаТовара.Количество * СтрокаТовара.Цена;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Комбинируя возможности платформы и стандарты БСП, мы получили чистое и расширяемое решение. Мы избежали изменения типовой конфигурации на уровне кода, сохранив полную поддержку обновлений. Теперь пользователи могут выделить в списке несколько планов закупок, нажать стандартную кнопку «Создать на основании» -> «Заказ поставщику (на основании нескольких планов)», и система автоматически создаст один общий заказ, заполнив его спецификацию и сохранив корректные ссылки на все документы-основания в структуре подчиненности.