Как исправить ошибку при обмене данными между 1С и мобильным приложением и настроить выгрузку на новые устройства?

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

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

Анализ исходной проблемы: почему возникает ошибка записи?

Рассмотрим ситуацию, когда при обмене между 1С:Розница и мобильным клиентом процедура внезапно прерывается на этапе формирования ответного сообщения. Проанализируем исходный программный код, который вызвал затруднение:


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

    ЗаписьХМЛ = Новый ЗаписьXML;
    ЗаписьХМЛ.УстановитьСтроку();
    ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
    
    // ОШИБКА ПРОИСХОДИТ ЗДЕСЬ:
    ЗаписьСообщения.НачатьЗапись(ЗаписьХМЛ, Отправитель); 
    
    ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(Отправитель, ЗаписьСообщения.НомерСообщения);
    // ... дальнейший код выгрузки
КонецФункции

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

Решение: необходимо явно вызвать метод ЧтениеСообщения.ЗакончитьЧтение() сразу после завершения цикла обработки входящих данных. Это освободит объект и позволит платформе переключиться на формирование исходящего пакета.

Исправленный алгоритм обмена данными

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


Функция ЗарегистрироватьВыгрузку(СтрокаСообщения)
    // 1. Инициализация чтения
    ЧтениеХМЛ = Новый ЧтениеXML;
    ЧтениеХМЛ.УстановитьСтроку(СтрокаСообщения);
    ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
    ЧтениеСообщения.НачатьЧтение(ЧтениеХМЛ);
    Отправитель = ЧтениеСообщения.Отправитель;
    
    // 2. Обработка входящих данных в транзакции
    НачатьТранзакцию();
    Попытка
        ПланыОбмена.УдалитьРегистрациюИзменений(Отправитель, ЧтениеСообщения.НомерПринятого);
        
        Пока ВозможностьЧтенияXML(ЧтениеХМЛ) Цикл
            Данные = ПрочитатьXML(ЧтениеХМЛ);
            Если ТипЗнч(Данные) = Тип("УдалениеОбъекта") Тогда
                // Обработка удаления (важно для мобильных приложений!)
                Попытка
                    НайтиОбъект = Данные.Ссылка.ПолучитьОбъект();
                    Если НайтиОбъект <> Неопределено Тогда
                        НайтиОбъект.Удалить();
                    КонецЕсли;
                Исключение
                    // Обработка ошибки удаления
                КонецПопытки;
            ИначеЕсли Данные <> Неопределено Тогда
                Данные.ОбменДанными.Отправитель = Отправитель;
                Данные.ОбменДанными.Загрузка = Истина;
                Данные.Записать();
            КонецЕсли;
        КонецЦикла;
        
        // ОБЯЗАТЕЛЬНЫЙ ШАГ:
        ЧтениеСообщения.ЗакончитьЧтение();
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ЧтениеСообщения.ЗакончитьЧтение(); // Закрываем даже при ошибке
        ВызватьИсключение КраткоеОписаниеОшибки(ИнформацияОбОшибке());
    КонецПопытки;

    // 3. Формирование ответа
    ЗаписьХМЛ = Новый ЗаписьXML;
    ЗаписьХМЛ.УстановитьСтроку();
    ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
    
    ЗаписьСообщения.НачатьЗапись(ЗаписьХМЛ, Отправитель);
    
    ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(Отправитель, ЗаписьСообщения.НомерСообщения);
    Пока ВыборкаИзменений.Следующий() Цикл
        ОбъектОбмена = ВыборкаИзменений.Получить();
        ЗаписатьXML(ЗаписьХМЛ, ОбъектОбмена);
    КонецЦикла;
    
    ЗаписьСообщения.ЗакончитьЗапись();
    Возврат ЗаписьХМЛ.Закрыть();
КонецФункции

Как передать существующие данные на новое устройство?

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

Проанализируем способы решения этой задачи:

  1. Программная регистрация всех данных: Это самый надежный способ. При создании нового узла в коде необходимо вызвать метод ЗарегистрироватьИзменения().

    Если нужно отправить вообще все данные, которые входят в план обмена:

    ПланыОбмена.ЗарегистрироватьИзменения(УзелСсылка);

    Если нужно отправить только определенные справочники (например, только номенклатуру):

    ПланыОбмена.ЗарегистрироватьИзменения(УзелСсылка, Метаданные.Справочники.Номенклатура);
  2. Интерактивная регистрация: Если вы используете типовые или доработанные конфигурации, можно воспользоваться обработкой "Регистрация изменений для обмена данными". Там можно выбрать нужный узел и нажать кнопку "Зарегистрировать все объекты".

После выполнения регистрации при следующем сеансе связи метод ПланыОбмена.ВыбратьИзменения получит список всех объектов, и они будут упакованы в XML-сообщение для мобильного приложения.

Оптимизация обмена для мобильных платформ

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

1. Использование транзакций. Как мы уже отметили в коде выше, запись каждого объекта по отдельности крайне медленна. Обернув цикл чтения в НачатьТранзакцию() / ЗафиксироватьТранзакцию(), мы ускоряем процесс записи в десятки раз, так как индексы базы данных обновляются один раз в конце, а не после каждой строки кода Данные.Записать().

2. Настройка сжатия данных. В исходном коде использовался Новый СжатиеДанных(9). Уровень 9 — это максимальное сжатие, требующее больших вычислительных мощностей процессора. Для мобильных телефонов это может стать "бутылочным горлышком". Рекомендуется использовать уровень 1 или 6. Разница в размере файла будет минимальной (всего несколько процентов), но скорость упаковки и распаковки данных вырастет существенно.

3. Проблема объема оперативной памяти. Метод ЗаписьХМЛ.УстановитьСтроку() формирует XML-текст прямо в оперативной памяти. Если данных много (тысячи товаров с картинками), мобильное приложение или сервер 1С может закрыться с ошибкой "Out of memory". Выясним, как этого избежать: вместо формирования строки записывайте данные во временный файл.


ИмяФайла = ПолучитьИмяВременногоФайла("xml");
ЗаписьХМЛ.ОткрытьФайл(ИмяФайла);
// ... процесс записи ...
ЗаписьХМЛ.Закрыть();
// Затем передаем файл через веб-сервис или хранилище

Выводы

Подведем итог нашему исследованию. Для корректной работы обмена данными в 1С:Розница с мобильным клиентом необходимо:

Соблюдение этих правил позволит создать отказоустойчивую систему синхронизации, которая будет корректно работать как с одним, так и с сотнями подключенных мобильных устройств.

← На главную