Интеграция между современными конфигурациями на платформе 1С:Предприятие 8 и старыми базами на 1С 7.7 все еще остается актуальной задачей. Часто возникает необходимость перенести данные о зарплате или хозяйственных операциях, создав в "семерке" документ "Бухгалтерская справка" или "Операция".
Одной из самых частых и неочевидных проблем при этом является заполнение реквизитов типа Счет. В данной статье мы подробно разберем ситуацию, когда при создании документа через OLE счета находятся, но в сам документ не записываются (поля остаются пустыми), и найдем верное техническое решение.
Рассмотрим типичный сценарий. Мы подключаемся из базы 1С 8.3 (например, ЗУП 3.1) к базе 1С 7.7 через COM-соединение (OLE Automation). Наша цель — создать документ "Бухгалтерская справка" и заполнить его табличную часть проводками.
Часто программисты используют подход, аналогичный работе внутри самой 7.7: создают агрегатный объект Счет, ищут нужный счет по коду и пытаются присвоить его реквизиту документа.
Взглянем на пример кода, который вызывает ошибку (счета не сохраняются):
// Подключение к базе (условно)
База77 = Новый COMОбъект("V77.Application");
// ... инициализация ...
// Попытка найти счет "по-старому"
Счет_77 = База77.EvalExpr("СоздатьОбъект(""Счет"")");
Счет_77.НайтиПоКоду("20");
Счет20 = Счет_77.ТекущийСчет;
// Проверка показывает, что счет найден
Сообщить(Счет20.Наименование); // Выводит правильное название!
// Создание документа
Док_77 = База77.EvalExpr("СоздатьОбъект(""Документ.БухгалтерскаяСправка"")");
Док_77.Новый();
// ... заполнение шапки ...
Док_77.НоваяСтрока();
// ОШИБКА ЗДЕСЬ: Присвоение проходит без ошибок, но по факту в базе 7.7 поле остается пустым
Док_77.СчетДт = Счет20;
Док_77.СчетКт = ...;
Док_77.Записать();
Почему это происходит? Ведь Сообщить(Счет20.Наименование) возвращает верный результат, значит объект существует.
Проблема кроется в особенностях работы OLE-моста и архитектуре 1С 7.7.
CreateObject("Счет"), является выборкой (итератором), предназначенным для позиционирования. Метод ТекущийСчет должен возвращать ссылку. Однако при передаче этого объекта через границу процессов (OLE) контекст может теряться. Система 7.7 ожидает конкретную ссылку на элемент метаданных, а получает COM-обертку сложного объекта, которую не всегда может корректно интерпретировать для записи в поле БД.Операция в 7.7 часто "проглатывает" такое присваивание корректно, в то время как реквизит табличной части документа или справочника оказывается более строгим к типу передаваемого значения.Счет, созданный без явного указания плана (через ИспользоватьПланСчетов), может указывать не на тот контекст, который ожидается в реквизите документа.Чтобы гарантированно записать счет в реквизит документа через OLE, нам нужно получить "чистую" ссылку на счет непосредственно от глобального контекста базы 7.7, минуя создание промежуточных объектов-выборок.
Для этого в глобальном контексте 1С 7.7 существует метод СчетПоКоду (AccountByCode). Он возвращает именно ссылку на счет, которую OLE-интерфейс передает корректно.
Рассмотрим правильный алгоритм действий:
Вместо создания объекта Счет, мы обращаемся к базе OLE напрямую. Также крайне важно указывать второй параметр — План счетов, к которому принадлежит искомый счет. Это устраняет неоднозначность.
// Правильный способ получения счета
// Получаем ссылку на основной план счетов базы 7.7
ПланСчетов77 = База77.ОсновнойПланСчетов;
// Ищем счет сразу через глобальный метод базы
Счет20 = База77.СчетПоКоду("20", ПланСчетов77);
Счет70 = База77.СчетПоКоду("70", ПланСчетов77);
Если вы используете специфический (не основной) план счетов, его можно получить через метаданные или глобальные переменные конфигурации 7.7, если они там определены.
Теперь полученные переменные Счет20 и Счет70 содержат корректные COM-объекты ссылочного типа, которые документ "Бухгалтерская справка" примет без проблем.
Док_77.НоваяСтрока();
Док_77.СчетДт = Счет20;
Док_77.СчетКт = Счет70;
Док_77.Субконто1 = ...; // Заполнение субконто
Док_77.Сумма = 1000;
В некоторых случаях (например, при сложной настройке DCOM или специфических версиях платформ) прямой вызов методов может работать нестабильно. В таких ситуациях можно использовать метод EvalExpr, чтобы выполнить код поиска счета внутри пространства имен 7.7.
// Вариант с полным выполнением на стороне 7.7
Счет20 = База77.EvalExpr("СчетПоКоду(""20"", ОсновнойПланСчетов)");
Этот код делает то же самое: функция СчетПоКоду выполняется движком 7.7, использует внутреннюю глобальную переменную ОсновнойПланСчетов и возвращает результат в переменную 1С 8.
При работе с бухгалтерскими справками и операциями через OLE обратите внимание на следующие моменты, которые обсуждались при решении проблемы:
СчетПоКоду вернет пустой объект, если счет не найден. Перед присвоением полезно проверить, что счет действительно найден.
Если База77.ПустоеЗначение(Счет20) = 1 Тогда
Сообщить("Счет 20 не найден в базе 7.7!");
КонецЕсли;
СоздатьОбъект("Счет") для каких-то целей (например, перебор субсчетов), не пытайтесь присвоить сам этот объект в реквизит. Не используйте конструкцию Док.Счет = ОбъекСчет.ТекущийСчет через OLE, это ненадежно. Лучше возьмите код найденного счета (ОбъектСчет.Код) и снова найдите ссылку через База77.СчетПоКоду(...).База77.CreateObject("Reference.X").FindByCode(...) и передавайте CurrentItem(), но для справочников это работает стабильнее, чем для счетов. Для счетов всегда используйте СчетПоКоду.Соберем все вместе в рабочем фрагменте кода для 1С 8.3:
// Инициализация соединения
Попытка
База77 = Новый COMОбъект("V77.Application");
Открыта = База77.Initialize(База77.RMTrade, "/D""ПутьКБазе"" /N""User"" /P""Pass""", "NO_SPLASH_SHOW");
Исключение
Сообщить("Ошибка подключения!");
Возврат;
КонецПопытки;
// Получаем ссылки на счета
// Используем ОсновнойПланСчетов, определенный в конфигурации 7.7
ПланСчетов = База77.ОсновнойПланСчетов;
СчетДт = База77.СчетПоКоду("20", ПланСчетов);
СчетКт = База77.СчетПоКоду("70", ПланСчетов);
Если (База77.ПустоеЗначение(СчетДт)=1) ИЛИ (База77.ПустоеЗначение(СчетКт)=1) Тогда
Сообщить("Не удалось найти счета в базе приемнике.");
Возврат;
КонецЕсли;
// Создаем документ
Док = База77.CreateObject("Документ.БухгалтерскаяСправка");
Док.Новый();
Док.ДатаДок = ТекущаяДата();
Док.СодержаниеОперации = "Выгрузка из ЗУП";
// Добавляем проводку
Док.НоваяСтрока();
Док.СчетДт = СчетДт;
Док.СчетКт = СчетКт;
Док.Сумма = 5000;
// Записываем
Док.Записать();
Сообщить("Документ создан: " + Док.НомерДок);
Используя этот подход, вы избежите проблем с пустыми полями счетов и обеспечите надежную передачу данных между системами.