В процессе сопровождения систем 1С часто возникает задача синхронизации данных между различными конфигурациями, например, между 1С:Управление торговлей (УТ) и 1С:Бухгалтерия предприятия (БП). Одной из типичных задач является выравнивание остатков: когда в одной базе (УТ) оперативный учет верный, а в другой (БП) данные разошлись. Для решения этой задачи нам необходимо получить две таблицы значений (ТЗ) с остатками из обеих баз, сравнить их и на основании разницы сформировать документы «Оприходование товаров» или «Списание товаров».
В этой статье мы подробно разберем, как технически реализовать сравнение двух ТаблицаЗначений быстро и эффективно, используя механизмы платформы 1С.
Проанализируем наиболее производительный способ. Вместо того чтобы перебирать таблицы в циклах, мы можем передать их в запрос и выполнить соединение на уровне СУБД (или внутреннего движка запросов 1С). Это позволит нам использовать всю мощь языка запросов, включая агрегатные функции и условия.
Разберем процесс по шагам:
Рассмотрим пример кода для реализации этого метода:
// Предположим, у нас уже есть ТЗ_УТ и ТЗ_БП с колонками "НоменклатураСтр" и "Количество"
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// Передаем таблицы в параметры запроса
Запрос.УстановитьПараметр("ТЗ_УТ", ТЗ_УТ);
Запрос.УстановитьПараметр("ТЗ_БП", ТЗ_БП);
// Помещаем первую таблицу во временную
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.НоменклатураСтр КАК НоменклатураСтр,
| ТЗ.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_УТ
|ИЗ
| &ТЗ_УТ КАК ТЗ;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ТЗ.НоменклатураСтр КАК НоменклатураСтр,
| ТЗ.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_БП
|ИЗ
| &ТЗ_БП КАК ТЗ";
Запрос.Выполнить();
// Основной запрос сравнения
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(ВТ_УТ.НоменклатураСтр, ВТ_БП.НоменклатураСтр) КАК НоменклатураСтр,
| ЕСТЬNULL(ВТ_УТ.Количество, 0) КАК КолУТ,
| ЕСТЬNULL(ВТ_БП.Количество, 0) КАК КолБП,
| ЕСТЬNULL(ВТ_УТ.Количество, 0) - ЕСТЬNULL(ВТ_БП.Количество, 0) КАК Разница
|ИЗ
| ВТ_УТ КАК ВТ_УТ
| ПОЛНОЕ СОЕДИНЕНИЕ ВТ_БП КАК ВТ_БП
| ПО ВТ_УТ.НоменклатураСтр = ВТ_БП.НоменклатураСтр
|ГДЕ
| ЕСТЬNULL(ВТ_УТ.Количество, 0) - ЕСТЬNULL(ВТ_БП.Количество, 0) <> 0";
РезультатСравнения = Запрос.Выполнить().Выгрузить();
В данном примере мы используем ПОЛНОЕ СОЕДИНЕНИЕ, что позволяет нам увидеть позиции, которые есть только в одной из баз. С помощью функции ЕСТЬNULL мы заменяем отсутствующие значения нулями, что позволяет вычислить разницу.
Если по каким-то причинам использование запросов невозможно (например, данные слишком динамичны или вы работаете в специфическом контексте), можно использовать методы объекта ТаблицаЗначений. Чтобы это не работало бесконечно долго на больших объемах, выясним причину медленной работы: поиск в неиндексированной таблице имеет линейную сложность.
Выясним, как ускорить этот процесс. Рассмотрим применение метода Индексы.Добавить(). Создание индекса по ключевому полю (например, «Артикул») превращает поиск из перебора всей таблицы в быстрый поиск по хеш-таблице.
Посмотрим на алгоритм реализации:
// Индексируем таблицу БП для быстрого поиска
ТЗ_БП.Индексы.Добавить("Артикул");
Для Каждого СтрокаУТ Из ТЗ_УТ Цикл
// Ищем соответствующую строку в БП
СтрокаБП = ТЗ_БП.Найти(СтрокаУТ.Артикул, "Артикул");
Если СтрокаБП <> Неопределено Тогда
Разница = СтрокаУТ.Количество - СтрокаБП.Количество;
// Обрабатываем разницу
СтрокаБП.Обработана = Истина; // Помечаем, что строку нашли
Иначе
// Товара нет в БП - это чистое оприходование
Разница = СтрокаУТ.Количество;
КонецЕсли;
КонецЦикла;
// В конце нужно пройтись по ТЗ_БП и найти те строки, которые НЕ были помечены как "Обработана"
// Это товары, которые есть в БП, но их нет в УТ - их нужно списать.
Объект Соответствие (или Кэш) работает крайне быстро. Мы можем загрузить одну из таблиц в Соответствие, где ключом будет уникальный идентификатор или артикул, а значением — количество.
Разберем по шагам этот подход:
Новый Соответствие.Этот метод удобен тем, что Соответствие потребляет меньше памяти, чем полноценная ТаблицаЗначений, и доступ к элементу по ключу происходит мгновенно.
Проанализируем ситуацию: базы разные, а значит, ссылки (объекты типа СправочникСсылка.Номенклатура) у них разные. Вы не сможете сравнить их напрямую. Необходимо выбрать «общий знаменатель»:
СокрЛП() и приведение к регистру ВРег().Часто в 1С:УТ учет ведется по характеристикам, а в 1С:БП — просто по номенклатуре. Перед сравнением таблицу из УТ необходимо «свернуть». Используйте метод ТЗ_УТ.Свернуть("Номенклатура", "Количество"). Это позволит объединить остатки всех характеристик в одну строку, сопоставимую с данными бухгалтерии.
Также проанализируем единицы измерения. Если УТ хранит данные в упаковках, а БП в штуках, при сравнении необходимо умножать количество на коэффициент упаковки, иначе вы получите ложные расхождения.
После того как вы получили таблицу РезультатСравнения с колонкой Разница, алгоритм прост:
Разница > 0: Товар в избытке в источнике (УТ). В базе БП нужно создать документ Оприходование товаров на это количество.Разница < 0: В базе БП товара больше, чем нужно. Создаем документ Списание товаров (используя модуль числа — Abs(Разница)).Такой системный подход позволяет быстро и без ошибок выровнять остатки даже между очень большими базами данных.