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