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

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача интерактивного разделения данных. Типичный сценарий: пользователь заполняет объемный документ (например, «Заказ клиента»), но в процессе понимает, что товары должны быть отгружены от разных организаций или по разным условиям налогообложения. При этом документ еще не записан в базу данных, и плодить «мусорные» записи не хочется. Рассмотрим подробнее, как реализовать механизм, который позволит «раскидать» строки табличной части по нескольким новым формам, сохранив при этом все данные шапки и корректность расчетов.

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

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

Проанализируем основные этапы решения этой задачи:

  1. Сбор данных из текущей формы на клиенте и передача их на сервер.
  2. Обработка данных на сервере (разделение на логические части).
  3. Передача подготовленных наборов данных обратно на клиент.
  4. Открытие новых форм документов с заполнением из переданных данных.

Метод 1: Использование временного хранилища и параметра ЗначенияЗаполнения

Это наиболее «чистый» и методически верный способ в архитектуре управляемого приложения. Вместо того чтобы пытаться передать огромные структуры между клиентом и сервером в параметрах формы, мы воспользуемся Временным хранилищем.

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


// Функция на сервере, вызываемая из формы
&НаСервере
Функция ПодготовитьДанныеДляРаспределения(ДанныеФормыОбъект)
    
    // 1. Преобразуем данные формы в реальный объект (без записи в БД)
    ДокОбъект = ДанныеФормыВЗначение(ДанныеФормыОбъект, Тип("ДокументОбъект.ЗаказКлиента"));
    
    // 2. Создаем массив, где будем хранить структуры для новых документов
    МассивНовыхДокументов = Новый Массив;
    
    // 3. Логика разделения (пример: по ставке НДС)
    ТаблицаТоваров = ДокОбъект.Товары.Выгрузить();
    СтавкиНДС = ТаблицаТоваров.Скопировать();
    СтавкиНДС.Свернуть("СтавкаНДС");
    
    Для Каждого СтрокаНДС Из СтавкиНДС Цикл
        
        // Создаем структуру с данными для заполнения нового документа
        ДанныеЗаполнения = Новый Структура;
        ДанныеЗаполнения.Вставить("ЗаполнениеИзОбработки", Истина);
        ДанныеЗаполнения.Вставить("Шапка", Новый Структура("Организация, Склад, Контрагент", 
            ДокОбъект.Организация, ДокОбъект.Склад, ДокОбъект.Контрагент));
            
        // Отбираем строки только с этой ставкой НДС
        Отбор = Новый Структура("СтавкаНДС", СтрокаНДС.СтавкаНДС);
        СтрокиТоваров = ТаблицаТоваров.НайтиСтроки(Отбор);
        
        ДанныеЗаполнения.Вставить("Товары", ТаблицаТоваров.Скопировать(СтрокиТоваров));
        
        МассивНовыхДокументов.Добавить(ДанныеЗаполнения);
        
    КонецЦикла;
    
    // 4. Помещаем результат во временное хранилище
    АдресХранилища = ПоместитьВоВременноеХранилище(МассивНовыхДокументов, ЭтаФорма.УникальныйИдентификатор);
    
    Возврат АдресХранилища;

КонецФункции

После того как данные подготовлены, на стороне клиента нам необходимо в цикле открыть новые формы. Важный нюанс: чтобы формы открывались в разных окнах, необходимо генерировать УникальныйИдентификатор для каждой формы.


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

Метод 2: Обработка заполнения в модуле менеджера

Чтобы система «подхватила» наши данные при открытии формы, необходимо переопределить обработчик ОбработкаЗаполнения в модуле менеджера документа. Рассмотрим, как это реализовать, чтобы сохранить все расчеты цен и налогов, которые заложены в логику конфигурации (например, в КА 2.4 или ERP).

В модуле менеджера документа ЗаказКлиента:


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

Нюансы работы с ДанныеФормыВЗначение

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

Проанализируем ситуацию из сообщения 16: автор пытался трижды вызвать ЗначениеВДанныеФормы(). Это бесполезно, так как ДанныеФормы в контексте текущего серверного вызова связаны с конкретным экранным объектом ЭтаФорма.Объект. Для создания новых документов нужно именно открытие новых форм через ОткрытьФорму() с параметрами заполнения.

Рекомендации по расчету цен и налогов

При распределении товаров между заказами крайне важно не просто скопировать цифры, но и убедиться, что документ остался валидным. Рассмотрим основные моменты:

Итоги

Мы разобрали эффективный способ передачи данных формы на сервер, их разделения и последующего открытия нескольких незаписанных документов. Основной секрет успеха — использование временного хранилища для передачи тяжелых данных и использование уникального идентификатора при открытии форм, чтобы избежать «залипания» окон. Такой подход позволяет реализовать удобный для пользователя механизм распределения товара без лишних записей в базу данных и без потери типовой логики расчетов.

← На главную