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