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