Оглавление
Предисловие
Уважаемые читатели, мы продолжаем историю про миграцию данных из «1С:Документооборот» редакции 2.1 в 3.0.
Перед прочтением рекомендуем ознакомиться с первой и второй частями статьи. В них мы рассмотрели теоретические основы блока миграции, подготовку к этому процессу. А также вопросы «узких» мест, на которые можно повлиять, дабы ускорить миграцию.
В данной статье мы погрузимся в некоторые нюансы касающиеся систем интегрируемых с мигрирующим Документооборотом, и в первую очередь — вопросы хранения файлов и их связей с объектами интегрированных систем.
Отображение файлов, открытие данных документооборота в ДО3 после миграции
Итак, «в прошлой серии» мы выполнили оптимизацию загрузки данных и обеспечили продолжение активных бизнес-процессов, перенесённых из ДО2 в ДО3. После этого мы снова запустили пробную миграцию на тестовом контуре. Это позволило убедиться, что основные механизмы работают, а время переноса данных сократилось до приемлемых значений (2 недели).
Вспомним схему нашего контура. На ней можно заметить, что к базе Документооборота подключена база ERP:
Данный обмен реализован через бесшовную интеграцию с помощью «Библиотеки интеграции с 1С:Документооборотом» — или «БИД» (its.1c.ru/db/bid302doc).
Одной из важных возможностей, которую предоставляла интеграция — это централизованное хранение привязанных к объектам файлов на стороне документооборота в томах хранения. Пользователи прикрепляли файлы к различным документам, например к реализациям:
Теперь, после того как пробная миграция завершилась успешно — предстояло перенастроить эту интеграцию в ERP и проверить, как система поведёт себя в новой реальности.
Для этого в базе ERP мы перешли в раздел «НСИ и администрирование» → «Настройки интеграции» → «Интеграция с 1С:Документооборот», прописали в поле «URL» путь к новому опубликованному веб сервису ДО3 и установили флажок «Интеграция с 1С:Документооборотом редакции 3»:
Далее провели простую проверку: зашли как обычно в документ реализации, нажали на знакомую гиперссылку «Документооборот», чтобы проверить наличие файлов. И получили чистое поле и ошибку:
Может мы где-то промахнулись в настройках? Решили вернуться в форму интеграции, удостовериться в её корректности. И заметили, что настройка «Хранение присоединенных файлов» установлена в вариант «1С:Документооборот не используется»:
И тут важно уточнить, что до миграции при работе интеграции с ДО2 был выбран вариант «Файлы в отдельных папках», а теперь он и вовсе исчез, вот так поворот:
Выбираем доступный вариант
Получается единственным доступным режимом, предполагающим хранение файлов в ДО, остался «Файлы прикреплены к документам»? За неимением альтернативы мы выбрали его:
Для проверки снова открываем несколько документов «Реализация товаров и услуг», к которым до миграции были прикреплены файлы.
И увы ситуация всё та же.
Симптом 1. Ошибка при открытии данных ДО
При попытке перейти по гиперссылке «Документооборот» по-прежнему возникала ошибка:
Симптом 2. Пропали файлы
В режиме «Файлы прикреплены к документам» в интерфейсе документа появилась гиперссылка «Файлы ДО»:
Но как на зло, в ней также не оказалось файлов, которые ранее были прикреплены к документу
Пробуем обновить Библиотеку интеграции документооборота (БИД)
На тот момент у нас была установлена БИД для 1C:ERP версии 3.0.2.3, хотя актуальной уже была 3.0.2.7. И возникла мысль, что дело может быть в устаревшей версии.
Мы обновили БИД в надежде, что это решит проблему, но, к сожалению, ситуацию это не исправило, и режим хранения «Файлы в отдельных папках» не вернуло.
Значит, причина глубже.
Проверяем наличие файлов в ДО3
А файлы сами то на месте? Может мы потеряли их при миграции?! Заходим в базу ДО3, проверяем:
Переходим в меню «Документы» → «Файлы» и, к счастью, обнаруживаем, что:
- Все файлы на месте.
- Они лежат в тех же папках, что и раньше.
- Структура, созданная режимом «Файлы в отдельных папках», сохранилась.
Давайте разбираться.
Неизвестный тип DMInternalDocument
Начнем с относительно простого — со всплывающей ошибки в карточке документа ERP.
Имеем следующий текст ошибки:
Попытка получить тип 1С:Документооборота для неизвестного типа XDTO: DMInternalDocument
{ОбщийМодуль.ОбработкаЗапросовXDTO.Модуль(2585)}:ВызватьИсключение СтрШаблон(
{ОбщийМодуль.ОбработкаЗапросовXDTO.Модуль(2469)}:ПолучитьТипДОПоТипуXDTO(objectId.type, ИмяТипа);
{ОбщийМодуль.ОбработкаЗапросовXDTO.Модуль(4969)}:Ссылка = ПолучитьСсылкуПоObjectID(ОбъектИд);
{ОбщийМодуль.ОбработкаЗапросовXDTO.Модуль(39)}:Результат = ПолучитьОбъекты(Сообщение);
{ОбщийМодуль.ОбработкаЗапросовXDTO.Модуль(4700)}:Ответ = ОбработатьУниверсальноеСообщение(СообщениеПакета);
{ОбщийМодуль.ОбработкаЗапросовXDTO.Модуль(30)}:Результат = ОбработатьПакет(Сообщение);
{WebСервис.DMService.Модуль(5)}:Возврат ОбработкаЗапросовXDTO.ОбработатьУниверсальноеСообщение(Запрос);
Данная фраза указывает на проблему с XDTO-типами: «Попытка получить тип 1С:Документооборота для неизвестного типа XDTO: DMInternalDocument».
Тип XDTO в 1С:Документообороте используется в обмене через веб-сервис интеграции, и в ряде случае является отражением типа метаданных базы ДО. Подробнее об этом можно почитать тут its.1c.ru/db/metod8dev#content:5889:hdoc.
Кроме этого, он участвует в блоке связи объектов ДО и интегрированных систем, расскажем про это подробнее.
Как связаны объекты ERP и ДО (архитектура интеграции)
Одна из возможностей, которую предоставляет БИД — это организация двусторонней связи объектов 1С:Документооборота и интегрированной системы. Что позволяет работать с данными ДО из интерфейса этой системы, появляется возможность обрабатывать некоторые события для участия в бизнес-процессах.
Для хранения данных об этой связи используются следующие регистры сведений, которые работают в паре:
- «Объекты, интегрированные с 1С:Документооборотом» на стороне ERP;
- «Связи объектов интегрированных систем» на стороне ДО.
Регистр «Объекты, интегрированные с 1С:Документооборотом» в 1С:ERP
Это главная «шпаргалка» ERP. Здесь хранится информация о том, какой документ ERP с каким документом в ДО связан. В регистре три поля:
- Объект — ссылка на конкретный объект в самой ERP. Например, на документ реализации товаров.
- Тип XDTO объекта — строковое представления XDTO типа того объекта в ДО, с которым установлена связь. Например, тот самый «DMInternalDocument», на который у нас ругалась система.
- Идентификатор объекта ДО — GUID связанного объекта в базе документооборота.
Когда пользователь в ERP в форме документа нажимает гиперссылку «Документооборот», система идет именно в этот регистр, находит нужную запись и по ней открывает карточку документа в ДО.
Помимо этого, при модификации объектов в ERP, система регистрирует правки, после чего регламентное задание «Обмен с Документооборотом» формирует и отправляет сообщение с изменениями.
Регистр «Связи объектов интегрированных систем» в ДО
Его задача хранить обратную информацию: какой объект ДО с каким объектом ERP связан. Здесь уже четыре основных поля:
- Узел интегрированной системы — ссылка на узел плана обмена, определяющий систему, с которой настроена интеграция. В нашем случае это ERP. Документооборот может одновременно обслуживать несколько учетных систем, поэтому и узлов может быть разное количество.
- Ссылка на объект ДО — ссылка на конкретный объект в ДО. Например для ДО2 на элемент справочника «Внутренние документы».
- Тип внешнего объекта — строковое представление типа объекта интегрированной системы, уже не в нотации XDTO, а в обычном представлении метаданных 1С. Например, «Документ.РеализацияТоваровУслуг».
- Идентификатор внешнего объекта — GUID объекта интегрированной системы.
Эта связь со стороны документооборота также нужна для обработки событий. Когда в документообороте изменяется объект, механизмы интеграции формируют сообщение и отправляют его в узел. Получив сообщение, система указанного узла (ERP) находит по данным регистра соответствующий объект и синхронизирует его, применяя внесенные изменения.
Помимо этого изменения, инициированные на стороне документооборота, могут служить триггерами для событий внутри ERP. К примеру, после успешного согласования документа «Заказ клиента» в ДО, его статус меняется с «На согласовании» на «К выполнению». Это изменение попадает в очередь обмена, затем регламентное задание «Обмен с Документооборотом» передает информацию в ERP. На стороне ERP на основе полученных данных по обмену может быть автоматически создан документ «Заказ на производство».
Как регистры работают в паре
Разберём процесс на примере создания нового документа в ERP и отправки его в ДО.
- Пользователь создает в ERP документ «Реализация товаров и услуг». Система отправляет данные в ДО3.
Документооборот получает запрос web-сервиса, создает новый элемент в справочнике «Документы предприятия» и сразу делает запись в своем регистре связей.
Связь на стороне ДОERP получает подтверждение от ДО вместе с GUID созданного объекта и записывает связь уже в свой регистр: «Объект ERP связан с документом ДО, у которого GUID = такой-то, тип = DMDocument».
Связь на стороне ERPТеперь системы синхронизированы. При следующем открытии документа в ERP найдет в своем регистре GUID и откроет нужный документ в ДО.
Общая схема связей выглядит так:
Связь объектов ERP — ДО
Примечание: помимо прочего, чтобы объект участвовал в такой связи, необходимо настроить специальные правила интеграции, в которых будет указана связь реквизитов объектов и другие настройки. Иначе система не сможет определить, каким образом создавать документ в ДО. В ДО2 и ДО3 эти правила настраиваются по-разному. Здесь подробно не будем на этом останавливаться. Пример, как они выглядят в ERP с интеграцией ДО2:
Что случилось с типами?
Итак, с новыми знаниями заходим в ERP в регистр сведений «Объекты, интегрированные с 1С:Документооборотом» и видим, что здесь указан тип «DMInternalDocument».
Как мы уже писали, данный тип является отражением типа метаданных базы ДО, такой тип должен присутствовать в XDTO-пакете DM, и именно в нём находятся основные типы участвующие в обмене.
Проверим сторону ДО — для этого запустим конфигуратор базы ДО3 и пройдем в XDTO-пакет «DM»:
Ищем тип DMInternalDocument и как видим такого в ДО3 уже не существует:
Допустим, тип DMInternalDocument устарел, попробуем узнать актуальный тип. Для этого создаем тестовую реализацию в ERP и создаем связанный объект в ДО3. И далее смотрим какой тип записался в регистр. Оказалось, это «DMDocument».
Изменение типизации XDTO
Погрузившись в документацию к разным редакциям Документооборота, мы осознали, что изменение типов XDTO связано с изменением архитектуры ДО2 и ДО3.
В ДО2 объекты интегрированных систем были связаны с элементами трёх разных справочников и для каждого из них зарезервирован свой тип XDTO:
- Справочник.ВнутренниеДокументы — тип DMInternalDocument;
- Справочник.ВходящиеДокументы — тип DMIncomingDocument;
- Справочник.ИсходящиеДокументы — тип DMOutgoingDocument.
В ДО3 всё устроено проще: теперь используется единый справочник «Документы предприятия» и соответственно один тип XDTO:
- Справочник.ДокументыПредприятия — тип DMDocument.
Что важно: во время миграции все элементы справочников ВнутренниеДокументы, ВходящиеДокументы, ИсходящиеДокументы автоматически «перетекли» в новый справочник ДокументыПредприятия:
Подробнее об этих изменениях можно почитать по ссылкам:
- 1С:Документооборот КОРП. Редакция 2.1 — Глава 4.1. Типы документов:
its.1c.ru/db/doccorp21#content:769:hdoc - 1С:Документооборот. Редакция 3.0 — Глава 6.1. Единый справочник документов:
its.1c.ru/db/doc30#content:53:hdoc - 1С:Документооборот Новое в версии 3.0.10, раздел Миграция данных с предыдущих версий, Глава 5.5. Миграция документов:
its.1c.ru/db/updinfo#content:1427:hdoc:issogl2_18
Решение
Раз все прежние элементы теперь находятся в справочнике ДокументыПредприятия, логика подсказала, что для исправления ошибки может быть достаточно переименовать прежние типы "DMInternalDocument"/"DMIncomingDocument"/"DMOutgoingDocument" в регистре на новый тип «DMDocument». Проверим эту гипотезу.
Возьмем проблемный документ «Реализация товаров и услуг ТД00-002349 от 17.02.2026». Найдем по нему в регистре «Объекты, интегрированные с 1С:Документооборотом» соответствующую запись и изменим поле «Тип объекта ДО» с «DMInternalDocument» на «DMDocument»:
Повторно открываем данные документооборота из карточки документа в ERP — и, вуаля, ошибка исчезла, данные открываются корректно!
Эксперимент подтвердил: достаточно скорректировать всего одно поле в регистре «Объекты, интегрированные с 1С:Документооборотом», чтобы доступ к данным восстановился.
Только вот прикрепленные файлы при этом всё равно не появляются.
И, несмотря на кажущуюся простоту исправления, мы не станем торопиться с разработкой обработчика для пакетной замены типов. Логичнее сначала разобраться со второй проблемой — «пропажей» режима хранения «Файлы в отдельных папках» и отображением файлов в ERP. Возможно, эти две трудности связаны и исправлять их лучше в рамках единого комплексного подхода.
«Пропавшие» файлы
Теперь разберем вторую проблему: в базе ERP при подключении к ДО3 стал недоступен вариант хранения «Файлы в отдельных папках». И переключение в режим «Файлы прикреплены к документам» не помогает — ранее прикрепленные файлы не отображаются.
Чтобы выработать способ решения, давайте сначала поймем, что это вообще за варианты хранения и как они работают. Для этого вернемся «в прошлую» интеграцию с ДО2 и посмотрим, какие варианты нам были тогда доступны.
В базе ERP интегрированной с ДО2 были следующие варианты:
- «1C:Документооборот не используется».
- «Файлы прикреплены к документам».
- «Файлы в отдельных папках».
Если кратко, можно описать их так:
- Настройка «Файлы прикреплены к документам» означает, что карточка файла в базе данных ДО привязана к «клону» объекта ERP на стороне ДО, например, к элементу справочника «Внутренний документ».
- Настройка «Файлы в отдельных папках» — что владельцем карточки является элемент справочника «Папки файлов», и в этом случае в связи объектов нет необходимости.
Предлагаем изучить их по отдельности и понять в чем отличия на уровне объектов.
Режим «1С:Документооборот не используется» само собой мы не рассматриваем :)
Прежде чем продолжим, важное пояснение: хотя в настройке «Хранение присоединенных файлов» и присутствует слово «Хранение», она не влияет на физическое хранение файлов, а отвечает только за логическую структуру связей объектов конфигурации. Физически все файлы системы в любом варианте лежат в томах хранения на диске.
Том хранения файлов в 1С:Документооборот — это настройка (элемент справочника), которая указывает системе физическую папку на диске или в сети для хранения файлов. Она позволяет хранить файлы не внутри базы данных, а отдельно — на диске.
Подробнее о настройках томов можно ознакомиться по ссылке на ИТС:
- ДО2 — its.1c.ru/db/doccorp21#content:789:hdoc:issogl2_хранить_файлы_в_томах_на_диске
- ДО3 — its.1c.ru/db/doc30ruseng/content/81/hdoc
Различия настроек хранения присоединённых файлов
Настройка «Файлы прикреплены к документам»
Этот способ подразумевает, что файл будет прикрепляться только к объектам ERP, которые участвуют в интеграции. То есть тем, о которых мы рассказали выше в разделе статьи «Как связаны объекты ERP и ДО (архитектура интеграции)», для которых настроены правила интеграции.
В этом случае в момент добавления файла к документу ERP на стороне ДО2 система находит объект, с которым он связан, и этот объект становится владельцем файла. В случае если связанного объекта в ДО нет, то он создается по правилам интеграции.
Рассмотрим процесс на примере документа «Реализация товаров и услуг» — для него правила интеграции в нашей тестовой демо базе уже заданы. Проследим цепочку действий шаг за шагом, чтобы в дальнейшем увидеть разницу с проблемным случаем:
Создадим новый документ «Реализации товаров и услуг» на стороне ERP, в карточке документа переходим по гиперссылке «Файлы ДО», расположенной в командной панели:
Карточка документа ERPЗагружаем тестовый файл с диска:
Список файлов документа на стороне ERPВ верхней панели карточки документа выбираем гиперссылку «Документооборот» и обнаруживаем, что в карточке уже есть ссылка на связанный объект, хотя мы его не создавали. Это подтверждает то, что механизм интеграции создал его в момент добавления файла:
Карточка документа на стороне ERPПереключаемся в ДО2 и находим созданный по нашему документу ERP связанный элемент справочника «Внутренний документ» с прикрепленным к нему файлом:
Карточка документа на стороне ДО2Откроем карточку этого файла в ДО2. Для этого выберем файл из списка в правой части окна и дважды щелкнем левой кнопкой мыши. В открывшейся карточке файла видно, что владельцем указан целевой внутренний документ:
Карточка файлаВ том, что внутренний документ стал владельцем файла, можно убедиться открыв элемент файла через редактор реквизитов:
Ссылка на файл открытый в редакторе реквизитов
Что важно запомнить: в этом режиме файл всегда принадлежит конкретному объекту ДО созданному из ERP. Это обеспечивает прямую связь «объект ERP -> объект ДО -> файл». Однако такая логика требует обязательной настройки правил интеграции для каждого типа объектов ERP, где планируется хранение файлов.
Настройка «Файлы в отдельных папках»
Принцип работы этого метода существенно отличается от предыдущего. С этой настройкой файлы на стороне Документооборота теперь прикрепляются не к синхронизируемому объекту, а к элементу справочника «Папки файлов», создание которого не требует связи объектов. То есть в этом варианте появляется возможность прикрепить файл к любому объекту ERP из списка «Объекты, интегрируемые с 1С:Документооборотом» в настройках интеграции, в том числе к тем, у которых не настроены правила интеграции:
Разберем его ключевые особенности:
В настройках интеграции требуется указать корневую папку в ДО, где будут храниться файлы:
Настройка корневой папки хранения файловВ интерфейсе документа ERP исчезает гиперссылка «Файлы ДО»:
Отличия в командной панели карточки документаВ разделе «Документооборот» карточки документа ERP появляется вкладка «Файлы»:
Сравнение карточек данных Документооборота на стороне ERPв разных вариантах хранения файловПерейдем к файлам на стороне ДО2, в меню выбираем «Документы и файлы» → «Файлы»:
Путь к «Файлам» в интерфейсе ДО2В указанной в настройках корневой папке автоматически формируется иерархия вложенных папок по следующему принципу:
- Корневая папка.
- «Каталог типа» — папка, в наименовании которой указывается полное имя объекта метаданных ERP, например, «Документ.РеализацияТоваровУслуг».
- «Каталог объекта» — папка, с наименованием в виде представления объекта, и в скобках GUID объекта.
Хранение файлов на стороне ДО2 при варианте настройки: «Файлы в отдельных папках»Через редактор реквизитов можем убедиться, что владельцем устанавливается папка объекта:
Владелец файла объект — Папка файловДополнительно можем убедиться в том, что данный механизм позволяет прикреплять файлы к объектам ERP, для которых в системе не настроены правила бесшовной интеграции. Можем увидеть, например, что в системе есть папки для таких типов объектов, как «Приобретение товаров и услуг», «Приобретение услуг и прочих активов» и «Счет-фактура полученный»:
Типы объектов ERP на которые не настроены правила бесшовной интеграцииДля данных объектов в нашей базе отсутствовали правила интеграции, тем не менее, благодаря режиму «Файлы в отдельных папках», файлы к ним успешно прикреплялись:
Настроенные правила бесшовной интеграции объектов ERP
Решение
Изыскания в документации и погружение в тему убедили, что глаза нас не обманули и, действительно, режим «Файлы в отдельных папках» при переходе на интеграцию с ДО3, перестал поддерживаться.
Почитать можно в разделе «Библиотека интеграции с 1С:Документооборотом 3.0.2» на ИТС (its.1c.ru/db/bid302doc#content:16:hdoc).
При этом, какого-то штатного метода переноса файлов между режимами «Файлы в отдельных папках» и «Файлы прикреплены к документам» мы, увы, не нашли. Однако, необходимость «вернуть» пользователям их файлы никто не отменял. Да и отсутствие чего-то в программе нас редко когда останавливало. Так что в итоге мы пришли к идее разработки собственного механизма по переносу из прежнего режима в новый.
Шаг 1. Анализируем текущую ситуацию
Перед тем как что-то исправлять, нужно было чётко понять, в каком состоянии находятся данные после миграции. Мы проверили в ДО3 несколько документов, по которым в ДО2 использовался режим «Файлы в отдельных папках», и зафиксировали следующую картину:
| Что проверяем | Что обнаружили |
|---|---|
| Где хранятся файлы после миграции | Все файлы на месте, лежат в ДО3 в тех же папках, что и раньше |
| Кто указан владельцем файла | Владельцем по-прежнему числится папка — ровно так, как работал режим «Файлы в отдельных папках» в ДО2 |
| Есть ли в ДО3 объект, связанный с объектом ERP | В некоторых случаях объект существовал, в некоторых — нет. Напомним, что в режиме «Файлы в отдельных папках» документ в ДО мог вообще не создаваться |
| Настроены ли правила интеграции для данного типа объектов | Для части объектов правила были, для части — отсутствовали. Как мы видели ранее, режим «Файлы в отдельных папках» позволял прикреплять файлы даже без настроенных правил |
Шаг 2. Алгоритм действий
Исходя из анализа, и поскольку ситуации бывают разные (где-то документ уже есть, где-то нет, где-то правила настроены, а где-то отсутствуют), мы решили не изобретать отдельное решение для каждого случая, а разработать единый алгоритм для перехода к новому режиму хранения файлов, который покрывает все возможные сценарии.
Алгоритм состоит из четырёх шагов, выполняемых последовательно:
- Создание правил интеграции.
- Для создания связанных объектов, для всех типов объектов ERP, по которым есть файлы, должны существовать правила загрузки данных в ДО3. Там, где правил нет, их нужно создать.
- Также в рамках этого пункта потребуется создать в ДО3 элемент в справочнике «Виды документов» соответствующий типу документа в ERP, если его нет.
- Создание связанных объектов.
- Если для объекта ERP в ДО3 не существует связанного объекта, его необходимо создать.
- Смена владельца файлов.
- В ДО3 владельцем файла должен быть элемент справочника «Документы предприятия», а не папка. Для каждого файла, привязанного к папке, нужно найти соответствующий элемент и переназначить владельца.
- Формирование связей между системами.
- После того как файлы будут перепривязаны к новым владельцам, ERP должна «узнать» об этих новых связях. Для этого требуется создать соответствующие записи в регистрах интеграции — как на стороне ДО3, так и в ERP.
- В этот же пункт включим исправление ситуации с ошибкой с XDTO-типами, разобранную ранее.
Примечание:
При создании правил интеграции необходимо учесть, что при переходе с 1С:Документооборот 2.1 на версию 3.0 изменился подход к настройке бесшовной интеграции с учетными системами.
В версии 2.1 все правила обмена (загрузка, отправка, соответствие документов) настраивались исключительно на стороне ERP. В версии 3.0 настройки разделились по принципу применения: правила загрузки данных в ДО создаются в 1С:Документообороте, а правила получения изменений от ДО настраиваются на стороне ERP.
Ниже представлено сравнение места настройки правил в каждой из версий.
| Вид правил | 1С:Документооборот 2.1 | 1С:Документооборот 3.0 |
|---|---|---|
| Правила загрузки данных в ДО | Настраиваются в ERP | Настраиваются в ДО (раздел «Настройка» → «Настройки интеграции» → «Правила загрузки данных») |
| Правила получения в ERP | Настраиваются в ERP | Настраиваются в ERP (раздел «НСИ и Администрирование» → «Интеграция с 1С:Документооборотом» → «Правила интеграции» |
Шаг 3. Проверяем алгоритм на практике
Прежде чем переходить к автоматизации, нужно было убедиться, что алгоритм действительно работает в реальных условиях, а не только в теории. Для этого мы отобрали несколько документов, попавших в разные типовые ситуации после миграции, и вручную прошли по всем шагам алгоритма.
Разберем три случая:
- Объект в ДО3 создан, правила интеграции есть.
- Объект в ДО3 не создан, правила интеграции есть.
- Объект в ДО3 не создан, правил интеграции нет.
Случай № 1. Объект на стороне ДО3 создан, правила интеграции существуют
Начнем с наиболее простого сценария. Для документа ERP «Реализация товаров и услуг ТД00-002345 от 13.02.2026», к которому был прикреплен файл, в ДО3 уже существует связанный документ.
Исходная ситуация:
На стороне ERP файл не отображается:
Отсутствие ранее прикрепленного файла у документа ТД00-002345На стороне ДО3 файл присутствует, но его владельцем является папка, а не документ:
Папка с «потерянным» файлом на стороне ДО3
Процесс исправления:
Смена владельца файла в ДО3. Открываем карточку файла через редактор реквизитов и в поле «ВладелецФайла» заменяем ссылку на папку ссылкой на целевой документ ДО3:
После этой операции файл появляется в карточке документа ДО3:
Файл в карточке целевого документаКорректировка типа объекта в регистре связей ERP. В регистре сведений «Объекты, интегрированные с 1С:Документооборотом» находим запись, соответствующую нашему документу, и исправляем значение поля «Тип объекта ДО» с «DMInternalDocument» на «DMDocument»:
Скорректированная запись связи документов ERP — ДОПроверка результата. Открываем документ в ERP. Данные документооборота открываются без ошибок, а список файлов снова содержит наш файл:
Данные ДО в карточке документа ERPВосстановленное отображение файла для Реализации
Случай № 2. Объект на стороне ДО3 не создан, правила интеграции существуют
Рассмотрим на примере документа «Реализация товаров и услуг ТД00-000047 от 12.02.2026». В базе ДО файл добавлен:
Но связанного объекта не существует:
В базе ERP также видно, что связанный объект ДО отсутствует:
Процесс исправления:
Создание связанного объекта. С помощью механизма интеграции со стороны ERP создаем объект в ДО3.
Для этого в карточке документа переходим по гиперссылке «Документооборот»:
Переход к данным ДокументооборотаВ открывшемся окне выбираем команду «Создать»:
Команда «Создать» документ на стороне ДООбъект ДО успешно создан:
Созданный объект ДОВ регистре «Объекты, интегрированные с 1С:Документооборотом» автоматически появилась запись связи документа ERP c объектом ДО:
Связь документа ERP c ДО- Смена владельца файла в ДО3. С помощью редактора реквизитов аналогично Случаю № 1 заменяем владельца на созданный объект.
Проверка результата. Список файлов снова содержит наш файл:
Список файлов документа ERP
Случай № 3. Объект на стороне ДО3 не создан, правила интеграции не существуют
Этот сценарий является наиболее сложным из всех рассматриваемых, поскольку требует восстановления интеграционных связей «с нуля». Возьмём для примера документ «Счет-фактура полученный № 79 от 01.12.2022», в котором ранее был прикреплен файл, но в ДО3 отсутствует как связанный объект, так и настроенные правила интеграции для данного типа объектов:
Исходная ситуация:
- Файл физически перенесен в ДО3 и находится в папке.
- Связанный объект в ДО3 отсутствует.
- Правила бесшовной интеграции для документов «Счет-фактура полученный» не настроены:
Процесс исправления:
Создание нового вида документа. В ДО3 проверяем наличие вида документа «Счет-фактура полученный». Переходим в подсистему «НСИ» → пункт «Виды документов»:
Переход к списку Видов документовТак как искомого вида нет, создаем его:
Список Видов документовЗаполняем, записываем новый вид документа «Счет-фактура полученный»:
Карточка нового вида документа «Счет-фактура полученный»Создание правила интеграции. В меню ДО3 выбираем «Настройки» → «Настройки интеграции»:
Переход к настройкам интеграции в ДО3В открывшемся окне нажимаем на гиперссылку «Правила загрузки данных в 1С:Документооборот»:
Пункт настройки правил загрузки в ДОВ форме списка правил нажимаем кнопку «Создать» и заполняем «Вид документа» и необходимые параметры правила для объекта «Счет-фактура полученный», указывая соответствия реквизитов и настройки загрузки:
Создание правил интеграцииСозданное правило интеграции для СЧФ полученныйМы не будем подробно описывать настройку правил интеграции, об этом можно почитать по ссылке на ИТС (its.1c.ru/db/bid303doc#content:17:hdoc).
- Создание связанного документа. После создания правила интеграции в ERP становится доступной команда формирования связанного документа. Действуем аналогично Случаю № 2. В карточке документа ERP переходим по гиперссылке «Документооборот» и выбираем команду «Создать». Система автоматически:
- Смена владельца файла в ДО3. Выполняется аналогично предыдущим случаям — через редактор реквизитов открываем карточку файла и в поле «ВладелецФайла» заменяем ссылку на папку ссылкой на созданный документ ДО3.
Проверка результата. Возвращаемся в карточку документа ERP. Список файлов снова содержит ранее прикрепленный файл, данные документооборота открываются без ошибок:
Восстановленное отображение файла СЧФ
Реализация обработчиков пакетной обработки: план автоматизации
Ручные эксперименты на тестовых примерах подтвердили: предложенный алгоритм работает во всех трех сценариях. Даже в самом сложном сценарии восстановление выполнимо за три ключевых действия: создание правил, создание связанного документа ДО и смена владельца файла.
Однако за кадром остаются реальные масштабы продуктивного контура клиента:
- 700 тысяч внутренних документов ДО требуют замены типа объекта обмена в регистре связей на стороне ERP и переназначения владельцев файлов на стороне ДО3.
- 800 тысяч документов ERP нуждаются в создании документов-владельцев и привязке файлов.
Итого: порядка 1,5 миллионов объектов разной степени сложности. С таким количеством о ручном исправлении не могло быть и речи. Поэтому понимая логику восстановления и имея подтвержденный алгоритм, мы решили переложить его на код и обработать миллионы записей автоматически.
Как мы уже писали, на стороне ERP хранится информация об интегрированных объектах, но не о добавленных файлах. Поэтому большая часть исправлений предполагается на стороне ДО3, и уже затем следует синхронизация связей объектов с ERP.
План действий разбили на четыре этапа:
Этап 1. Подготовительный: анализ и создание правил интеграции.
- Создание правил интеграции. На этом этапе выявляем типы объектов ERP, по которым в ДО3 есть файлы, и создаем правила интеграции для тех, у кого их нет. Это обязательная предпосылка для последующего создания документов-владельцев.
Этап 2. Основной: обработка данных в ДО3.
- Выборка файлов к обработке. Выбираем файлы, у которых владельцем является объект папка.
- Автоматическое создание связанных объектов. Для найденных папок на основе созданных правил формируем объекты в ДО3, сразу создавая записи в регистре «Связи объектов интегрированных систем» и помечая их для выгрузки в ERP.
- Замена владельцев файлов. Обработка проходит по всем мигрировавшим файлам: если владелец — папка, ищем соответствующий объект в ДО3 (по ГУИД в представлении) и переназначаем владельца.
Этап 3. Выгрузка данных для ERP.
- Обработка формирует файл обмена со связями созданных объектов ДО и объектов ERP.
Этап 4. Загрузка в ERP.
- Обработка читает файл, создает записи в регистре «Объекты, интегрированные с 1С:Документооборотом».
- А также заменяет устаревший тип «DMInternalDocument» на «DMDocument» во всех записях.
От разовой обработки к работающей системе
Здесь следует понимать, что хоть в плане мы и оперируем понятиями «выборка», «выгрузка», «замена» — на самом деле мы подразумеваем, что для решения задачи такого масштаба (1,5 млн объектов) простой разовой обработки было бы недостаточно.
Мы предполагали, что обработать такой объем за один прогон будет проблематично, к тому же были мысли об использовании похожего исправления в будущих решениях. Поэтому требовалось создать решение, которое будет надежным, контролируемым и, при необходимости, возобновляемым.
Исходя из этого мы изначально проектировали не просто набор обработчиков, а целостную подсистему в расширении с набором необходимых метаданных для порционной обработки и контроля процесса.
Идентификация объекта ERP со стороны ДО
Отдельно хотим сказать о том, что для программного создания правил интеграции нам необходимо заранее знать для каких типов объектов это нужно сделать. А далее так и вовсе, чтобы связать с объектом ERP, нам необходимо его идентифицировать полностью до ссылки.
Как мы ранее писали: в представлении папки, куда помещаются файлы из внешних систем, содержится наименование документа и его GUID в скобках. А у её папки-родителя в наименовании указано полное имя объекта метаданных внешней системы. Эта родительская папка, в свою очередь, принадлежит к корневой папке, которая идентифицирует конкретную интегрированную систему (если только под разные системы не используется общая папка; в нашем проекте система была одна, что упростило задачу).
Эту закономерность мы и будем использовать для определения типа и ссылки объекта ERP, то есть получим их из иерархии папок файлов. Такой подход позволяет явно привязать файл к внешнему объекту и идентифицировать саму систему через общую корневую папку.
Техническая реализация: от плана к коду
Приступим к реализации. Четыре описанных этапа должны превратиться в работающий программный код, который без участия человека обработает 1,5 млн объектов. Давайте разберем техническую начинку каждого блока.
I. Подготовительный этап: создание правил интеграции
Прежде чем создавать связанные объекты в ДО, необходимо обеспечить наличие правил интеграции для всех нужных типов объектов ERP. Для этого мы реализовали обработку, которая анализирует структуру папок и автоматически генерирует недостающие правила для объектов.
Алгоритм работы обработки
- Выбираем узел ERP, с которым настраивается интеграция.
- Указываем общую корневую папку хранения файлов (заданную в настройках ERP).
- Запускаем анализ и генерацию правил.
Обработка выполняет запрос, который находит все папки, которые входят в настроенный корневой каталог (соответствующие типам объектов ERP) и сопоставляет их с существующими правилами интеграции. Для папок, по которым правила отсутствуют, автоматически создаются новые правила с предзаполненными реквизитами.
Результат работы обработки
В табличной части отображаются созданные правила интеграции. Например, на картинке отсутствует «Счет-фактура полученный» — мы создавали его вручную при разборе Случая № 3, и обработка его не дублирует. В реальном проекте таким образом было автоматически сгенерировано 62 правила интеграции, что заняло бы длительное время при ручном создании, в итоге правила сформировались в течении 5 минут.
Ключевые моменты программного создания правил
- Для каждого типа объекта автоматически определяется или создается соответствующий вид документа в ДО3.
- Создается папка документов данного вида (если отсутствует).
- Формируется правило заполнения реквизитов с настройками по умолчанию.
- Создается связанное правило интеграции в информационной системе (ERP) через механизм обмена.
Обработка работает в транзакционном режиме: при возникновении ошибки на любом этапе транзакция откатывается, информация об ошибке логируется в журнал регистрации, а проблемный объект пропускается для последующего ручного разбора.
Ключевой метод автоматического создания правил:
// Переопределяет размер порций в запрос
// Определяет объекты у которых отсутствует правило интеграции, автоматически создает их
//
&НаСервере
Процедура СгенерироватьПравилаНаСервере()
Если Не ЗначениеЗаполнено(Узел) Тогда
Сообщить("Заполните узел");
Возврат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Ссылка КАК Ссылка,
| Т.Наименование КАК Наименование
|ПОМЕСТИТЬ Папки
|ИЗ
| Справочник.ПапкиФайлов КАК Т
|ГДЕ
| Т.Родитель = &Родитель
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Папки.Ссылка КАК Папка,
| Папки.Наименование КАК Наименование,
| Правила.Ссылка КАК Правило
|ИЗ
| Папки КАК Папки
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПравилаЗагрузкиДанныхВДО КАК Правила
| ПО Папки.Наименование = Правила.ТипОбъектаИС
| И (Правила.ПометкаУдаления = ЛОЖЬ)
|ГДЕ
| Правила.Ссылка ЕСТЬ NULL";
Запрос.УстановитьПараметр("Родитель", ПапкаОбщая);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
ПравилаЗаполненияРеквизитовДО.ПолучитьЭлементы().Очистить();
Объект.ПравилаЗаполненияРеквизитовДО.Очистить();
НачатьТранзакцию();
Попытка
ТипДокумента = "Справочник.ДокументыПредприятия";
ВидДокументаСтрокой = СформироватьСиноним(СтрЗаменить(Выборка.Наименование, "Документ.", ""));
ВидДокумента = Справочники.ВидыДокументов.НайтиПоНаименованию(ВидДокументаСтрокой);
Папка = Справочники.ПапкиДокументов.НайтиПоНаименованию(ВидДокументаСтрокой);
Если ВидДокумента = Справочники.ВидыДокументов.ПустаяСсылка() Тогда
СтруктураЗаполнения = Новый Структура("Наименование, ФормаДокумента"
, ВидДокументаСтрокой, Перечисления.ВариантыФормДокументов.БумажнаяИлиЭлектронная);
ВидДокумента = Справочники.ВидыДокументов.СоздатьВидДокумента(СтруктураЗаполнения);
КонецЕсли;
Если Папка = Справочники.ПапкиДокументов.ПустаяСсылка() Тогда
СтруктураЗаполнения = Новый Структура("Наименование", ВидДокументаСтрокой);
Папка = Справочники.ПапкиДокументов.СоздатьПапку(СтруктураЗаполнения);
КонецЕсли;
Объект.ТипОбъектаДО = ТипДокумента;
Объект.ТипОбъектаИС = Выборка.Наименование;
Объект.ВидДокумента = ВидДокумента;
ЗаполнитьСтруктуруРеквизитовОбъектаДОПоПравилу();
ЗаполнитьПоУмолчаниюНаСервере(Ложь, Истина, Истина);
Для Каждого текДанные Из ПравилаЗаполненияРеквизитовДО.ПолучитьЭлементы() Цикл
Если текДанные.ИмяРеквизитаОбъектаДО = "ВидДокумента" Тогда
текДанные.ЗначениеРеквизитаДО = ВидДокумента;
текДанные.Вариант = Перечисления.ВариантыПравилЗаполненияРеквизитов.УказанноеЗначение;
КонецЕсли;
Если текДанные.ИмяРеквизитаОбъектаДО = "Папка" Тогда
текДанные.ЗначениеРеквизитаДО = Папка;
текДанные.Вариант = Перечисления.ВариантыПравилЗаполненияРеквизитов.УказанноеЗначение;
КонецЕсли;
Если текДанные.ИмяРеквизитаОбъектаДО = "ФормаДокумента" Тогда
текДанные.ЗначениеРеквизитаДО = Перечисления.ВариантыФормДокументов.БумажнаяИлиЭлектронная;
текДанные.Вариант = Перечисления.ВариантыПравилЗаполненияРеквизитов.УказанноеЗначение;
КонецЕсли;
КонецЦикла;
ПеренестиПравилаВОбъект(Объект, ПравилаЗаполненияРеквизитовДО);
НовоеПравило = Справочники.ПравилаЗагрузкиДанныхВДО.СоздатьЭлемент();
НовоеПравило.ВидДокумента = Объект.ВидДокумента;
НовоеПравило.ИзМакета = Ложь;
НовоеПравило.Комментарий = "Сгенерировано автоматически";
НовоеПравило.ПредставлениеОбъектаДО = "Документ, " + ВидДокументаСтрокой;
НовоеПравило.ПредставлениеОбъектаИС = ВидДокументаСтрокой;
НовоеПравило.ТипОбъектаДО = Объект.ТипОбъектаДО;
НовоеПравило.ТипОбъектаИС = Объект.ТипОбъектаИС;
НовоеПравило.ТипФайловСохраненияПечатныхФорм = Перечисления.ТипыФайловСохраненияПечатныхФормОбъектов.PDF_A_1;
НовоеПравило.УзелИнтегрированнойСистемы = Узел;
НовоеПравило.ПравилаЗаполненияРеквизитовДО.Загрузить(Объект.ПравилаЗаполненияРеквизитовДО.Выгрузить());
НовоеПравило.Записать();
Стр = СозданныеПравила.Добавить();
Стр.Правило = НовоеПравило.Ссылка;
СоздатьСвязанноеПравилоИнтеграцииВИС(
Узел,
НовоеПравило.Ссылка,
НовоеПравило.ВидДокумента,
НовоеПравило.ТипОбъектаДО,
НовоеПравило.ТипОбъектаИС,
НовоеПравило.ПредставлениеОбъектаДО,
НовоеПравило.Комментарий);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
Сообщить("Ошибка при создании правила для " + Выборка.Наименование + ". " + ТекстОшибки);
ЗаписьЖурналаРегистрации("ГенерацияПравилОбмена", УровеньЖурналаРегистрации.Ошибка, Выборка.Папка, Выборка.Папка, ТекстОшибки);
КонецПопытки;
КонецЦикла;
КонецПроцедуры
II. Основной этап: создание документов, замена владельцев
Подготовительный этап заложил фундамент: правила интеграции созданы, и теперь будем решать задачи создания недостающих связанных объектов, и замену владельцев в файлах.
Логика алгоритма
- С некоторой периодичностью запускается регламентное задание, которое отбирает из справочника файлов порцию ссылок с папками, находящимися в иерархии корневой папки. И формирует несколько потоков обработки в виде фоновых заданий
- Фоновое задание для каждого файла из порции:
- Определяет тип и GUID внешнего объекта по наименованиям папок.
- Ищет связанный объект ДО по GUIDу объекта в регистре «Связи объектов интегрированных систем».
- В случае если связанного объекта нет — создает его и добавляет запись в регистр связей.
- Заменяет владельца файла с папки на связанный объект ДО.
Описание вспомогательных метаданных
Константы для идентификации контура интеграции:
- Узел интегрированной системы: указывает на конкретный узел обмена, в нашем проекте это единственный узел ERP. Это необходимо для фильтрации данных и корректной адресации при обмене.
- Общая папка хранения файлов: задает корневую папку в ДО3, которая в настройках интеграции ERP указана как точка монтирования для режима «Файлы в отдельных папках».
Регистры сведений для хранения промежуточных данных:
ОбработанныеПапки: регистр, фиксирующий факт обработки файла и замены его владельца.
Измерения:
- Папка: папка файла до замены.
Файл: целевой обрабатываемый файл.
Ресурсы:
- Замена: ссылка на документ предприятия — новый владелец.
- Ошибка: признак ошибки.
- ТекстОшибки: текст ошибки.
- Дата: дата обработки.
- Выгружено: признак выгрузки, для дальнейшей порционной выгрузки.
Регистр хранения обработанных папокСоответствиеВидовДокументовПапок: регистр для явного указания соответствий между объектами ERP и структурой ДО3. В нашем случае часть папок и видов документов в ДО3 уже существовала на момент миграции — они могли быть созданы ранее при тестовых интеграциях или в ходе другой деятельности пользователей.
Если бы мы доверились только автоматической генерации, система могла бы создать дублирующие виды и папки, что привело бы к лишним объектам в системе и путанице.
Этот регистр решает проблему однозначно — он позволяет в ручном или полуавтоматическом режиме указать, какой именно вид документа ДО3 и какую целевую папку следует использовать при создании нового документа-владельца для конкретного типа объектов ERP.
Измерения:
Папка: папка файла до замены.
Ресурсы:
- ПапкаДокументов: папка документа.
- ВидДокумента: вид документа.
Регистр хранения соответствий видов документов и папок
Общий модуль и регламентное задание
- Общий модуль ОбработкаЗамены: вся серверная логика по поиску «потерянных» файлов, созданию документов и переназначению владельцев вынесена в этот модуль.
- Регламентное задание ЗаменаВладельцевФайлов: инициирует выполнение основной обработки в фоновом режиме.
Метод регламентного задания «ЗаменаВладельцевФайловНачать()»
Запускает многопоточную обработку по замене владельцев файлов. Разбивает общую коллекцию объектов на порции и для каждой порции создает отдельное фоновое задание:
// Запускает замену владельцев файла на документы
//
Процедура ЗаменаВладельцевФайловНачать() Экспорт
ПредставлениеЗадания = "Замена владельцев папок (Files)";
ПараметрыОтбора = Новый Структура("Наименование, Состояние", ПредставлениеЗадания, СостояниеФоновогоЗадания.Активно);
Если ФоновыеЗадания.ПолучитьФоновыеЗадания(ПараметрыОтбора).Количество() Тогда
Возврат;
КонецЕсли;
Коллекция = КоллекцияКОбработкеПолучить();
КоличествоПотоков = 30;
ДанныеКоличество = Коллекция.Количество();
Размер = ДанныеКоличество / КоличествоПотоков;
ПорцияРазмер = ?(Размер = Цел(Размер), Размер, Цел(Размер) + 1);
ПорцияЗадания = Новый Массив;
МетодаИмя = "files_ОбработкаЗамены.ФайлыВладельцыЗаменить";
Задания = Новый Массив;
Для Каждого СоставПараметров Из Коллекция Цикл
ПорцияЗадания.Добавить(СоставПараметров);
Если ПорцияЗадания.Количество() = ПорцияРазмер Тогда
ПараметрыМетода = Новый Массив;
ПараметрыМетода.Добавить(Новый Структура("Порция", ПорцияЗадания));
Задания.Добавить(ФоновоеЗапустить(МетодаИмя, ПараметрыМетода, , ПредставлениеЗадания));
ПорцияЗадания = Новый Массив;
КонецЕсли;
КонецЦикла;
Если ПорцияЗадания.Количество() Тогда
ПараметрыМетода = Новый Массив;
ПараметрыМетода.Добавить(Новый Структура("Порция", ПорцияЗадания));
Задания.Добавить(ФоновоеЗапустить(МетодаИмя, ПараметрыМетода, , ПредставлениеЗадания));
КонецЕсли;
КонецПроцедуры // ЗаменаВладельцевФайловНачать
Метод регламентного задания «КоллекцияКОбработкеПолучить()»
Коллекцию объектов для обработки мы получаем непосредственно из папок хранения файлов так как именно эти объекты являются первоисточником к обработке:
// Возвращает коллекцию к обработке
//
// Возвращаемое значение:
// Массив
//
Функция КоллекцияКОбработкеПолучить() Экспорт
ПапкаХраненияФайлов = Константы.files_ПапкаХраненияФайлов.Получить();
Если Не ЗначениеЗаполнено(ПапкаХраненияФайлов) Тогда
ВызватьИсключение "Не заполнена константа ""Папка хранения файлов (Files)""...";
КонецЕсли;
Запрос = Новый Запрос(ТекстЗапросаВыборка());
Запрос.УстановитьПараметр("Родитель", ПапкаХраненияФайлов);
Выборка = Запрос.Выполнить().Выбрать();
Результат = Новый Массив;
Пока Выборка.Следующий() Цикл
Данные = Новый Структура("Файл, ГУИД, ВладелецФайла");
ЗаполнитьЗначенияСвойств(Данные, Выборка);
Результат.Добавить(Данные);
КонецЦикла;
Возврат Результат;
КонецФункции // КоллекцияКОбработкеПолучить
// Возвращает текст запроса данных к обработке
//
// Возвращаемое значение:
// Строка
//
Функция ТекстЗапросаВыборка()
Возврат "ВЫБРАТЬ ПЕРВЫЕ 5000
| Т.Ссылка КАК Файл,
| Т.ВладелецФайла КАК ВладелецФайла,
| ПРАВ(ВЫРАЗИТЬ(Т.ВладелецФайла КАК Справочник.ПапкиФайлов).Наименование, 38) КАК ГУИД
|ИЗ
| Справочник.Файлы КАК Т
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.files_ОбработанныеПапки КАК Обработанные
| ПО Т.Ссылка = Обработанные.Файл
| И (Обработанные.Ошибка)
|ГДЕ
| ТИПЗНАЧЕНИЯ(Т.ВладелецФайла) = ТИП(Справочник.ПапкиФайлов)
| И Обработанные.Файл ЕСТЬ NULL
| И Т.ВладелецФайла.Родитель.Родитель = &Родитель";
КонецФункции // ТекстЗапросаВыборка
Основной метод замены владельцев «ФайлыВладельцыЗаменить()»
Выполняет замену владельца для группы файлов с папки на документ ДО3. Для каждого файла из переданной порции данных выполняет:
- Поиск связанного объекта ДО3 по GUID объекта ERP.
- Создание нового объекта, если он не найден.
- Установку найденного/созданного документа владельцем файла.
- Запись результата обработки в регистр files_ОбработанныеПапки.
// Заменяет владельцы файлов на документы по полученному ГУИД
//
// Параметры:
// Источник - Массив - Источник
//
Процедура ФайлыВладельцыЗаменить(Источник) Экспорт
Узел = Константы.files_УзелИнтегрированнойСистемы.Получить();
Для каждого Элемент Из Источник Цикл
ГуидПредварительно = Элемент.ГУИД;
ГУИД = СтрЗаменить(ГуидПредварительно, "(", "");
ГУИД = СтрЗаменить(ГУИД, ")", "");
ТекстОшибки = "";
ВладелецПапка = Элемент.ВладелецФайла;
ВладелецНовый = Неопределено;
НачатьТранзакцию();
Попытка
ФайлОбъект = Элемент.Файл.ПолучитьОбъект();
ВладелецНовый = СвязанныйДокументПолучить(ГУИД, Узел);
Если ВладелецНовый = Неопределено Тогда
ДокументСоздать(ВладелецПапка, ВладелецНовый, ФайлОбъект.Автор, ФайлОбъект.ДатаСоздания);
СвязиСоздать(ВладелецПапка, ВладелецНовый, ГУИД);
КонецЕсли;
ФайлОбъект.ВладелецФайла = ВладелецНовый;
ФайлОбъект.Записать();
ЗафиксироватьТранзакцию();
Исключение
ТекстОшибки = СтрШаблон("По файлу %1 и строке гуид %2 не удалось заменить владельца по причине: %3"
, Элемент.Файл
, ГУИД
, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ОтменитьТранзакцию();
КонецПопытки;
ПараметрыФиксации = Новый Структура("Папка, Файл, Замена, ТекстОшибки"
, ВладелецПапка
, Элемент.Файл
, ВладелецНовый
, ТекстОшибки);
РезультатЗафиксировать(ПараметрыФиксации);
КонецЦикла;
КонецПроцедуры // ФайлыВладельцыЗаменить
Метод идентификации связанного объекта ERP «СвязанныйДокументПолучить()»
Выполняет поиск связанного документа ДО3 по идентификатору внешнего объекта ERP и узлу указанному в константе:
// Выполняет поиск связанного документа ДО3 по идентификатору внешнего объекта ERP
// и узлу интегрированной системы. Используется для определения, существует ли уже
// в ДО3 документ, соответствующий конкретному объекту ERP в рамках заданного узла обмена.
//
// Параметры:
// Идентификатор - Строка - GUID объекта ERP, по которому выполняется поиск.
// Узел - ПланОбменаСсылка - Узел интегрированной системы
//
// Возвращаемое значение:
// СправочникСсылка.ДокументыПредприятия - ссылка на найденный документ ДО3,
// если связь существует.
// Неопределено - если связанный документ не найден.
//
Функция СвязанныйДокументПолучить(Идентификатор, Узел)
Результат = Неопределено;
Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 1
| Т.СсылкаНаОбъектДО КАК СсылкаНаОбъектДО
|ИЗ
| РегистрСведений.СвязиОбъектовИнтегрированныхСистем КАК Т
|ГДЕ
| Т.ИДВнешнегоОбъекта = &ИДВнешнегоОбъекта
| И ТИПЗНАЧЕНИЯ(Т.СсылкаНаОбъектДО) = ТИП(Справочник.ДокументыПредприятия)
| И Т.УзелИнтегрированнойСистемы = &УзелИнтегрированнойСистемы");
Запрос.УстановитьПараметр("ИДВнешнегоОбъекта", Идентификатор);
Запрос.УстановитьПараметр("УзелИнтегрированнойСистемы", Узел);
РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Результат = Выборка.СсылкаНаОбъектДО;
КонецЕсли;
Возврат Результат;
КонецФункции // СвязанныйДокументПолучить()
Метод создания документа ДО «ДокументСоздать»
Если не обнаружили связанный документ с внешним объектом интегрированной системы создаем документ по данным папки-владельца файла:
- Определяем вид и папку документа по родителю папки, используя настройки соответствия.
- Заполняем реквизиты: наименование (из папки), автора, дату, форму (Электронная).
- В регистре «Текущие состояния документов» устанавливаем статус «Проект». По данному регистру определяются настройки доступности документов а так же файлов для редактирования и просмотра. Настройка доступности по состоянию производится в справочнике «Настройки доступности по состоянию». Подробно о настройках доступности по состоянию можно ознакомиться на ИТС (its.1c.ru/db/doc30/content/262/hdoc).
// Создает карточку документа для привязки к файлу
//
// Параметры:
// ВладелецПапка - Справочник.ПапкиФайлов - Папка файла
// СсылкаНового - Справочник.ДокументПредприятия - Ссылка нового
// Автор - Справочник.Сотрудники/Пользователи - Ссылка нового
//
// Возвращаемое значение
// Справочник.ДокументыПредприятия
//
Процедура ДокументСоздать(ВладелецПапка, СсылкаНового, Автор, ДатаСоздания)
ДанныеВладельца = Новый Структура("Родитель, Наименование");
ЗаполнитьЗначенияСвойств(ДанныеВладельца, ВладелецПапка);
РодительВладельца = ДанныеВладельца.Родитель;
Настройки = НастройкиСозданияДокумента(РодительВладельца);
ТекстОшибки = "";
Если Настройки.ВидДокумента = Неопределено Тогда
ТекстОшибки = СтрШаблон("По родителю %1 папки %2 не удалось определить вид документа
|из регистра Соответствие видов документов папок (Files)..."
, РодительВладельца
, ДанныеВладельца.Наименование);
КонецЕсли;
Если Настройки.ПапкаДокумента = Неопределено Тогда
Текст = СтрШаблон("По родителю %1 папки %2 не удалось определить папку документа
|из регистра Соответствие видов документов папок (Files)..."
, РодительВладельца
, ДанныеВладельца.Наименование);
ТекстОшибки = ТекстОшибки + Символы.ПС + Текст;
КонецЕсли;
Если ТекстОшибки <> "" Тогда
ВызватьИсключение ТекстОшибки;
КонецЕсли;
ДокументНовый = Справочники.ДокументыПредприятия.СоздатьЭлемент();
ДокументНовый.Наименование = ДанныеВладельца.Наименование;
ДокументНовый.ДатаСоздания = ДатаСоздания;
ДокументНовый.ВидДокумента = Настройки.ВидДокумента;
ДокументНовый.Папка = Настройки.ПапкаДокумента;
ДокументНовый.Заголовок = ДанныеВладельца.Наименование;
ДокументНовый.Создал = Автор;
ДокументНовый.Комментарий = СозданоАвтоматическиТекст();
ДокументНовый.ФормаДокумента = Перечисления.ВариантыФормДокументов.Электронная;
//ДокументНовый.ОбменДанными.Загрузка = Истина;
ДокументНовый.Записать();
СсылкаНового = ДокументНовый.Ссылка;
СостояниеДокументаУстановить(ДокументНовый.Ссылка, Перечисления.СостоянияДокументов.Проект);
КонецПроцедуры // ДокументСоздать
// Устанавливает текущее состояние для указанного документа в регистре сведений
// ТекущиеСостоянияДокументов.
//
// Параметры:
// Элемент - ЛюбаяСсылка - Ссылка на документ или другой объект метаданных,
// для которого устанавливается состояние.
// Состояние - ПеречислениеСсылка.СостоянияДокументов - Устанавливаемое состояние
//
Процедура СостояниеДокументаУстановить(Элемент, Состояние) Экспорт
// Создаем набор записей регистра с отбором по документу и состоянию
НаборЗаписей = РегистрыСведений.ТекущиеСостоянияДокументов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Документ.Установить(Элемент);
НаборЗаписей.Отбор.Состояние.Установить(Состояние);
// Добавляем новую запись
Запись = НаборЗаписей.Добавить();
Запись.Документ = Элемент;
Запись.Состояние = Состояние;
Запись.ДатаУстановки = ТекущаяДатаСеанса();
// Выполняем запись с обработкой возможных ошибок
Попытка
НаборЗаписей.Записать();
Исключение
// В случае ошибки записываем информацию в журнал регистрации
ЗаписьЖурналаРегистрации(
"ТекущиеСтатусыДокументов.Ошибка",
УровеньЖурналаРегистрации.Ошибка,
, // Необязательные параметры пропускаем
Элемент,
СтрШаблон("Не удалось установить статус проект по причине %1",
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())));
КонецПопытки;
КонецПроцедуры
Метод «СвязиСоздать»
Создает связь документа ДО с интегрированным объектом ERP в регистре «Связи объектов интегрированных систем»:
// Создает связи интеграции по узлу
//
// Параметры:
// ВладелецПапка - Справочник.ПапкиФайлов - ВладелецПапка
// ВладелецНовый - Справочник.ДокументыПредприятия - ВладелецНовый
// ГУИД - Строка - ГУИД
//
Процедура СвязиСоздать(ВладелецПапка, ВладелецНовый, ГУИД)
ДанныеВладельца = Новый Структура("Родитель, Наименование");
ЗаполнитьЗначенияСвойств(ДанныеВладельца, ВладелецПапка);
УзелИнтегрированнойСистемы = Константы.files_УзелИнтегрированнойСистемы.Получить();
СсылкаНаОбъектДО = ВладелецНовый;
ИДВнешнегоОбъекта = ГУИД;
ТипВнешнегоОбъекта = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ДанныеВладельца.Родитель, "Наименование");
НаборЗаписей = РегистрыСведений.СвязиОбъектовИнтегрированныхСистем.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.УзелИнтегрированнойСистемы.Установить(УзелИнтегрированнойСистемы);
НаборЗаписей.Отбор.СсылкаНаОбъектДО.Установить(СсылкаНаОбъектДО);
НаборЗаписей.Отбор.ТипВнешнегоОбъекта.Установить(ТипВнешнегоОбъекта);
НаборЗаписей.Отбор.ИДВнешнегоОбъекта.Установить(ИДВнешнегоОбъекта);
Запись = НаборЗаписей.Добавить();
Запись.УзелИнтегрированнойСистемы = УзелИнтегрированнойСистемы;
Запись.СсылкаНаОбъектДО = СсылкаНаОбъектДО;
Запись.ТипВнешнегоОбъекта = ТипВнешнегоОбъекта;
Запись.ИДВнешнегоОбъекта = ИДВнешнегоОбъекта;
НаборЗаписей.Записать();
КонецПроцедуры // СвязиСоздать
Итоги основного этапа
В рамках основного этапа мы не просто запустили пакетную обработку, а привели в действие целостную систему исправления связей объектов и отображения файлов. Опираясь на подготовленные на предыдущем шаге правила интеграции, разработанный механизм выполнил полный цикл восстановления данных в ДО3:
- Провел инвентаризацию «потерянных» файлов, идентифицировав их по структуре папок.
- Нашел существующие документы-владельцы для части файлов.
- Создал недостающие документы в ДО3 для тех объектов ERP, у которых их не было.
- Переназначил владельцев для всех файлов, переподчинив их от папок к документам ДО3.
- Зафиксировал результаты каждой операции в специализированных регистрах, обеспечив прозрачность и возможность проконтролировать процесс.
Все операции выполнялись в многопоточном режиме, что позволило обработать полуторамиллионный массив данных за приемлемое время. Благодаря 30 параллельным потокам и порционной обработке, замена владельцев и создание документов для 1,5 млн объектов заняли около часа, что считаем отличным результатом для такого объема данных.
III. Выгрузка данных для ERP
Когда основная обработка данных в ДО3 завершена — объекты-владельцы созданы, файлы переназначены, а связи зарегистрированы — перед нами встала задача передать эту информацию в ERP. Без этого шага все выполненные операции останутся «известными» только документообороту, а в карточках документов ERP файлы по-прежнему отображаться не будут.
Для решения этой задачи мы реализовали механизм, который выгружает данные о новых связях в формате, понятном для ERP.
Основная сложность заключалась в объемах. Выгружать сразу миллион записей — значит получить гигантский файл, с которым ERP не сможет работать. Поэтому мы применили порционный подход. Порционная выгрузка выполняет не только функцию оптимизации, но и служит индикатором успешности переноса. Механизм работы построен по принципу «транзакционного файла»: порция удаляется только после полной и успешной загрузки в ERP. Следовательно, наличие файла в каталоге однозначно говорит о том, что данные этой порции еще не загружены. Это простое и надежное решение, перешедшее к нам из опыта миграции с ДО2, позволяет гарантировать сохранность данных на всех этапах.
Логика выгрузки
В обработке был реализован метод «ВыгрузитьНаСервере()». Его задача — выбирать из регистра «files_ОбработанныеПапки» записи, по которым уже выполнена замена владельца, но которые еще не были выгружены, и сохранять их в файл для передачи в ERP.
Алгоритм работы
- Запрос порции данных. Метод выполняет запрос к регистру «files_ОбработанныеПапки», отбирая записи, не попавшие во временную таблицу. Размер порции настраивается (По умолчанию это 50 000 записей).
Формирование табличного документа. С полями:
- ИДВнешнегоОбъекта — GUID объекта ERP, извлеченный ранее из наименования папки.
- ТипОбъектаИС — тип объекта ERP, например «Документ.РеализацияТоваровУслуг».
- ИДДО — GUID объекта ДО.
Пример файла выгрузки для ERPСохранение файла.
- Каждая сформированная порция сохраняется в отдельный файл формата ODS. Особых причин по выбору именно этого формата нет, просто у нас под рукой были наработки готового алгоритма для него, поэтому их и использовали.
- Имя файла генерируется с порядковым номером, чтобы при передаче не возникало путаницы — ДанныеСинхронизацииДО_1.ods, ДанныеСинхронизацииДО_2.ods и так далее.
Пример выгрузки файлов для ERP- Фиксация выгрузки. После успешного сохранения файла метод вызывает процедуру «ВыгруженоУстановить()», которая проставляет признак выгрузки для обработанных записей в регистре «files_ОбработанныеПапки». Это гарантирует, что в следующих порциях эти записи не попадут в выборку повторно.
- Повторение цикла. Метод продолжает работу, пока в регистре остаются невыгруженные записи. Как только очередной запрос возвращает пустой результат, выгрузка завершается.
Код порционной выгрузки данных в файл
// // Выгружает данные из базы источника в соответствии с макетом и текстом запроса.
// Реализован механизм пакетной обработки: данные выгружаются порциями,
// количество записей в порции задается в реквизите "Порция" (через замену в тексте запроса).
// Каждая порция сохраняется в отдельный файл ODS.
// В процессе работы ведется учет выгруженных объектов для исключения дублирования
// в последующих порциях.
//
&НаСервере
Процедура ВыгрузитьНаСервере()
Выгружать = Истина;
ТаблицаОбработанных = Новый ТаблицаЗначений;
Тип36 = Новый КвалификаторыСтроки(36);
Тип1024 = Новый КвалификаторыСтроки(1024);
ТаблицаОбработанных.Колонки.Добавить("ИДВнешнегоОбъекта", Новый ОписаниеТипов("Строка", Тип36));
ТаблицаОбработанных.Колонки.Добавить("ТипОбъектаИС", Новый ОписаниеТипов("Строка", Тип1024));
СчетчикФайл = 0;
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
Макет = ОбработкаОбъект.ПолучитьМакет("Макет");
ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
ТекстЗапроса = ТекстЗапроса();
Если Объект.Порция <> "" Тогда
ТекстПоиска = "50000 //Замена";
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ТекстПоиска, Объект.Порция);
КонецЕсли;
Пока Выгружать Цикл
СчетчикФайл = СчетчикФайл + 1;
Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("Обработанные", ТаблицаОбработанных);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Выгружать = Ложь;
Прервать;
КонецЕсли;
ТабЛицаРезультат = РезультатЗапроса.Выбрать();
ТабДок = Новый ТабличныйДокумент;
ТабДок.Вывести(ОбластьЗаголовок);
ОтметитьВыгрузку = Новый Массив;
Пока ТабЛицаРезультат.Следующий() Цикл
ОбластьСтрока.Параметры.ИДВнешнегоОбъекта = ТабЛицаРезультат.ИДВнешнегоОбъекта;
ОбластьСтрока.Параметры.ИДДО = Строка(ТабЛицаРезультат.Замена.УникальныйИдентификатор());
ОбластьСтрока.Параметры.ТипОбъектаИС = ТабЛицаРезультат.ТипОбъектаИС;
ТабДок.Вывести(ОбластьСтрока);
НоваяСтрока = ТаблицаОбработанных.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ТабЛицаРезультат);
ОтметкаВыгрузки = Новый Структура("Папка, Файл");
ЗаполнитьЗначенияСвойств(ОтметкаВыгрузки, ТабЛицаРезультат);
ОтметитьВыгрузку.Добавить(ОтметкаВыгрузки);
КонецЦикла;
ПутьЗаписи = СтрШаблон("%1\ДанныеСинхронизацииДО_%2.ods", ПутьВыгрузки, СчетчикФайл);
ТабДок.Записать(ПутьЗаписи, ТипФайлаТабличногоДокумента.ODS);
ВыгруженоУстановить(ОтметитьВыгрузку);
КонецЦикла;
КонецПроцедуры
// Обновляет значение ресурса "Выгружено" в существующих записях регистра
// сведений files_ОбработанныеПапки для указанных ключей "Папка, Файл".
//
// !!! Важно: Новые записи не создаются. Обновляются только существующие.
// Другие измерения и ресурсы регистра не изменяются.
//
// Параметры:
// МассивДанных - Массив - Коллекция структур, каждая из которых содержит ключи:
// * Папка - СправочникСсылка.ПапкиФайлов - Ссылка на папку (обязательно).
// * Файл - СправочникСсылка.Файлы - Ссылка на файл (обязательно).
// Выгружено - Булево - Новое значение ресурса "Выгружено".
// По умолчанию: Истина.
//
// Возвращаемое значение:
// Число - Количество записей, которые были обновлены.
//
&НаСервереБезКонтекста
Функция ВыгруженоУстановить(МассивДанных, Выгружено = Истина) Экспорт
Если МассивДанных = Неопределено ИЛИ МассивДанных.Количество() = 0 Тогда
Возврат 0;
КонецЕсли;
КоличествоОбновлено = 0;
// Начинаем транзакцию для обеспечения целостности
НачатьТранзакцию();
Попытка
Для Каждого ЭлементДанных Из МассивДанных Цикл
// Проверка наличия необходимых ключей
Если ЭлементДанных = Неопределено
ИЛИ ЭлементДанных.Свойство("Папка") = Ложь
ИЛИ ЭлементДанных.Свойство("Файл") = Ложь Тогда
Продолжить;
КонецЕсли;
Папка = ЭлементДанных.Папка;
Файл = ЭлементДанных.Файл;
// Пропускаем пустые ссылки
Если Папка = Неопределено ИЛИ Папка.Пустая()
ИЛИ Файл = Неопределено ИЛИ Файл.Пустая() Тогда
Продолжить;
КонецЕсли;
// Создаем менеджер записи для конкретного ключа
МенеджерЗаписи = РегистрыСведений.files_ОбработанныеПапки.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Папка = Папка;
МенеджерЗаписи.Файл = Файл;
// Пытаемся прочитать существующую запись
МенеджерЗаписи.Прочитать();
// Если запись существует - обновляем только ресурс Выгружено
Если МенеджерЗаписи.Выбран() Тогда
МенеджерЗаписи.Выгружено = Выгружено;
МенеджерЗаписи.Записать();
КоличествоОбновлено = КоличествоОбновлено + 1;
КонецЕсли;
КонецЦикла;
// Фиксируем транзакцию
ЗафиксироватьТранзакцию();
Исключение
// Отменяем транзакцию при ошибке
ОтменитьТранзакцию();
// Логируем ошибку
ТекстОшибки = СтрШаблон(
"Ошибка при обновлении признака выгружено (обновлено %1 из %2): %3",
КоличествоОбновлено,
МассивДанных.Количество(),
ОписаниеОшибки());
ЗаписьЖурналаРегистрации(
"files_ОбработанныеПапки.Ошибка",
УровеньЖурналаРегистрации.Ошибка,
, , , ТекстОшибки);
// Пробрасываем исключение дальше
ВызватьИсключение ТекстОшибки;
КонецПопытки;
Возврат КоличествоОбновлено;
КонецФункции
// Возвращает текст запроса получения данных синхронизации
//
// Возвращаемое значение:
// Строка
&НаСервереБезКонтекста
Функция ТекстЗапроса()
Возврат "ВЫБРАТЬ РАЗЛИЧНЫЕ
| ОП.Замена КАК Замена,
| ОП.Папка КАК Папка,
| ОП.Файл КАК Файл,
| ОП.Замена.ВидДокумента КАК ЗаменаВидДокумента,
| СВ.ИДВнешнегоОбъекта КАК ИДВнешнегоОбъекта,
| ПравилаЗагрузкиДанныхВДО.ТипОбъектаИС КАК ТипОбъектаИС
|ПОМЕСТИТЬ ДанныеПредварительно
|ИЗ
| РегистрСведений.files_ОбработанныеПапки КАК ОП
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СвязиОбъектовИнтегрированныхСистем КАК СВ
| ПО ОП.Замена = СВ.СсылкаНаОбъектДО
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПравилаЗагрузкиДанныхВДО КАК ПравилаЗагрузкиДанныхВДО
| ПО ОП.Замена.ВидДокумента = ПравилаЗагрузкиДанныхВДО.ВидДокумента
|ГДЕ
| СВ.ИДВнешнегоОбъекта ЕСТЬ НЕ NULL
| И ПравилаЗагрузкиДанныхВДО.ТипОбъектаИС ЕСТЬ НЕ NULL
| И НЕ ОП.Ошибка
| И НЕ ОП.Выгружено
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ ПЕРВЫЕ 50000 //Замена
| ДанныеПредварительно.ИДВнешнегоОбъекта КАК ИДВнешнегоОбъекта,
| ДанныеПредварительно.Папка КАК Папка,
| ДанныеПредварительно.Замена КАК Замена,
| ДанныеПредварительно.Файл КАК Файл,
| ДанныеПредварительно.ТипОбъектаИС КАК ТипОбъектаИС
|ИЗ
| ДанныеПредварительно КАК ДанныеПредварительно";
КонецФункции // ТекстЗапроса
Далее файлы передаются на сторону ERP для выполнения завершающего шага — загрузки в регистр «Объекты, интегрированные с 1С:Документооборотом».
Итоги этапа выгрузки данных для ERP
С помощью данного механизма нам удалось превратить миллионный массив данных в компактные, готовые к передаче файлы. На нашем проекте было сформировано порядка 150 файлов (при объеме 1,5 млн записей и порции в 10 000 записей), а общее время выгрузки составило не более 15 минут.
IV. Загрузка данных для ERP (Синхронизация связей интеграции)
Теперь, когда данные связей подготовлены и упакованы в порционные файлы, перед нами стоит завершающая задача: корректно загрузить эти файлы на сторону ERP, восстановить связи в регистре «Объекты, интегрированные с 1С:Документооборотом» и, наконец, вернуть отображение файлов в карточках документов.
Архитектура загрузки данных
В отличие от описанных ранее задач здесь мы работаем исключительно в контуре ERP.
Прежде чем погружаться в детали, давайте посмотрим на общую схему процесса. Она наглядно демонстрирует маршрут данных: от файлов загрузки в каталоге до фиксации связей в регистре ERP и обработки нештатных ситуаций.
В основе лежит регламентное задание, которое выполняет ряд действий по шагам.
Шаг 1. Замена типов
Поиск всех записей с типами DMInternalDocument, DMIncomingDocument и DMOutgoingDocument и замена их на актуальный DMDocument.
Обработка производится циклически: за один проход выбирает порцию из 1000 записей со старыми типами, заменяет их и переходит к следующей порции. Процесс продолжается до тех пор, пока в регистре не останется ни одной записи с устаревшими типами.
// Выполняет многопоточную синхронизацию в 10 потоков интеграции путем пакетной
// обработки файлов из указанного каталога. Процесс ограничен таймаутом 120 минут.
//
// Параметры:
// Путь - Строка - полный путь к каталогу, содержащему файлы для синхронизации.
//
Процедура СинхронизацияИнтеграцииЗапустить(Путь = Неопределено) Экспорт
Если Не ЗначениеЗаполнено(Путь) Тогда
Путь = Константы.sync_КаталогСинхронизации.Получить();
КонецЕсли;
ТипыОбъектовДООбновить();
Повторять = Истина;
ТаймаутМинут = 120;
ТаймаутМиллисекунд = ТаймаутМинут * 60 * 1000;
ВремяНачала = ТекущаяУниверсальнаяДатаВМиллисекундах();
ВремяТаймаут = ВремяНачала + ТаймаутМиллисекунд;
МетодИмя = "sync_ВходящиеСообщения.ПотокСинхронизацииСоздать";
ПредставлениеЗадания = "Синхронизация связей интеграции с ДО (Sync)";
Пока Повторять Цикл
Если ТекущаяУниверсальнаяДатаВМиллисекундах() > ВремяТаймаут Тогда
ЗаписьЖурналаРегистрации("Синхронизация.МногопоточнаяЗагрузка"
, УровеньЖурналаРегистрации.Предупреждение
,
,
, СтрШаблон("Загрузка прервана. Превышен таймаут синхронизации: %1 минут", ТаймаутМинут));
Прервать;
КонецЕсли;
Файлы = ПачкаФайловПолучить(Путь, 10);
Если НЕ Файлы.Количество() Тогда
Повторять = Ложь;
КонецЕсли;
Задания = Новый Массив;
// Запустим потоки по количеству в пачке
Для Каждого Путь Из Файлы Цикл
Данные = Новый Структура("Путь", Путь);
ПараметрыМетода = Новый Массив;
ПараметрыМетода.Добавить(Данные);
Задания.Добавить(ФоновоеЗапустить(МетодИмя, ПараметрыМетода, , ПредставлениеЗадания));
КонецЦикла;
ТекстОшибки = Ожидать(Задания);
Если ЗначениеЗаполнено(ТекстОшибки) Тогда
ВызватьИсключение ТекстОшибки;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Создает поток синхронизации для обработки файлов по указанному пути.
// Использует обработку sync_ЗагрузкаДанныхСинхронизации для выполнения
// синхронизации данных из внешних файлов.
//
// Параметры:
// Путь - Строка - полный путь к файлу, который содержит
// данные для синхронизации
//
Процедура ПотокСинхронизацииСоздать(Путь) Экспорт
ОбработкаТ = Обработки.sync_ЗагрузкаДанныхСинхронизации.Создать();
ОбработкаТ.ИнтеграцияСинхронизировать(Путь);
КонецПроцедуры
// Выполняет замену устаревших значений измерения ТипОбъектаДО
// в регистре сведений "ОбъектыИнтегрированныеС1СДокументооборотом".
// Заменяемые значения: DMOutgoingDocument, DMIncomingDocument, DMInternalDocument
// Новое значение: DMDocument
//
Процедура ТипыОбъектовДООбновить()
КоллекцияТипов = Новый Массив;
КоллекцияТипов.Добавить("DMOutgoingDocument");
КоллекцияТипов.Добавить("DMIncomingDocument");
КоллекцияТипов.Добавить("DMInternalDocument");
Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 1000
| Т.Объект КАК Объект,
| Т.ТипОбъектаДО КАК ТипОбъектаДО,
| Т.ИдентификаторОбъектаДО КАК ИдентификаторОбъектаДО
|ИЗ
| РегистрСведений.ОбъектыИнтегрированныеС1СДокументооборотом КАК Т
|ГДЕ
| Т.ТипОбъектаДО В(&Типы)");
Запрос.УстановитьПараметр("Типы", КоллекцияТипов);
Замена = "DMDocument";
Обрабатывать = Истина;
Пока Обрабатывать Цикл
Выборка = Запрос.Выполнить().Выбрать();
Обрабатывать = Выборка.Количество() > 0;
НачатьТранзакцию();
Попытка
Пока Выборка.Следующий() Цикл
// Чистим
НаборЗаписей = РегистрыСведений.ОбъектыИнтегрированныеС1СДокументооборотом.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Объект.Установить(Выборка.Объект);
НаборЗаписей.Отбор.ТипОбъектаДО.Установить(Выборка.ТипОбъектаДО);
НаборЗаписей.Отбор.ИдентификаторОбъектаДО.Установить(Выборка.ИдентификаторОбъектаДО);
НаборЗаписей.Записать();
// Добавляем
НаборНовый = РегистрыСведений.ОбъектыИнтегрированныеС1СДокументооборотом.СоздатьНаборЗаписей();
НаборНовый.Отбор.Объект.Установить(Выборка.Объект);
НаборНовый.Отбор.ТипОбъектаДО.Установить(Замена);
НаборНовый.Отбор.ИдентификаторОбъектаДО.Установить(Выборка.ИдентификаторОбъектаДО);
ЗаписьНовая = НаборНовый.Добавить();
ЗаписьНовая.Объект = Выборка.Объект;
ЗаписьНовая.ТипОбъектаДО = Замена;
ЗаписьНовая.ИдентификаторОбъектаДО = Выборка.ИдентификаторОбъектаДО;
НаборНовый.Записать();
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки
КонецЦикла;
КонецПроцедуры
Шаг 2. Подготовка к многопоточной обработке
Проверяется каталог, указанный в константе sync_КаталогСинхронизации. Если файлов нет, задание завершается. Если есть — начинается загрузка.
// Получает список файлов по указанному пути
// и возвращает первые 5 файлов
//
// Параметры:
// ПутьКаталога - Строка - полный путь к каталогу на диске.
// КоличествоФайлов - Число - количество файлов для отбора
// (по умолчанию 5).
//
// Возвращаемое значение:
// Массив - массив строк с полными именами файлов.
Функция ПачкаФайловПолучить(Знач ПутьКаталога, Знач КоличествоФайлов = 5)
Результат = Новый Массив;
Если НЕ ФайлСуществует(ПутьКаталога) Тогда
Возврат Результат;
КонецЕсли;
ВсеФайлы = НайтиФайлы(ПутьКаталога, "*.*", Ложь);
Для Индекс = 0 По Мин(КоличествоФайлов - 1, ВсеФайлы.Количество() - 1) Цикл
Результат.Добавить(ВсеФайлы[Индекс].ПолноеИмя);
КонецЦикла;
Возврат Результат;
КонецФункции // ПачкаФайловПолучить()
Шаг 3. Многопоточная обработка
Если в каталоге присутствуют файлы (те самые порционные ODS-файлы из ДО3), система запускает многопоточную обработку. Принцип простой: каждый файл получает отдельный поток — для этого создается собственный экземпляр обработки sync_ЗагрузкаДанныхСинхронизации. Благодаря параллельной работе мы можем обрабатывать десятки файлов одновременно, максимально используя ресурсы сервера. И что важно, потоки при этом не конфликтуют — у каждого свои уникальные данные. После запуска всех файлов в потоки производится ожидание выполнения заданий.
// ....................................
Задания = Новый Массив;
// Запустим потоки по количеству в пачке
Для Каждого Путь Из Файлы Цикл
Данные = Новый Структура("Путь", Путь);
ПараметрыМетода = Новый Массив;
ПараметрыМетода.Добавить(Данные);
Задания.Добавить(ФоновоеЗапустить(МетодИмя, ПараметрыМетода, , ПредставлениеЗадания));
КонецЦикла;
ТекстОшибки = Ожидать(Задания);
Если ЗначениеЗаполнено(ТекстОшибки) Тогда
ВызватьИсключение ТекстОшибки;
КонецЕсли;
// ....................................
Шаг 3. Экземпляр обработки
Каждый экземпляр sync_ЗагрузкаДанныхСинхронизации работает самостоятельно и выполняет следующие действия:
- Открывает файл и читает данные из ODS-таблицы.
- Ищет в базе ERP объекты по их GUID и типу.
- Раскладывает результаты по двум направлениям:
- Если объект нашелся — записывает связь в регистр «Объекты, интегрированные с 1С:Документооборотом». Именно после этой записи файлы снова появляются в карточках документов ERP. Можно сказать, что здесь происходит «магия» восстановления.
- Если объект не нашелся (например, GUID изменился или документ случайно удалили), запись уходит в специальный регистр «Неидентифицированные объекты».
Зачем нужен регистр «Неидентифицированные объекты»
Это наша «страховочная сетка». В него попадают все проблемные записи с указанием, какой объект не нашелся и почему. Данные не теряются, а просто откладываются для разбора. Позже администратор может зайти, посмотреть на проблемные места и проанализировать. В нашем случае проблем не обнаружилось.
// Создает поток синхронизации для обработки файлов по указанному пути.
// Использует обработку sync_ЗагрузкаДанныхСинхронизации для выполнения
// синхронизации данных из внешних файлов.
//
// Параметры:
// Путь - Строка - полный путь к файлу, который содержит
// данные для синхронизации
//
Процедура ПотокСинхронизацииСоздать(Путь) Экспорт
ОбработкаТ = Обработки.sync_ЗагрузкаДанныхСинхронизации.Создать();
ОбработкаТ.ИнтеграцияСинхронизировать(Путь);
КонецПроцедуры
// Код модуля объекта обработки:
#Область ПрограммныйИнтерфейс
// Выполняет синхронизацию данных интеграции с Документооборотом.
// Читает данные из файла, записывает идентифицированные объекты в регистр
// сведений и удаляет исходный файл при успешной обработке.
//
// Параметры:
// Путь - Строка - полный путь к файлу с данными для синхронизации.
//
Процедура ИнтеграцияСинхронизировать(Путь) Экспорт
ДанныеТаблицаПрочитать(Путь);
ТипДокумент = "DMDocument";
Для Каждого Строка Из ИдентифицированныеДанные Цикл
НаборЗаписей = РегистрыСведений.ОбъектыИнтегрированныеС1СДокументооборотом.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Объект.Установить(Строка.Ссылка);
НаборЗаписей.Отбор.ТипОбъектаДО.Установить(ТипДокумент);
НаборЗаписей.Отбор.ИдентификаторОбъектаДО.Установить(Строка.ИДДО);
Запись = НаборЗаписей.Добавить();
Запись.Объект = Строка.Ссылка;
Запись.ТипОбъектаДО = ТипДокумент;
Запись.ИдентификаторОбъектаДО = Строка.ИДДО;
НаборЗаписей.Записать();
КонецЦикла;
Если Не НеУдалять Тогда
Попытка
// Удаляем
Файл = Новый Файл(Путь);
УдалитьФайлы(Путь);
Исключение
ВызватьИсключение;
КонецПопытки;
КонецЕсли;
КонецПроцедуры // ИнтеграцияСинхронизировать()
#КонецОбласти
#Область ОбработчикиСобытий
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
// Выполняет чтение данных из файла ODS и заполняет таблицы: ДанныеКОбработке, ИдентифицированныеДанные.
//
// Параметры:
// Путь - Строка - полный путь к файлу ODS на сервере.
Процедура ДанныеТаблицаПрочитать(Путь)
ПрочитатьODSСоВторойСтроки(Путь);
СсылкиИдентифицировать();
КонецПроцедуры // ДанныеТаблицаКЛЗаполнитьНаСервере
// Читает файл ODS со 2-й строки, заполняет таблицу ДанныеКОбработке
//
// Параметры:
// ПутьКФайлу - Строка - полный путь к файлу ODS на сервере
//
Процедура ПрочитатьODSСоВторойСтроки(Знач ПутьКФайлу) Экспорт
Попытка
ТабличныйДокумент = Новый ТабличныйДокумент;
ТабличныйДокумент.Прочитать(ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Значение);
КоличествоСтрок = ТабличныйДокумент.ВысотаТаблицы;
Для НомерСтроки = 2 По КоличествоСтрок Цикл
// Получаем значения ячеек
ЗначениеКолонки1 = СокрЛП(ТабличныйДокумент.Область(НомерСтроки, 1).Текст);
ЗначениеКолонки2 = СокрЛП(ТабличныйДокумент.Область(НомерСтроки, 2).Текст);
ЗначениеКолонки3 = СокрЛП(ТабличныйДокумент.Область(НомерСтроки, 3).Текст);
Если ЗначениеКолонки1 = "" И ЗначениеКолонки2 = "" И ЗначениеКолонки3 = ""Тогда
Продолжить;
КонецЕсли;
// Добавляем строку в результат
НоваяСтрока = ДанныеКОбработке.Добавить();
НоваяСтрока.ИДВнешнегоОбъекта = ЗначениеКолонки1;
НоваяСтрока.ТипОбъектаИС = ЗначениеКолонки2;
НоваяСтрока.ИДДО = ЗначениеКолонки3;
КонецЦикла;
Исключение
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры // ПрочитатьODSСоВторойСтроки()
Процедура СсылкиИдентифицировать()
// Обрабатываем каждую строку
Для Каждого Строка Из ДанныеКОбработке Цикл
НоваяСтрока = ИдентифицированныеДанные.Добавить();
НоваяСтрока.ИДВнешнегоОбъекта = Строка.ИДВнешнегоОбъекта;
НоваяСтрока.ТипОбъектаИС = Строка.ТипОбъектаИС;
НоваяСтрока.ИДДО = Строка.ИДДО;
Попытка
// Ищем ссылку по GUID
Ссылка = СсылкуПоГУИДНайти(Строка.ИДВнешнегоОбъекта, Строка.ТипОбъектаИС);
Если Ссылка = Неопределено Тогда
НеидентифицированнаяСсылкаЗаписать(Строка.ИДВнешнегоОбъекта, Строка.ТипОбъектаИС);
НеУдалять = Истина;
Иначе
НоваяСтрока.Ссылка = Ссылка;
КонецЕсли;
Исключение
НеидентифицированнаяСсылкаЗаписать(Строка.ИДВнешнегоОбъекта
, Строка.ТипОбъектаИС
, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
НеУдалять = Истина;
КонецПопытки;
КонецЦикла;
КонецПроцедуры // СсылкиИдентифицировать()
// Выполняет запись информации о неидентифицированной ссылке
// в регистр сведений "НеидентифицированныеОбъекты".
// Используется при обнаружении GUID из внешнего источника,
// для которого не найден соответствующий объект в базе данных.
//
// Параметры:
// ГУИД - Строка - уникальный идентификатор объекта
Процедура НеидентифицированнаяСсылкаЗаписать(Знач ГУИД, Знач ТипОбъектаИС, ТекстОшибки = "")
Запись = РегистрыСведений.sync_НеидентифицированныеОбъекты.СоздатьМенеджерЗаписи();
Запись.ИДВнешнегоОбъекта = ГУИД;
Запись.ТипОбъектаИС = ТипОбъектаИС;
Запись.Комментарий = ТекстОшибки;
Запись.ДатаДобавления = ТекущаяДатаСеанса();
Запись.Записать();
КонецПроцедуры
// Возвращает ссылку по GUID. Поиск во всех справочниках и документах
//
// Параметры:
// GUID - Строка - уникальный идентификатор объекта в формате GUID
// ПолноеИмяМетаданных - Строка - Полное имя метаданных
//
// Возвращаемое значение:
// ЛюбаяСсылка - ссылка на найденный объект или Неопределено, если объект не найден
//
Функция СсылкуПоГУИДНайти(Знач GUID, Знач ПолноеИмяМетаданных) Экспорт
Если ПустаяСтрока(GUID) ИЛИ ПустаяСтрока(ПолноеИмяМетаданных) Тогда
Возврат Неопределено;
КонецЕсли;
Попытка
УИД = Новый УникальныйИдентификатор(GUID);
Исключение
Возврат Неопределено;
КонецПопытки;
Менеджер = ПолучитьМенеджерПоИмени(ПолноеИмяМетаданных); //Возвращает менеджер объекта метаданных по его полному имени
Если Менеджер = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
Попытка
Ссылка = Менеджер.ПолучитьСсылку(УИД);
Возврат ?(ОбщегоНазначения.СсылкаСуществует(Ссылка), Ссылка, Неопределено);
Исключение
Возврат Неопределено;
КонецПопытки;
КонецФункции // СсылкуПоГУИДНайти()
#КонецОбласти
Шаг 4. Уборка и завершение цикла
После того как все объекты идентифицированы и загружены, экземпляр обработки удаляет успешно обработанный файл из каталога. Это одновременно и признак успеха, и защита от повторной обработки. Как только все параллельные потоки завершат свою работу, регламентное задание фиксирует окончание цикла синхронизации.
// .................................
Если Не НеУдалять Тогда
Попытка
// Удаляем
Файл = Новый Файл(Путь);
УдалитьФайлы(Путь);
Исключение
ВызватьИсключение;
КонецПопытки;
КонецЕсли;
// .................................
Итоги этапа загрузки данных в ERP
Завершающий этап стал финальным аккордом в восстановлении отображения файлов. Разработанный механизм загрузки данных в ERP, построенный на многопоточной обработке, выполнил полный цикл синхронизации связей интеграции.
Ключевые результаты работы механизма:
- Автоматическая замена устаревших типов. На старте процесса было выполнено обновление регистра «Объекты, интегрированные с 1С:Документооборотом» — все записи с типами DMInternalDocument, DMIncomingDocument и DMOutgoingDocument заменены на актуальный DMDocument. Это устранило саму причину ошибки открытия данных документооборота.
- Многопоточная обработка порционных файлов. Механизм анализировал каталог синхронизации, отбирал файлы порциями по 10 штук и запускал для каждого файла отдельный поток обработки. Это позволило максимально эффективно использовать ресурсы сервера и обработать все файлы.
- Идентификация объектов и восстановление связей. Для каждой записи из файла выполнялся поиск соответствующего объекта в базе ERP по GUID и типу. Найденные объекты получали новую запись в регистре интеграции, что моментально восстанавливало отображение файлов в карточках документов.
- Контроль полноты обработки. Для неидентфицированных объектов предусмотрен регистр «Неидентифицированные объекты», выполняющий роль страховочной сетки.
- Автоматическая очистка. После успешной обработки файлы удалялись из каталога, что исключало повторную загрузку и служило индикатором выполнения.
На нашем проекте общее время загрузки данных в ERP составило около 20 минут.
Результаты
По итогу восстановлено отображение файлов и возможность работы с данными документооборота в ERP после миграции:
- Устранена ошибка открытия данных документооборота, связанная с устаревшими типами обмена пакета XDTO «DM».
- Полностью восстановлено отображение файлов в карточках документов ERP.
- Создана отказоустойчивая, прозрачная и масштабируемая система, которая при необходимости может быть запущена повторно.
- Общее время обработки — менее 1,5 часов при объеме данных в 1,5 млн объектов.
Проблема, с которой мы столкнулись после миграции, выглядела пугающе: файлы исчезли из карточек ERP, а при попытке открыть данные документооборота возникали ошибки. Причина крылась в архитектурных изменениях ДО3 — устаревший режим хранения «Файлы в отдельных папках» перестал поддерживаться, старая структура связей оказалась несовместима с новой логикой интеграции.
Вместо «быстрых» заплаток, мы построили целостную систему восстановления, которая поэтапно решила проблему комплексно:
Диагностика.
Проанализированы механизмы связи ERP и ДО, выявлена причина ошибок (устаревшие XDTO-типы) и отсутствия файлов (неподдерживаемый режим хранения).
Разработка универсального алгоритма.
Создан алгоритм, покрывающий все возможные сценарии: от простой корректировки связей до полного восстановления «с нуля».
Подготовка инфраструктуры.
Автоматически сгенерированы недостающие правила интеграции для типов объектов, по которым были файлы, но не было настроек.
В нашем проекте таким образом было создано 62 правила, что заняло бы длительное время при ручном создании, в итоге правила сформировались в течении 5 минут.
Обработка данных в ДО3.
Разработанная система в многопоточном режиме (30 потоков) выполнила инвентаризацию файлов, создала недостающие связанные объекты и переназначила владельцев файлов.
На продуктивной системе для 1,5 млн объектов это заняло около 1 часа.
Порционная выгрузка.
Результаты обработаны и упакованы в файлы.
В нашем случае получилось ~150 файлов (порциями по 10 000 записей), общее время выгрузки ~15 минут.
Загрузка и синхронизация в ERP.
Многопоточный механизм (10 параллельных потоков) загрузил данные, обновил устаревшие типы в регистре интеграции и восстановил связи.
Время выполнения ~20 минут.
Для конечных пользователей всё выглядит просто и привычно: они открывают карточку документа, переходят к файлам ДО и видят все прикрепленные файлы. Новая реальность (ДО3) осталась за кулисами, никак не усложняя пользовательский опыт. А для нас это означает, что восстановление прошло успешно, и 1,5 млн объектов больше не разделяют, а связывают две системы.
Друзья, на этом цикл статей ещё не завершен, ожидайте новостей о выходе четвертой части, где мы расскажем про сложности с миграцией прав доступа, а также с практическими данными и результатами продуктивной миграции.
До скорых встреч.
От экспертов «1С-Рарус»
Читайте первыми статьи от экспертов «1С‑Рарус»
Вы можете получать оповещения по электронной почте
Или получайте уведомления в телеграм-боте