Перенос стандартного функционала (встроенных в конфигурацию обработок) во внешние файлы — частая задача для разработчика 1С. Однако при простом копировании кода возникают ошибки, связанные с потерей контекста. Одной из самых распространенных является ошибка «Поле объекта не обнаружено (Товары)» при попытке обратиться к табличной части документа. Разберем подробно, почему это происходит и как правильно адаптировать код для работы во внешней обработке.
Когда обработка «вшита» в документ или вызывается напрямую из его формы, переменная Объект чаще всего ссылается на сам документ (его данные на форме). В этом контексте обращение Объект.Товары работает корректно, так как у документа ПеремещениеТоваров действительно есть табличная часть Товары.
Однако, как только мы создаем внешнюю обработку, переменная Объект начинает ссылаться на саму обработку. У внешней обработки нет табличной части Товары (если вы не создали её там вручную), поэтому система выдает ошибку. Чтобы решить проблему, нам нужно четко разделить: данные самой обработки (путь к файлу, настройки) и данные документа, в который мы хотим загрузить информацию.
Если ваша внешняя обработка должна изменять уже существующий в базе документ, нам необходимо передать в обработку ссылку на этот документ. Проанализируем, как правильно изменить код процедуры загрузки:
СсылкаНаДокумент с типом ДокументСсылка.ПеремещениеТоваров.Рассмотрим пример кода для серверной части:
&НаСервере
Процедура ЗагрузитьДанныеВДокументНаСервере(ТаблицаДанных)
// Получаем объект документа из ссылки, хранящейся в реквизите обработки
ДокументОбъект = Объект.СсылкаНаДокумент.ПолучитьОбъект();
Если ДокументОбъект = Неопределено Тогда
ВызватьИсключение "Не выбран документ для загрузки!";
КонецЕсли;
// Очищаем табличную часть документа, если это необходимо
ДокументОбъект.Товары.Очистить();
Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
НоваяСтрока = ДокументОбъект.Товары.Добавить();
НоваяСтрока.Номенклатура = СтрокаДанных.Номенклатура;
НоваяСтрока.Количество = СтрокаДанных.Количество;
// Здесь можно вызвать заполнение цен или других реквизитов
// ОбработкаТабличныхЧастей.ЗаполнитьСтавкуНДС(ДокументОбъект, НоваяСтрока);
КонецЦикла;
// Обязательно записываем объект в базу данных
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецПроцедуры
Важный момент: работа с ДокументОбъект возможна только на стороне сервера. Ссылка (ДокументСсылка) доступна на клиенте, но она предназначена только для чтения. Чтобы добавить строки в таблицу, метод ПолучитьОбъект() обязателен.
Часто внешнюю обработку запускают непосредственно из формы документа. В этом случае пользователю удобно, чтобы данные появились в таблице сразу, без предварительного сохранения документа в базу. Для этого мы можем использовать свойство ВладелецФормы.
Проанализируем ситуацию: если форма обработки открыта из формы документа, то ЭтаФорма.ВладелецФормы содержит контекст формы документа. Мы можем обратиться к его данным напрямую.
&НаКлиенте
Процедура ЗагрузитьВФормуДокумента(ТаблицаДанных)
// Проверяем, открыта ли обработка из формы документа
Если ТипЗнч(ВладелецФормы) = Тип("ФормаКлиентскогоПриложения") Тогда
ДанныеФормыДокумента = ВладелецФормы.Объект;
Для Каждого Строка Из ТаблицаДанных Цикл
НоваяСтрока = ДанныеФормыДокумента.Товары.Добавить();
НоваяСтрока.Номенклатура = Строка.Номенклатура;
НоваяСтрока.Количество = Строка.Количество;
КонецЦикла;
// Оповещаем форму документа об изменении данных
ВладелецФормы.Модифицированность = Истина;
КонецЕсли;
КонецПроцедуры
В исходном коде автора используется COMОбъект("Excel.Application"). Это старый метод, который имеет ряд недостатков: требует установленного MS Excel на сервере или клиенте, работает медленно и часто «зависает» в процессах Windows.
Рассмотрим современный и надежный способ — использование встроенного в 1С метода Прочитать() объекта ТабличныйДокумент. Этот метод работает напрямую с файлами .xlsx и .xls без использования стороннего ПО.
&НаСервере
Функция ПрочитатьДанныеИзExcel(ПутьКФайлу)
ТабДок = Новый ТабличныйДокумент;
Попытка
ТабДок.Прочитать(ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
Сообщить("Ошибка чтения файла: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("Номенклатура");
ТаблицаРезультат.Колонки.Добавить("Количество");
// Обходим строки табличного документа (начиная со второй, если есть шапка)
Для НомерСтроки = 2 По ТабДок.ВысотаТаблицы Цикл
Артикул = СокрЛП(ТабДок.Область(НомерСтроки, 1).Текст);
Количество = ТабДок.Область(НомерСтроки, 2).Значение;
// Поиск номенклатуры (лучше делать одним запросом ко всему файлу, но для примера упростим)
СсылкаНоменклатура = Справочники.Номенклатура.НайтиПоНаименованию(Артикул);
Если Не СсылкаНоменклатура.Пустая() Тогда
НоваяСтрока = ТаблицаРезультат.Добавить();
НоваяСтрока.Номенклатура = СсылкаНоменклатура;
НоваяСтрока.Количество = Число(Количество);
КонецЕсли;
КонецЦикла;
Возврат ТаблицаРезультат;
КонецФункции
Запрос.Выполнить()) и поиск ссылок (НайтиПоНаименованию, НайтиПоКоду) нельзя выполнять в процедурах &НаКлиенте. Всегда выносите логику поиска и записи в серверные функции.СтрЗаменить(Значение, Символы.НПП, "") и явное приведение к типу Число().ПараметрыВыполнения.ОбъектыНазначения.ТабличныйДокумент гарантирует кроссплатформенность вашего решения.Проанализировав вышеописанные шаги, мы видим, что основная задача при создании внешней обработки — правильно сориентировать код в пространстве имен 1С, перестав обращаться к Объекту обработки как к документу, и наладить корректную передачу ссылок между клиентской и серверной частью программы.