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