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

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

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

Рассмотрим детально, как реализовать этот функционал в конфигурации на обычных формах, используя программные методы платформы 1С 8.3.

Шаг 1. Создание механизма запуска в документе-основании

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

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

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


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

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

Шаг 2. Модификация логики заполнения в документе-приемнике

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

Проанализируем, как перехватить данные, переданные через ДополнительныеСвойства:


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

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

Важные технические нюансы

При реализации такой задачи необходимо учитывать несколько критических моментов, которые обеспечат стабильную работу системы и корректность учета:

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

Альтернативный вариант: Внешняя обработка

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

В этом случае вся логика переносится во внешний файл .epf. Пользователь нажимает «Заполнить» -> «Выбрать из внешних обработок», и ваша обработка сама формирует нужные документы, не затрагивая основной код конфигурации. Это наиболее «чистый» способ с точки зрения архитектуры 1С.

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

← На главную