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