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