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