При разработке внешних обработок заполнения табличных частей (ОЗТЧ) для обычных форм разработчики часто сталкиваются с ситуацией: строки в табличную часть добавляются программно, но в открытой форме документа они не отображаются. Попытки принудительно обновить форму через ЭтаФорма.Обновить() или Перечитать() не дают результата, а вызов записи объекта приводит к конфликтам версий.
Разберем причины этой проблемы, проанализируем типичные ошибки в коде и рассмотрим правильные варианты реализации диалога с пользователем и заполнения строк документа.
Основная ошибка заключается в получении нового экземпляра объекта через ссылку внутри обработки. Проанализируем ошибочный фрагмент кода:
Процедура ЗаполнитьТЧ()
Док = СсылкаНаОбъект.ПолучитьОбъект();
// Заполнение строк в переменную Док...
Док.Записать();
КонецПроцедуры
Выясним, что происходит на уровне платформы 1С при таком подходе:
Ссылка.ПолучитьОбъект() считывает данные из базы данных и создает в оперативной памяти совершенно новый, независимый экземпляр документа.ДокументОбъект, в котором изменений не произошло.Док.Записать(), измененный дубль запишется в базу данных. В этот момент при попытке пользователя нажать кнопку «Записать» в открытой форме возникнет ошибка блокировки: «Данные были изменены или удалены другим пользователем».Главное правило: чтобы данные появились в форме сразу и без записи в базу данных, необходимо изменять именно тот объект, ссылка на который передается платформой в стандартную процедуру Инициализировать.
Рассмотрим стандартный и наиболее чистый архитектурный подход. Если обработка требует диалога с пользователем (например, выбора параметров, спецификаций или ввода количества), открываем форму обработки модально, возвращаем из нее структуру или таблицу значений и заполняем переданный Объект.
Разберем по шагам реализацию данного варианта.
В модуле объекта внешней обработки заполнения ТЧ прописываем экспортную процедуру:
Процедура Инициализировать(Объект, ИмяТабличнойЧасти = Неопределено, ТабличноеПолеОбъекта = Неопределено) Экспорт
ФормаПараметров = ЭтотОбъект.ПолучитьФорму("Форма");
// Открываем форму обработки и получаем результат выбора пользователя
ТаблицаДанных = ФормаПараметров.ОткрытьМодально();
Если ТаблицаДанных = Неопределено Тогда
Возврат;
КонецЕсли;
// Заполняем непосредственно переданный объект документа
Для Каждого СтрДанных Из ТаблицаДанных Цикл
НоваяСтрока = Объект.Материалы.Добавить();
НоваяСтрока.Номенклатура = СтрДанных.Номенклатура;
НоваяСтрока.Количество = СтрДанных.Количество;
КонецЦикла;
КонецПроцедуры
В модуле формы обработки по нажатию кнопки «Выполнить» подготавливаем результат и закрываем форму с передачей данных:
Процедура КнопкаВыполнитьНажатие(Кнопка)
// Формируем таблицу значений с введенными пользователем данными
ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("Номенклатура");
ТаблицаРезультат.Колонки.Добавить("Количество");
Для Каждого ТекСтр Из ТабЧа Цикл
Если ТекСтр.Количество > 0 Тогда
Для Каждого СтрКомплектующих Из ТекСтр.Наименование.ИсходныеКомплектующие Цикл
СтрРез = ТаблицаРезультат.Добавить();
СтрРез.Номенклатура = СтрКомплектующих.Номенклатура;
СтрРез.Количество = СтрКомплектующих.Количество * ТекСтр.Количество;
КонецЦикла;
КонецЕсли;
КонецЦикла;
// Закрываем форму с передачей результата в точку вызова ОткрытьМодально()
ЭтаФорма.Закрыть(ТаблицаРезультат);
КонецПроцедуры
Рассмотрим альтернативный способ, когда заполнение строк происходит непосредственно внутри формы обработки. Для этого необходимо передать ссылку на редактируемый в памяти документ напрямую в форму.
ДокументОбъект (тип произвольный или соответствующий типу документа).Инициализировать передаем входящий Объект в этот реквизит перед открытием.ДокументОбъект.В модуле объекта обработки:
Процедура Инициализировать(Объект, ИмяТабличнойЧасти = Неопределено, ТабличноеПолеОбъекта = Неопределено) Экспорт
ФормаОбработки = ЭтотОбъект.ПолучитьФорму("Форма");
ФормаОбработки.ДокументОбъект = Объект;
ФормаОбработки.ОткрытьМодально();
КонецПроцедуры
В модуле формы обработки:
Процедура КнопкаВыполнитьНажатие(Кнопка)
Для Каждого ТекСтр Из ТабЧа Цикл
Если ТекСтр.Количество > 0 Тогда
Для Каждого СтрКомплектующих Из ТекСтр.Наименование.ИсходныеКомплектующие Цикл
НоваяСтрока = ДокументОбъект.Материалы.Добавить();
НоваяСтрока.Номенклатура = СтрКомплектующих.Номенклатура;
НоваяСтрока.Количество = СтрКомплектующих.Количество * ТекСтр.Количество;
КонецЦикла;
КонецЕсли;
КонецЦикла;
ЭтаФорма.Закрыть();
КонецПроцедуры
При работе с обычными формами обратите внимание на следующие нюансы:
Объект.Материалы.Добавить()) события табличной части (например, ПриИзменении) не вызываются. Все сопутствующие данные (единицы измерения, коэффициенты, счета учета, цены) необходимо рассчитывать и заполнять в коде вручную либо вызывать соответствующие типовые процедуры общих модулей конфигурации.ТабличноеПолеОбъекта.ОбновитьСтроки(), если параметр передан платформой.