Как сравнить наборы записей регистра бухгалтерии между двумя базами 1С

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

При организации обмена данными между информационными базами 1С нередко возникает необходимость проверить корректность переноса проводок. Задача усложняется тем, что документы могут перепроводиться или изменяться в базе-источнике, а итоговые данные бухгалтерского учета должны сходиться «в ноль». При этом требуется выполнить детальную сверку всех реквизитов, ресурсов и аналитики (включая субконто, валютные суммы и количество) и получить быстрый результат — признак соответствия (Истина/Ложь) по каждому документу-регистратору.

Разберем основные технические нюансы таких сверок и рассмотрим наиболее эффективные способы решения задачи.

Важные нюансы при сверке регистров бухгалтерии

Прежде чем приступать к программному сравнению наборов записей, проанализируем факторы, которые могут приводить к ложным расхождениям:

  1. Флаг активности записей: в регистре могут присутствовать неактивные строки (Активность = Ложь), например, при ручном отключении проводок или сторнировании. При выборке данных важно явно фильтровать только активные записи.
  2. Порядок следования субконто: субконто в таблицах регистра бухгалтерии могут храниться в полях Субконто1, Субконто2, Субконто3. Если в планах счетов двух баз порядок видов субконто на счете не совпадает, одинаковая аналитика попадет в разные колонки.
  3. Порядок строк (НомерСтроки): проводки могут быть идентичны по дебету, кредиту, суммам и аналитике, но иметь разные номера строк из-за перепроведения. При детальном сравнении поле НомерСтроки обычно исключают из сопоставления.
  4. Идентификаторы объектов (UUID): прямое сравнение значений ссылочных типов (справочники, документы) корректно работает только в том случае, когда уникальные идентификаторы объектов в обеих базах полностью совпадают.

Способ 1. Сверка через вычисление хеш-суммы (MD5 / SHA256)

Если требуется получить однозначный результат «Истина/Ложь» по каждому регистратору с минимальной нагрузкой на каналы связи, наиболее быстрым вариантом является расчет контрольной суммы проводок документа. Рассмотрим этот алгоритм.

Для каждого документа в обеих базах формируется каноническая строка из упорядоченных полей проводок, после чего рассчитывается хеш через объект платформы ХешированиеДанных.


Функция ПолучитьХешПроводокДокумента(СсылкаНаДокумент) Экспорт

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Хозрасчетный.СчетДт.Код КАК СчетДт,
        |   Хозрасчетный.СчетКт.Код КАК СчетКт,
        |   Хозрасчетный.СубконтоДт1,
        |   Хозрасчетный.СубконтоДт2,
        |   Хозрасчетный.СубконтоДт3,
        |   Хозрасчетный.СубконтоКт1,
        |   Хозрасчетный.СубконтоКт2,
        |   Хозрасчетный.СубконтоКт3,
        |   Хозрасчетный.Сумма КАК Сумма,
        |   Хозрасчетный.КоличествоДт КАК КоличествоДт,
        |   Хозрасчетный.КоличествоКт КАК КоличествоКт,
        |   Хозрасчетный.ВалютнаяСуммаДт КАК ВалютнаяСуммаДт,
        |   Хозрасчетный.ВалютнаяСуммаКт КАК ВалютнаяСуммаКт
        |ИЗ
        |   РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
        |ГДЕ
        |   Хозрасчетный.Регистратор = &Регистратор
        |   И Хозрасчетный.Активность = ИСТИНА
        |УПОРЯДОЧИТЬ ПО
        |   СчетДт,
        |   СчетКт,
        |   Сумма";

    Запрос.УстановитьПараметр("Регистратор", СсылкаНаДокумент);
    Выборка = Запрос.Выполнить().Выбрать();

    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.УстановитьСтроку();
    ЗаписьXML.ЗаписатьНачалоЭлемента("Movements");

    Пока Выборка.Следующий() Цикл
        ЗаписьXML.ЗаписатьНачалоЭлемента("Row");
        ЗаписьXML.ЗаписатьАтрибут("AccountDr", XMLСтрока(Выборка.СчетДт));
        ЗаписьXML.ЗаписатьАтрибут("AccountCr", XMLСтрока(Выборка.СчетКт));
        ЗаписьXML.ЗаписатьАтрибут("Amount", XMLСтрока(Выборка.Сумма));
        ЗаписьXML.ЗаписатьАтрибут("QtyDr", XMLСтрока(Выборка.КоличествоДт));
        ЗаписьXML.ЗаписатьАтрибут("QtyCr", XMLСтрока(Выборка.КоличествоКт));
        ЗаписьXML.ЗаписатьАтрибут("CurAmountDr", XMLСтрока(Выборка.ВалютнаяСуммаДт));
        ЗаписьXML.ЗаписатьАтрибут("CurAmountCr", XMLСтрока(Выборка.ВалютнаяСуммаКт));
        ЗаписьXML.ЗаписатьКонецЭлемента();
    КонецЦикла;

    ЗаписьXML.ЗаписатьКонецЭлемента();
    СтрокаXML = ЗаписьXML.Закрыть();

    Хеширование = Новый ХешированиеДанных(ХешФункция.MD5);
    Хеширование.Добавить(СтрокаXML);

    Возврат СтрЗаменить(Строка(Хеширование.ХешСумма), " ", "");

КонецФункции

Сравнивая полученные хеш-строки для одного и того же регистратора из базы-источника и базы-приемника, мы моментально получаем статус идентичности без передачи полных таблиц движений.

Способ 2. Свертка разнознаковых сумм в таблице значений

Посмотрим на классический аналитический подход, позволяющий не только зафиксировать факт расхождения, но и наглядно увидеть, в каких именно ресурсах и проводках возникла разница.

Разберем логику по шагам:

  1. Выгружаем движения регистратора из первой базы в объект ТаблицаЗначений. Числовые ресурсы (сумма, количество, валютная сумма) оставляем со знаком «плюс».
  2. Выгружаем движения регистратора из второй базы и добавляем их в ту же таблицу, но числовые ресурсы умножаем на -1.
  3. Сворачиваем полученную таблицу методом Свернуть(), указав в качестве группировочных колонок счета и аналитику, а в качестве суммируемых — ресурсы.
  4. Удаляем строки, где все числовые ресурсы равны нулю. Оставшиеся строки указывают на конкретные расхождения в проводках.

// Пример свертки объединенной таблицы движений
КолонкиГруппировки = "СчетДт,СчетКт,СубконтоДт1,СубконтоДт2,СубконтоДт3,СубконтоКт1,СубконтоКт2,СубконтоКт3";
КолонкиСуммирования = "Сумма,КоличествоДт,КоличествоКт,ВалютнаяСуммаДт,ВалютнаяСуммаКт";

ТаблицаСверки.Свернуть(КолонкиГруппировки, КолонкиСуммирования);

// Поиск расхождений
Индекс = ТаблицаСверки.Количество() - 1;
Пока Индекс >= 0 Цикл
    СтрокаТЗ = ТаблицаСверки[Индекс];
    Если СтрокаТЗ.Сумма = 0 
        И СтрокаТЗ.КоличествоДт = 0 
        И СтрокаТЗ.КоличествоКт = 0 
        И СтрокаТЗ.ВалютнаяСуммаДт = 0 
        И СтрокаТЗ.ВалютнаяСуммаКт = 0 Тогда
        ТаблицаСверки.Удалить(Индекс);
    КонецЕсли;
    Индекс = Индекс - 1;
КонецЦикла;

// Если ТаблицаСверки.Количество() = 0 — наборы записей идентичны

Способ 3. Запрос с полным соединением временных таблиц

Если обе таблицы движений передаются в один контекст выполнения запроса (например, через менеджер временных таблиц), наиболее строгим методом является ПОЛНОЕ СОЕДИНЕНИЕ.


ВЫБРАТЬ
    ЕСТЬNULL(Таб1.Регистратор, Таб2.Регистратор) КАК Регистратор,
    ЕСТЬNULL(Таб1.Сумма, 0) - ЕСТЬNULL(Таб2.Сумма, 0) КАК РазницаСуммы
ИЗ
    вт_ДвиженияБаза1 КАК Таб1
    ПОЛНОЕ СОЕДИНЕНИЕ вт_ДвиженияБаза2 КАК Таб2
        ПО Таб1.Регистратор = Таб2.Регистратор
        И Таб1.СчетДт = Таб2.СчетДт
        И Таб1.СчетКт = Таб2.СчетКт
        И Таб1.СубконтоДт1 = Таб2.СубконтоДт1
        И Таб1.СубконтоКт1 = Таб2.СубконтоКт1
ГДЕ
    (Таб1.Регистратор ЕСТЬ NULL 
    ИЛИ Таб2.Регистратор ЕСТЬ NULL 
    ИЛИ ЕСТЬNULL(Таб1.Сумма, 0) <> ЕСТЬNULL(Таб2.Сумма, 0))

Выясним преимущества такого подхода: запрос возвращает только строки с несоответствиями, позволяя точно определить, в какой именно базе проводка отсутствует или имеет иную сумму.

Рекомендации по организации межпрограммного взаимодействия

Для передачи данных между базами можно использовать следующие механизмы:

  1. HTTP-сервисы (REST API): наиболее надежный и кроссплатформенный вариант. База-приемник передает список идентификаторов документов через POST-запрос, а база-источник возвращает рассчитанные хеш-суммы или структуру проводок в формате JSON.
  2. COM-соединение (COMConnector): подходит для сред под управлением Windows, однако чувствительно к разрядности платформы (32/64 бит) и требует регистрации библиотеки comcntr.dll.

Использование функции ЗначениеВСтрокуВнутр для сравнения объектов между разными базами применять не рекомендуется, так как внутреннее представление зависит от релиза платформы и системных идентификаторов метаданных конфигурации.

← На главную