При разработке механизмов создания связанных объектов в 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда программно назначенные значения реквизитов «исчезают» или заменяются значениями по умолчанию сразу после открытия формы. Особенно часто это проявляется в зарплатных конфигурациях (ЗУП, КА, ERP) с такими полями, как ПериодВзаиморасчетов или ПериодРегистрации. Разберем подробно, почему это происходит и как решить проблему правильно.
Рассмотрим ситуацию, описанную в теме: при выполнении команды создания документа на клиенте получается форма, заполняется её объект, а затем форма открывается. Однако поле ПериодВзаиморасчетов упорно ставит дату по умолчанию. Выясним причину этого поведения. Проблема кроется в жизненном цикле формы и встроенных механизмах инициализации типовых конфигураций.
Когда мы используем метод ОткрытьФорму(), платформа инициирует цепочку событий:
ОбработкаЗаполнения (в модуле объекта).ПриСозданииНаСервере (в модуле формы).ПриОткрытии (на клиенте).В типовых решениях, таких как «Зарплата и управление персоналом», в процедуре ПриСозданииНаСервере часто прописана жесткая логика: если документ новый, система автоматически устанавливает текущий период. Ваше ручное заполнение, выполненное до открытия формы, просто перезаписывается стандартным кодом инициализации формы. Кроме того, как отмечено в обсуждении, платформа может корректировать дату документа в зависимости от настроек рабочей даты и свойства АвтоВремя.
Вместо того чтобы вручную манипулировать объектом формы через КопироватьДанныеФормы, наиболее профессиональным подходом является передача параметров заполнения. Это позволяет платформе корректно вызвать событие ОбработкаЗаполнения в модуле объекта.
Рассмотрим, как изменить клиентский код для передачи данных:
&НаКлиенте
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ОбъектыНазначенияМассив) Экспорт
Для Каждого ОбъектНазначения Из ОбъектыНазначенияМассив Цикл
// Формируем структуру значений, которые нужно передать в новый документ
ДанныеДляЗаполнения = ПолучитьДанныеДляЗаполненияНаСервере(ОбъектНазначения);
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("ЗначенияЗаполнения", ДанныеДляЗаполнения);
// Открываем форму, передавая параметры
ОткрытьФорму("Документ.КомпенсацияЗаЗадержкуЗарплаты.ФормаОбъекта", ПараметрыФормы);
КонецЦикла;
КонецПроцедуры
&НаСервере
Функция ПолучитьДанныеДляЗаполненияНаСервере(СсылкаНаИсточник)
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("ПериодРегистрации", СсылкаНаИсточник.Дата);
// Важно: приводим дату к началу месяца для зарплатных реквизитов
СтруктураДанных.Вставить("ПериодВзаиморасчетов", НачалоМесяца(СсылкаНаИсточник.Дата));
Возврат СтруктураДанных;
КонецФункции
При таком подходе в модуле объекта нового документа сработает стандартный механизм, и значения будут установлены корректно еще до того, как форма начнет выполнять свои процедуры инициализации.
Если стандартная ОбработкаЗаполнения по каким-то причинам перекрывается логикой формы, мы можем принудительно передать свой параметр и обработать его в самой форме. Это гарантирует, что наши значения будут установлены в самый последний момент инициализации сервера.
Шаг 1. Передача произвольного параметра из клиентского кода:
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("МойПериодВзаиморасчетов", НужнаяДата);
ОткрытьФорму("Документ.КомпенсацияЗаЗадержкуЗарплаты.ФормаОбъекта", ПараметрыФормы);
Шаг 2. Обработка в модуле формы документа:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем наличие нашего параметра
Если Параметры.Свойство("МойПериодВзаиморасчетов") Тогда
Объект.ПериодВзаиморасчетов = Параметры.МойПериодВзаиморасчетов;
КонецЕсли;
КонецПроцедуры
Этот метод является самым надежным, если вы работаете с очень сложной типовой формой, которая игнорирует стандартные механизмы заполнения.
Проанализируем ситуацию с конкретным полем ПериодВзаиморасчетов. В конфигурациях 1С этот реквизит почти всегда имеет специфические требования:
15.08.2023, встроенные проверки формы (в событиях ПриЧтенииНаСервере или ПриИзменении) могут сбросить её, так как ожидается 01.08.2023. Всегда используйте функцию НачалоМесяца().В ходе обсуждения на форуме было предложено записывать документ перед открытием. Рассмотрим этот пример кода, предложенный участниками:
&НаСервере
Процедура СоздатьНовыйДокумент(ДокументИсточник, НовыйОбъект)
ДокументОснование = ДокументИсточник.Ссылка;
НовыйОбъект.ПериодРегистрации = ДокументОснование.Дата;
НовыйОбъект.ПериодВзаиморасчетов = ДокументОснование.ПериодРегистрации;
// Принудительная запись объекта для фиксации данных в БД
НовыйОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецПроцедуры
Важный нюанс: Хотя этот способ «работает», он имеет существенный минус — в базе данных появляются «мусорные» документы, которые пользователь мог и не захотеть сохранять. Если пользователь закроет форму без сохранения, пустой (или полузаполненный) документ уже останется в списке. Поэтому мы рекомендуем использовать Решение №1 через параметры формы.
ПолучитьФорму() с последующей манипуляцией ОбъектФормы. Вместо этого подготовьте структуру ЗначенияЗаполнения.ОткрытьФорму(), передавая эту структуру вторым аргументом.ПриСозданииНаСервере) код, который считывает значение из Параметры и присваивает его Объект.ИмяРеквизита в самом конце процедуры.Соблюдение этой последовательности позволит вам корректно заполнять любые сложные реквизиты, не прибегая к преждевременной записи объекта в информационную базу.