Как выгрузить остатки на сайт по позициям, удаленным при перепроведении документа

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

В процессе интеграции 1С с интернет-магазином нередко применяется оперативная выгрузка остатков в момент проведения документов движения товаров (например, реализаций или заказов покупателей). Однако при таком подходе возникает типовая проблема: если пользователь открывает ранее проведенный документ и заменяет одну позицию на другую (или просто удаляет строку), платформа выгружает актуальные остатки только по товарам, присутствующим в текущей табличной части. Позиция, которую удалили из документа, физически возвращается на остаток склада в базе 1С, но внешняя система об этом не узнает, так как товар отсутствует в текущем объекте и не попадает в алгоритм выгрузки.

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

Анализ проблемы: почему теряются удаленные строки

Проанализируем механику работы события ОбработкаПроведения. В этот обработчик передается ссылка или объект документа, содержащий уже обновленный состав табличной части. Данные в информационной базе еще не зафиксированы окончательно, но в оперативной памяти объекта удаленной строки уже нет.

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

Решение 1: Передача старого состава ТЧ через ДополнительныеСвойства

Наиболее простое и быстрое решение, не требующее изменения модулей самих документов, строится на использовании двух подписок на события и встроенной коллекции ДополнительныеСвойства. Это свойство представляет собой структуру, которая существует в контексте экземпляра объекта в памяти на протяжении всего жизненного цикла записи и проведения.

Разберем по шагам реализацию этого механизма:

  1. Создаем подписку на событие ПередЗаписью для требуемых документов. В ней мы считываем из базы данных исходное состояние строк документа по его ссылке и помещаем список номенклатуры в ДополнительныеСвойства.
  2. Создаем вторую подписку на событие ОбработкаПроведения (или используем существующую). В ней мы получаем текущие строки, объединяем их со сохраненным списком из ДополнительныеСвойства, удаляем дубли и рассчитываем остатки по полному перечню товаров.

Посмотрим на пример реализации обработчика в подписке на событие ПередЗаписью:


Процедура ПередЗаписьюДокументаДляВыгрузки(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт

    // Проверяем, что документ уже существует в базе (не новый) и проводится
    Если РежимЗаписи <> РежимЗаписиДокумента.Проведение Или Источник.ЭтоНовый() Тогда
        Возврат;
    КонецЕсли;

    // Считываем состав табличной части из базы данных по текущей ссылке
    Запрос = Новый Запрос;
    Запрос.Текст =
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |   Т.Номенклатура КАК Номенклатура
        |ИЗ
        |   Документ.РеализацияТоваровУслуг.Товары КАК Т
        |ГДЕ
        |   Т.Ссылка = &Ссылка";

    Запрос.УстановитьПараметр("Ссылка", Источник.Ссылка);
    ТаблицаСтарыхПозиций = Запрос.Выполнить().Выгрузить();

    // Сохраняем массив или список номенклатуры в дополнительных свойствах объекта
    МассивСтарыхПозиций = ТаблицаСтарыхПозиций.ВыгрузитьКолонку("Номенклатура");
    Источник.ДополнительныеСвойства.Вставить("ПредыдущийСоставНоменклатуры", МассивСтарыхПозиций);

КонецПроцедуры

Теперь посмотрим, как обработать сохраненные данные в событии ОбработкаПроведения:


Процедура ОбработкаПроведенияВыгрузкаОстатков(Источник, Отказ, РежимПроведения) Экспорт

    Если Отказ Тогда
        Возврат;
    КонецЕсли;

    // Формируем результирующий список номенклатуры
    СписокДляВыгрузки = Новый Соответствие;

    // 1. Добавляем текущие строки из документа
    Для Каждого Стр Из Источник.Товары Цикл
        Если ЗначениеЗаполнено(Стр.Номенклатура) Тогда
            СписокДляВыгрузки.Вставить(Стр.Номенклатура, Истина);
        КонецЕсли;
    КонецЦикла;

    // 2. Добавляем старые строки, сохраненные перед записью
    Если Источник.ДополнительныеСвойства.Свойство("ПредыдущийСоставНоменклатуры") Тогда
        Для Каждого Позиция Из Источник.ДополнительныеСвойства.ПредыдущийСоставНоменклатуры Цикл
            СписокДляВыгрузки.Вставить(Позиция, Истина);
        КонецЦикла;
    КонецЕсли;

    // Получаем результирующий массив уникальных позиций
    МассивНоменклатуры = Новый Массив;
    Для Каждого КлючИЗначение Из СписокДляВыгрузки Цикл
        МассивНоменклатуры.Добавить(КлючИЗначение.Ключ);
    КонецЦикла;

    // Вызываем алгоритм актуализации остатков
    АктуализироватьОстаткиПоСписку(МассивНоменклатуры);

КонецПроцедуры

Подводные камни и риски прямой выгрузки из проведения

Рассмотренный выше вариант решает локальную задачу редактирования строк, однако при эксплуатации боевой системы важно учитывать критические риски выгрузки непосредственно в момент проведения:

  1. Блокировки СУБД и сетевые задержки: Обработка проведения выполняется внутри неделимой транзакции базы данных. Если отправка на сайт выполняется через HTTP/веб-сервисы, любая сетевая задержка, таймаут или зависание внешнего сервера приведут к блокировке таблиц базы данных для всех остальных пользователей.
  2. Рассинхронизация при ошибках: Если HTTP-запрос к сайту завершился успешно, а затем внутри транзакции 1С произошла ошибка (например, контроль отрицательных остатков другой подписки), транзакция базы данных откатится, а на сайте останутся новые некорректные остатки.
  3. Пропуск отмены проведения: Если документ отменяют с проведения или помечают на удаление, событие ОбработкаПроведения не вызывается вовсе. Товар вернется на склад, но сайт об этом не узнает.
  4. Массовое перепроведение: При групповой обработке или восстановлении последовательности система начнет непрерывно слать одиночные запросы на сайт, вызывая перегрузку внешнего сервера.

Решение 2: Отслеживание движений на уровне регистра накопления

Более универсальный и отказоустойчивый способ — отслеживать не строки табличных частей разных документов, а фактические изменения в регистрах остатков (например, через подписку на событие ПередЗаписью объекта РегистрНакопленияНаборЗаписей).

Когда документ перепроводится или снимается с проведения, платформа очищает старые движения и записывает новые. В процедуре ПередЗаписью набора записей мы можем прочитать запросом текущие движения из базы данных по регистратору, сравнить их с новым набором (ЭтотОбъект) и выявить разницу. Преимущество подхода: алгоритм работает единообразно для любых документов (поступления, реализации, перемещения, инвентаризации) и автоматически фиксирует отмену проведения.

Решение 3: Асинхронная очередь и регламентные задания

Чтобы полностью исключить риски блокировок СУБД и падений транзакций, выгрузку на сайт разделяют на два этапа:

  1. Регистрация изменений (в транзакции): При записи движений или проведении документов измененные позиции номенклатуры только фиксируются в буферной таблице (специальный регистр сведений) либо регистрируются на узле штатного объекта ПланыОбмена через метод ПланыОбмена.ЗарегистрироватьИзменения(). Запись в локальную таблицу 1С выполняется за доли миллисекунды и не зависит от доступности интернета.
  2. Фоновая выгрузка (вне транзакции): Регламентное задание по расписанию (раз в 1–3 минуты) считывает накопившийся список измененных товаров, одним запросом вычисляет текущие итоговые остатки и отправляет пакет данных на сайт одним сетевым запросом. После успешного ответа сервера очередь очищается.

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

← На главную