Представим стандартную задачу: вы разрабатываете внешнюю обработку для интеграции или автоматического заполнения документов в системе «1С:Предприятие 8.3». Вам необходимо программно открыть форму нового документа, например, «Поступление товаров», и предзаполнить его табличную часть данными. Вы пишете логичный, на первый взгляд, код на клиенте, получаете форму, добавляете строки, устанавливаете номенклатуру, количество и цену, а затем вызываете метод открытия формы.
Но при запуске кода вы сталкиваетесь с неприятным сюрпризом: документ открывается, номенклатура и количество на месте, а вот поле цены остается пустым или сбрасывается в ноль. Метод Обновить() не помогает, ручной пересчет не запускается. Почему так происходит и как заставить платформу корректно работать с ценами? Давайте подробно разберем эту проблему и изучим правильные архитектурные подходы к её решению.
Для того чтобы решить проблему, нам необходимо понять внутренние механизмы работы управляемого интерфейса платформы «1С:Предприятие 8.3». Происходящее обусловлено тремя ключевыми факторами:
Открыть(), платформа запускает сложную цепочку событий инициализации формы. Сначала на сервере отрабатывает событие ПриСозданииНаСервере, затем на клиенте — ПриОткрытии. В типовых конфигурациях (включая специализированные решения, такие как «Альфа-Авто» от Рарус) в эти обработчики заложены жесткие алгоритмы автоматического ценообразования.Прямое заполнение элементов коллекции ДанныеФормыКоллекция (которой и является Форма.Товары) на клиенте до открытия формы — это обход объектной техники 1С, который часто приводит к подобным багам. Давайте рассмотрим три правильных и безопасных способа заполнения данных.
Это самый «чистый» и рекомендуемый стандарт разработки фирмы «1С» способ создания нового заполненного документа. Вместо того чтобы напрямую модифицировать форму на клиенте, мы передаем массив структур с данными в параметры формы при ее открытии. Платформа сама передаст эти данные в обработчик объекта, где они инициализируются по всем правилам бизнес-логики.
Разберем по шагам, как реализовать этот метод в коде нашей внешней обработки:
&НаКлиенте
Процедура ЗаполнитьИОткрытьДокумент(Команда)
// Шаг 1. Создаем массив строк табличной части
МассивСтрок = Новый Массив;
// Шаг 2. Формируем структуру данных для каждой строки
СтрокаТовара = Новый Структура;
СтрокаТовара.Вставить("Номенклатура", ПредопределенноеЗначение("Справочник.Номенклатура.ПустаяСсылка")); // Замените на реальную ссылку
СтрокаТовара.Вставить("Количество", 10);
СтрокаТовара.Вставить("Цена", 1000);
СтрокаТовара.Вставить("Сумма", 10000); // Рекомендуется сразу рассчитать сумму самостоятельно
МассивСтрок.Добавить(СтрокаТовара);
// Шаг 3. Упаковываем данные ТЧ в структуру параметров заполнения
ПараметрыЗаполнения = Новый Структура;
ПараметрыЗаполнения.Вставить("Товары", МассивСтрок);
// Шаг 4. Передаем параметры в форму через стандартное свойство "ЗначенияЗаполнения"
ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
// Шаг 5. Открываем форму
ОткрытьФорму("Документ.ПоступлениеТоваров.Форма.ФормаДокумента", ПараметрыФормы);
КонецПроцедуры
При таком подходе платформа применит параметры к создаваемому объекту на сервере. В модуле объекта документа сработает предопределенный обработчик ОбработкаЗаполнения, который корректно перенесет данные в документ до начала построения интерфейса формы. Это гарантирует, что алгоритмы ценообразования не сбросят ваши данные.
Если объем данных большой или структура табличной части слишком сложная, передавать её через параметры клиента может быть неэффективно или неудобно. В этом случае мы можем подготовить полноценную таблицу значений на сервере, поместить ее во временное хранилище и передать в форму только уникальный адрес.
Проанализируем пример реализации данного подхода. Код внешней обработки будет выглядеть следующим образом:
&НаКлиенте
Процедура ОткрытьЧерезХранилище(Команда)
// Получаем адрес временного хранилища с заполненной таблицей
АдресДанных = ПодготовитьДанныеНаСервере();
// Передаем адрес в параметры открываемой формы
ПараметрыФормы = Новый Структура("АдресДанныхЗаполнения", АдресДанных);
ОткрытьФорму("Документ.ПоступлениеТоваров.Форма.ФормаДокумента", ПараметрыФормы);
КонецПроцедуры
&НаСервере
Функция ПодготовитьДанныеНаСервере()
// Создаем таблицу значений с нужной структурой колонок
ТаблицаТоваров = Новый ТаблицаЗначений;
ТаблицаТоваров.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаТоваров.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТаблицаТоваров.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
ТаблицаТоваров.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
// Наполняем таблицу данными
НоваяСтрока = ТаблицаТоваров.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("10000001");
НоваяСтрока.Количество = 10;
НоваяСтрока.Цена = 1000;
НоваяСтрока.Сумма = 10000;
// Помещаем таблицу во временное хранилище
Возврат ПоместитьВоВременноеХранилище(ТаблицаТоваров, УникальныйИдентификатор);
КонецФункции
Для того чтобы этот способ сработал, в модуле управляемой формы самого документа (например, через механизм расширений, если вы не хотите снимать конфигурацию с поддержки) в обработчике ПриСозданииНаСервере необходимо прописать прием этой таблицы:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем, передан ли наш параметр с адресом данных
Если Параметры.Свойство("АдресДанныхЗаполнения") Тогда
ТаблицаТоваров = ПолучитьИзВременногоХранилища(Параметры.АдресДанныхЗаполнения);
// Загружаем данные напрямую в объект формы
Объект.Товары.Загрузить(ТаблицаТоваров);
КонецЕсли;
КонецПроцедуры
Если специфика бизнес-логики вашей конфигурации (особенно это актуально для «Альфа-Авто») требует строгого соблюдения всех внутренних взаимосвязей, расчетов налогов, партий и скидок, то самым надежным решением будет создание, полное заполнение и запись документа непосредственно на сервере. После этого пользователю открывается уже готовый, записанный в базу данных документ.
Посмотрим на пример реализации этого сценария:
&НаКлиенте
Процедура СоздатьИОткрытьЗаписанный(Команда)
// Создаем документ на сервере и получаем его ссылку
СсылкаНаНовыйДокумент = СоздатьДокументНаСервере();
Если ЗначениеЗаполнено(СсылкаНаНовыйДокумент) Тогда
// Формируем параметры для открытия существующего документа
ПараметрыФормы = Новый Структура("Ключ", СсылкаНаНовыйДокумент);
ОткрытьФорму("Документ.ПоступлениеТоваров.Форма.ФормаДокумента", ПараметрыФормы);
Иначе
ВызватьИсключение "Не удалось создать документ на сервере!";
EndIf;
КонецПроцедуры
&НаСервере
Функция СоздатьДокументНаСервере()
// Инициализируем объект документа
НовыйДокумент = Документы.ПоступлениеТоваров.СоздатьДокумент();
// Заполняем реквизиты шапки (Крайне важно для отработки алгоритмов ценообразования!)
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = Справочники.Организации.НайтиПоРеквизиту(...);
НовыйДокумент.Контрагент = Справочники.Контрагенты.НайтиПоКоду(...);
НовыйДокумент.ДоговорКонтрагента = Справочники.ДоговорыКонтрагентов.НайтиПоРеквизиту(...);
// Заполняем табличную часть напрямую в объекте
НоваяСтрока = НовыйДокумент.Товары.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("10000001");
НоваяСтрока.Количество = 10;
НоваяСтрока.Цена = 1000;
НоваяСтрока.Сумма = 10000;
// Для конфигураций Альфа-Авто и ERP-систем рекомендуется вызывать внутреннее API
// для корректного пересчета налогов (НДС), скидок и итогов:
// ОбработкаТабличныхЧастей.РассчитатьСуммуТабличнойЧасти(НовыйДокумент, "Товары");
Попытка
НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
Возврат НовыйДокумент.Ссылка;
Исключение
ЗаписьЖурналаРегистрации("ПрограммноеСозданиеДокумента", УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат Неопределено;
КонецПопытки;
КонецФункции
Преимущества данного метода: документ создается строго в соответствии с метаданными. Отрабатывают все подписки на события, процедуры модуля объекта ПередЗаписью и ПриЗаписи. Когда пользователь увидит форму документа на своем экране, все цены, суммы и ставки налогов гарантированно будут рассчитаны верно и не сбросятся в процессе визуализации формы.
Программная работа с интерфейсом в управляемом приложении «1С:Предприятие 8.3» требует четкого разделения клиентской и серверной логики. Попытки напрямую заполнять ячейки табличных частей на клиенте перед открытием формы часто приводят к конфликтам со стандартными процедурами пересчета цен.
Используйте Способ 1 (Параметры заполнения) для создания чистых, еще незаписанных документов. Применяйте Способ 2 (Временное хранилище) при сложной структуре передаваемых коллекций. И отдавайте предпочтение Способу 3 (Создание на сервере с записью), если работаете со сложными отраслевыми конфигурациями вроде «Альфа-Авто» или ERP, где ценообразование и расчет налогов сильно завязаны на проведение документа и проверку прав доступа.