В процессе разработки внешних обработок заполнения для конфигураций на управляемых формах, таких как 1С:Управление торговлей 11, программисты часто сталкиваются с задачей импорта данных из внешних файлов (Excel, CSV) напрямую в открытую форму документа. Типичный пример — документ Пересчет товаров, где необходимо быстро проставить фактическое количество по результатам инвентаризации.
В этой статье мы подробно разберем, как правильно реализовать поиск строк в табличной части, избежать типичных ошибок при работе с массивами и заставить интерфейс 1С отобразить изменения без принудительной записи документа в базу данных.
Когда мы запускаем внешнюю обработку из формы документа, у нас есть доступ к этой форме через свойство ВладелецФормы. Чтобы изменить данные в табличной части, нам нужно обращаться к реквизиту Объект этой формы. Рассмотрим типичную архитектуру кода, которую мы будем использовать в нашем решении:
Проанализируем основные этапы задачи:
Товары открытого документа.Одной из первых проблем, с которой сталкиваются разработчики, является неправильная обработка результата метода НайтиСтроки(). Рассмотрим фрагмент кода, который часто приводит к ошибке "Поле объекта не обнаружено":
Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", СсылкаНаНоменклатуру);
НайденноеЗначение = ВладелецФормы.Объект.Товары.НайтиСтроки(Отбор);
// ОШИБКА: НайденноеЗначение — это МАССИВ, а не конкретная строка
НайденноеЗначение.КоличествоУпаковокФакт = ЧислоИзExcel;
Выясним причину: метод НайтиСтроки() всегда возвращает объект типа Массив, даже если найдена всего одна строка или не найдено ничего. Чтобы изменить данные, нам необходимо проверить, содержит ли массив элементы, и обратиться к конкретному индексу (обычно [0], если номенклатура в документе не дублируется).
Рассмотрим исправленный алгоритм работы. Мы добавим проверку количества найденных строк и корректное обращение к элементам коллекции. Также важно помнить, что работа с Excel на клиенте требует осторожности при высвобождении ресурсов COM.
// Фрагмент цикла обработки строк Excel
Для СтрокаОтсчета = 2 по КоличествоСтрок Цикл
КодТовара = Excel.Cells(СтрокаОтсчета, 4).Text;
// Получаем ссылку на номенклатуру (вызов серверной функции)
НоменклатураСсылка = ПолучитьНоменклатуруПоКоду(КодТовара);
Если НоменклатураСсылка.Пустая() Тогда
Сообщить("Товар с кодом " + КодТовара + " не найден в базе!");
Продолжить;
КонецЕсли;
// Формируем отбор для поиска в ТЧ документа
Отбор = Новый Структура("Номенклатура", НоменклатураСсылка);
НайденныеСтроки = ВладелецФормы.Объект.Товары.НайтиСтроки(Отбор);
Если НайденныеСтроки.Количество() > 0 Тогда
// Берем первую найденную строку
ТекущаяСтрокаТЧ = НайденныеСтроки[0];
// Проставляем значение из колонки Excel
ТекущаяСтрокаТЧ.КоличествоУпаковокФакт = Число(Excel.Cells(СтрокаОтсчета, 6).Text);
// Устанавливаем признак модифицированности формы, чтобы пользователь видел изменения
ВладелецФормы.Модифицированность = Истина;
Иначе
Сообщить("Номенклатура " + НоменклатураСсылка + " отсутствует в документе пересчета!");
КонецЕсли;
КонецЦикла;
Часто после выполнения кода значения в памяти изменились, признак Модифицированность установлен, но пользователь видит на экране старые цифры. Это происходит потому, что форма-владелец не знает, что данные её объекта были изменены "извне" другой формой.
Использование метода ВладелецФормы.Прочитать() в данном случае не подходит, так как этот метод перечитывает данные из базы данных, затирая все несохраненные изменения, которые мы только что внесли в объект формы.
Правильное решение: использование глобального метода ОповеститьОбИзменении(). Рассмотрим, как это применить в финальной части кода обработки:
// Закрываем Excel и освобождаем память
Excel.Quit();
Excel = Неопределено;
// Оповещаем систему об изменении объекта, чтобы формы обновили свое содержимое
ОповеститьОбИзменении(ВладелецФормы.Объект.Ссылка);
// Закрываем текущую форму обработки заполнения
ЭтаФорма.Закрыть();
Метод ОповеститьОбИзменении() посылает сигнал платформе 1С о том, что данные конкретного ссылочного объекта изменились. Платформа автоматически инициирует обновление всех открытых форм, связанных с этой ссылкой, при этом данные подтягиваются из текущего контекста объекта, и пользователь видит актуальные заполненные значения.
Проанализируем ситуацию, когда в документе Пересчет товаров содержатся тысячи строк. Поиск методом НайтиСтроки() внутри цикла по Excel-файлу будет работать со скоростью $O(N*M)$, что может привести к зависанию интерфейса.
Для ускорения работы рекомендуем применить следующие подходы:
ТаблицуЗначений на стороне сервера, добавьте индекс по колонке Номенклатура. Выполните все расчеты в этой таблице и загрузите результат обратно в объект.ПолучитьНоменклатуруПоКоду в цикле. Сначала соберите все коды из Excel в массив, одним запросом получите соответствие "Код - Ссылка" на сервере, и используйте это соответствие в цикле.ТабличныйДокумент для чтения файлов Excel (.xlsx). Метод Прочитать() работает быстрее и стабильнее на серверной стороне, не требуя установленного Microsoft Office на компьютере или сервере.В конфигурации 1С:Управление торговлей 11 документ Пересчет товаров имеет свои особенности. Например, если вы меняете КоличествоУпаковокФакт, в документе могут быть завязаны события, рассчитывающие отклонения (излишки и недостатки). При программном изменении реквизита "напрямую" через объект формы эти обработчики могут не сработать.
Поэтому в идеале стоит вызывать типовые процедуры пересчета из модулей конфигурации, если они доступны для вызова. Однако для простых задач заполнения фактических данных связки НайтиСтроки() + ОповеститьОбИзменении() обычно достаточно для корректной работы пользователя.
Таким образом, мы разобрали, как эффективно взаимодействовать с формами в управляемом приложении, корректно обрабатывать коллекции строк и обеспечивать отзывчивость пользовательского интерфейса при автоматизации рутинных операций заполнения документов.