При организации интеграции между различными конфигурациями «1С:Предприятие» (например, при переносе данных из «Бухгалтерии предприятия» в «Управление торговлей 10.3») разработчики часто используют механизм COM-соединения. Это мощный инструмент, однако при работе с подчиненными справочниками в контексте другого процесса могут возникать неочевидные ошибки.
В этой статье мы подробно разберем, почему при создании документов через COM-соединение в «1С:Управление торговлей 10.3» (УТ 10.3) некорректно заполняются единицы измерения, выясним, почему из-за этого перестает работать типовая печать этикеток и штрихкодов, и найдем наиболее элегантные и производительные способы решения этих проблем.
---
Рассмотрим классическую ситуацию. Разработчик пытается заполнить табличную часть создаваемого через COM документа «Оприходование товаров» на основании данных из базы-источника. На первый взгляд код выглядит логичным:
// Попытка прямого заполнения единицы измерения
СомТовары.ЕдиницаИзмерения = СомТовары.Номенклатура.БазоваяЕдиницаИзмерения;
Однако при выполнении этого кода поле ЕдиницаИзмерения в документе остается либо пустым, либо заполняется некорректно, из-за чего документ впоследствии невозможно нормально использовать. Чтобы понять причину, проанализируем структуру хранения данных в УТ 10.3.
В конфигурации «Управление торговлей 10.3» учет единиц измерения построен по двухъярусной схеме:
Справочник.КлассификаторЕдиницИзмерения) — это общесистемный плоский список, содержащий международные и национальные стандарты (штуки, килограммы, литры и т.д.). Реквизит карточки номенклатуры БазоваяЕдиницаИзмерения ссылается именно на этот справочник.Справочник.ЕдиницыИзмерения) — это подчиненный справочник, где владельцем каждого элемента выступает конкретная позиция номенклатуры. Именно здесь хранятся индивидуальные коэффициенты пересчета (например, «коробка» с коэффициентом 10 для товара А).В табличных частях документов УТ 10.3 в колонке ЕдиницаИзмерения система строго ожидает ссылку на подчиненный справочник ЕдиницыИзмерения, а не на классификатор ОКЕИ. Пытаясь записать туда БазоваяЕдиницаИзмерения, мы нарушаем типизацию метаданных. Документ может записаться (поскольку COM-соединение позволяет обходить некоторые интерактивные проверки платформы), но база данных придет в рассогласованное состояние.
---
Пытаясь исправить ситуацию, разработчики часто прибегают к поиску по наименованию в подчиненном справочнике с указанием владельца:
СомТовары.ЕдиницаИзмерения = ComConnection.Справочники.ЕдиницыИзмерения.НайтиПоНаименованию(
строка.ЕдиницаИзмерения.Наименование,
,
СомТовары.Номенклатура
);
При выполнении этого кода в контексте COM-соединения отбор по владельцу часто попросту игнорируется или возвращает пустую ссылку. Выясним причину этой особенности.
Дело в том, что переменная СомТовары.Номенклатура возвращает прокси-объект COM, в то время как метод НайтиПоНаименованию на стороне COM-сервера ожидает полноценную ссылку типа СправочникСсылка.Номенклатура во внутреннем контексте базы-приемника. Из-за трудностей маршалинга типов между процессами отбор по владельцу сбрасывается. В результате система находит первую попавшуюся единицу измерения с наименованием «шт» или «пар», принадлежащую совершенно другому товару. Это приводит к хаосу в учете: в документе оказывается чужая единица измерения.
---
Рассмотрим два пути решения этой задачи: тот, к которому пришли участники форума, и более профессиональный, оптимальный вариант.
В ходе обсуждения участники пришли к следующей конструкции:
КодЕдиницы = ComConnection.Справочники.Номенклатура.НайтиПоКоду(Строка.Номенклатура.Код).ЕдиницаДляОтчетов.Code;
СомТовары.ЕдиницаИзмерения = ComConnection.Справочники.ЕдиницыИзмерения.НайтиПоКоду(КодЕдиницы);
Плюсы: Этот способ действительно работает. Он позволяет однозначно найти нужную единицу в подчиненном справочнике базы-приемника.
Минусы: Данный код крайне непроизводителен. При обработке каждой строки документа выполняется несколько тяжелых сетевых вызовов через COM: поиск номенклатуры по коду, чтение реквизита единицы отчетов, получение ее кода, а затем повторный поиск единицы по этому коду. На документе в 100 строк это вызовет заметные задержки.
Поскольку мы уже нашли объект номенклатуры в базе-приемнике и присвоили его строке документа (СомТовары.Номенклатура), мы можем мгновенно получить ссылки на связанные с ним единицы измерения. В карточке номенклатуры УТ 10.3 всегда заполнены реквизиты ЕдиницаХраненияОстатков и ЕдиницаДляОтчетов, которые по определению являются элементами подчиненного справочника ЕдиницыИзмерения.
Посмотрим на пример оптимального заполнения строки документа:
// Находим номенклатуру один раз
СсылкаНоменклатуры = ComConnection.Справочники.Номенклатура.НайтиПоКоду(Строка.Номенклатура.Код);
Если НЕ СсылкаНоменклатуры.Пустая() Тогда
СомТовары = ДокументОбъект.Товары.Добавить();
СомТовары.Номенклатура = СсылкаНоменклатуры;
// Прямое переопределение без лишних поисков в базе данных!
СомТовары.ЕдиницаИзмерения = СсылкаНоменклатуры.ЕдиницаХраненияОстатков;
СомТовары.Коэффициент = СсылкаНоменклатуры.ЕдиницаХраненияОстатков.Коэффициент;
СомТовары.Количество = Строка.Количество;
СомТовары.Цена = Строка.Цена;
СомТовары.Сумма = Строка.СуммаВРознице;
КонецЕсли;
Такой подход работает молниеносно, так как мы не делаем ни одного лишнего запроса к базе данных через COM-соединение, а используем уже полученные свойства объекта номенклатуры.
---
После успешного создания и проведения документа через COM-соединение разработчики часто сталкиваются со следующей проблемой: при попытке открыть обработку «Печать этикеток и ценников» на основании созданного документа табличная часть обработки оказывается пустой.
Разберем три ключевые причины, почему так происходит.
Типовая обработка печати этикеток в УТ 10.3 при заполнении из документа использует внутренний запрос, который жестко связывает (соединяет) товары из документа с регистром сведений РегистрСведений.Штрихкоды. Если для перенесенного из другой базы товара штрихкод еще не зарегистрирован в базе УТ 10.3, запрос вернет пустой результат, и строка товара просто не попадет на форму печати.
Решение: При переносе номенклатуры или документов необходимо также программно переносить или генерировать штрихкоды в базе-приемнике.
Регистр сведений Штрихкоды в УТ 10.3 имеет ключевое измерение ЕдиницаИзмерения. Штрихкод всегда привязывается к конкретной единице измерения из подчиненного справочника. Если в регистре штрихкод записан для единицы «шт» с кодом УТ0007893, а в созданном документе в поле ЕдиницаИзмерения по ошибке записалась другая копия единицы (например, созданная заново с кодом УТ0011671), то связь в запросе печати этикеток разорвется. Программа посчитает, что для этой единицы измерения штрихкода не существует.
Если в коде загрузки документа все-таки осталась запись БазоваяЕдиницаИзмерения (из классификатора), документ запишется, но в регистре штрихкодов данные хранятся в разрезе подчиненного справочника. Сравнение типов в запросе обработки печати выдаст ложь, и этикетка не распечатается.
---
Для того чтобы решить обе проблемы разом, рассмотрим полноценный пример кода на стороне базы-источника. Напишем алгоритм, который не только оптимально создает документ оприходования в УТ 10.3 через COM, но и гарантирует создание корректных штрихкодов для последующей беспрепятственной печати этикеток.
// Инициализация подключения к базе УТ 10.3
ComConnector = Новый COMОбъект("V83.COMConnector");
ComConnection = ComConnector.Connect("Srvr=""Server1C"";Ref=""UT_Base"";Usr=""Admin"";Pwd=""Password""");
// Создаем документ Оприходование товаров в УТ 10.3
НовыйДок = ComConnection.Документы.ОприходованиеТоваров.СоздатьДокумент();
НовыйДок.Дата = ТекущаяДата();
НовыйДок.Организация = ComConnection.Справочники.Организации.НайтиПоРеквизиту("ИНН", "7700000000");
НовыйДок.СкладОтравитель = ComConnection.Справочники.Склады.НайтиПоНаименованию("Основной склад");
Для Каждого СтрокаТЧ Из ТоварыКПереносу Цикл
// Находим номенклатуру в УТ
СомНоменклатура = ComConnection.Справочники.Номенклатура.FindByCode(СтрокаТЧ.НоменклатураКод);
Если СомНоменклатура.IsEmpty() Тогда
Продолжить; // Если номенклатура не найдена, пропускаем строку или логируем ошибку
КонецЕсли;
НоваяСтрока = НовыйДок.Товары.Добавить();
НоваяСтрока.Номенклатура = СомНоменклатура;
// ПРАВИЛЬНО: берем подчиненную единицу измерения напрямую из свойств номенклатуры
НоваяСтрока.ЕдиницаИзмерения = СомНоменклатура.ЕдиницаХраненияОстатков;
НоваяСтрока.Коэффициент = СомНоменклатура.ЕдиницаХраненияОстатков.Коэффициент;
НоваяСтрока.Количество = СтрокаТЧ.Количество;
НоваяСтрока.Цена = СтрокаТЧ.Цена;
НоваяСтрока.Сумма = СтрокаТЧ.Сумма;
// ПРОВЕРКА И СОЗДАНИЕ ШТРИХКОДА: гарантирует, что этикетка распечатается
Если Не ПустаяСтрока(СтрокаТЧ.Штрихкод) Тогда
// Ищем штрихкод в регистре сведений УТ 10.3
РегистрШтрихкодов = ComConnection.РегистрыСведений.Штрихкоды;
НайденныйШтрихкод = РегистрШтрихкодов.СоздатьМенеджерЗаписи();
НайденныйШтрихкод.Штрихкод = СтрокаТЧ.Штрихкод;
НайденныйШтрихкод.Прочитать();
Если Не НайденныйШтрихкод.Выбран() Тогда
// Если штрихкода нет, создаем новую запись
НоваяЗаписьШК = РегистрШтрихкодов.СоздатьМенеджерЗаписи();
НоваяЗаписьШК.Штрихкод = СтрокаТЧ.Штрихкод;
НоваяЗаписьШК.Владелец = СомНоменклатура;
// Важно привязать именно к той единице измерения, которая заполнена в документе!
НоваяЗаписьШК.ЕдиницаИзмерения = СомНоменклатура.ЕдиницаХраненияОстатков;
НоваяЗаписьШК.ТипШтрихкода = ComConnection.ПланыВидовХарактеристик.ТипыШтрихкодов.EAN13;
НоваяЗаписьШК.Записать();
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Записываем и проводим документ
НовыйДок.Записать(ComConnection.РежимЗаписиДокумента.Проведение);
ВызватьИсключение "Документ успешно проведен!";
---
Для того чтобы избежать ошибок при программном заполнении справочников и табличных частей документов в «1С:Управление торговлей 10.3» через COM-соединение, всегда придерживайтесь следующих правил:
Справочник.КлассификаторЕдиницИзмерения (ОКЕИ) и подчиненный справочник Справочник.ЕдиницыИзмерения. В документах всегда должна быть ссылка на подчиненный справочник.ЕдиницаХраненияОстатков и ЕдиницаДляОтчетов.Штрихкоды и соответствие единиц измерения в документе и в регистре.