При интеграции 1С с внешними системами на базе MSSQL программисты часто сталкиваются с фундаментальным различием между объектными и необъектными данными. Если для справочников или документов у нас есть уникальный идентификатор (GUID), по которому легко найти, обновить или удалить запись в сторонней базе, то с регистрами накопления ситуация иная. Разберем подробно, как организовать выгрузку данных регистра накопления, например, ВзаиморасчетыСКонтрагентами, и как обеспечить уникальность этих записей во внешней таблице.
Прежде всего, проанализируем структуру данных. В 1С объекты делятся на ссылочные (справочники, документы) и нессылочные (регистры, табличные части). У регистров накопления нет встроенного поля Ссылка. Это означает, что «из коробки» у записи нет единого ГУИДа.
Многие начинающие разработчики пытаются найти способ получить ГУИД для строки регистра, но его не существует в привычном виде. Вместо него используется составной ключ уникальности. Чтобы корректно работать с данными во внешней базе, нам нужно воспроизвести этот ключ в структуре таблицы MSSQL. Рассмотрим подробнее, из чего он состоит на уровне СУБД:
_Period) — дата и время совершения движения._RecorderTRef и _RecorderRRef) — ссылка на документ, который создал запись. В SQL она состоит из идентификатора типа таблицы и самого бинарного кода ссылки._LineNo) — порядковый номер движения в рамках одного документа-регистратора.Только комбинация этих полей гарантирует уникальность каждой конкретной записи в физической таблице регистра.
При создании таблицы в MSSQL для приема данных из 1С, мы должны предусмотреть колонки, которые позволят нам идентифицировать записи при повторной выгрузке. Если мы просто будем делать INSERT, то при перепроведении документа в 1С во внешней базе возникнут дубли.
Рассмотрим пример структуры таблицы:
CREATE TABLE ExternalAccruals (
Period DATETIME,
RecorderGUID UNIQUEIDENTIFIER,
LineNumber INT,
CounterpartyGUID UNIQUEIDENTIFIER,
Amount DECIMAL(15,2),
PRIMARY KEY (Period, RecorderGUID, LineNumber)
);
Здесь RecorderGUID и LineNumber выступают в роли якоря. При выгрузке мы сначала должны удалить старые записи по данному регистратору, а затем записать новые, либо использовать конструкцию MERGE в SQL.
Если вы используете механизм 1С «Внешние источники данных», то система позволяет работать с таблицами SQL почти так же, как с внутренними объектами. Для необъектных данных во внешней базе 1С предоставляет объект МенеджерЗаписи.
Выясним, как это работает на практике. Если в схеме внешнего источника данных для таблицы определен первичный ключ (пусть даже составной), мы можем использовать следующий код для записи:
НоваяЗапись = ВнешниеИсточникиДанных.НашаВнешняяБаза.Таблицы.dbo_ExternalAccruals.СоздатьМенеджерЗаписи();
НоваяЗапись.Period = Движение.Период;
НоваяЗапись.RecorderGUID = Движение.Регистратор.УникальныйИдентификатор();
НоваяЗапись.LineNumber = Движение.НомерСтроки;
НоваяЗапись.Amount = Движение.Сумма;
НоваяЗапись.Записать();
Важный нюанс: Метод Записать() в менеджере записи сначала пытается найти запись по ключевым полям. Если она найдена — обновляет, если нет — создает новую. Это избавляет нас от ручного написания сложных SQL-запросов.
Выгружать весь регистр целиком при каждом изменении — плохая практика, особенно если записей миллионы. Проанализируем ситуацию: документ перепровели, движения изменились. Как об этом узнать внешней базе?
Правильным подходом будет использование Планов обмена. Мы можем создать план обмена и включить в состав его состава наш регистр накопления. Рассмотрим шаги реализации:
ОбменСВнешнейБазой).ВзаиморасчетыСКонтрагентами в состав плана обмена (авторегистрация — «Разрешить»).ВыбратьИзменения().УдалитьРегистрациюИзменений().Это гарантирует, что мы передаем только инкрементальные изменения и не теряем данные при сбоях.
При прямой записи в SQL через ADO или ODBC часто возникает путаница с форматом ГУИДов. В 1С ссылка хранится как Binary(16), а в MSSQL — как Uniqueidentifier. Порядок байтов в 1С отличается от стандартного SQL-представления (байт-сваппинг).
Чтобы избежать проблем при поиске контрагентов или договоров в сторонней базе, мы рекомендуем передавать ГУИД в виде строки или использовать функцию преобразования. Посмотрим на пример получения строкового ГУИДа в 1С:
СтрокаГУИД = Строка(СсылкаНаКонтрагента.УникальныйИдентификатор());
// Результат будет в формате "550e8400-e29b-41d4-a716-446655440000"
На стороне SQL такая строка легко конвертируется в тип UNIQUEIDENTIFIER командой CAST(StringValue AS UNIQUEIDENTIFIER).
Часто для внешних систем не нужны все детальные движения (каждый чих бухгалтера), а нужны актуальные остатки. В этом случае мы можем упростить себе задачу и не следить за LineNo и Recorder.
Рассмотрим вариант выгрузки через Виртуальные таблицы. Вместо физической таблицы регистра мы обращаемся к ОстаткиИОбороты. В этом случае ключом во внешней базе будет набор измерений (Контрагент, Договор, Организация), а ресурсом — СуммаОстаток. Это значительно сокращает объем передаваемых данных и упрощает логику обновления в MSSQL.
При записи во внешний источник данных важно помнить о транзакциях. Если запись в SQL происходит внутри транзакции проведения документа в 1С, то любая ошибка в сети или на стороне SQL-сервера приведет к откату транзакции в 1С. Это хорошо для согласованности данных, но может блокировать работу пользователей при нестабильной связи с внешней базой.
Рассмотрим алгоритм безопасной записи:
Подводя итог, отметим: работа с необъектными данными требует четкого понимания того, что является ключом уникальности. Используйте составные ключи из периода, регистратора и номера строки для детальных движений, или агрегируйте данные через виртуальные таблицы для передачи остатков.