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

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

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

Метод 1. Сравнение с исходным состоянием через скрытую колонку

Это самый наглядный и часто используемый метод в управляемых формах. Суть заключается в том, чтобы хранить «слепок» данных в момент их загрузки в обработку. Проанализируем ситуацию: когда мы заполняем таблицу на форме данными из регистра, мы создаем для каждой строки её копию.

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

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

Рассмотрим пример кода для упаковки данных в JSON при загрузке:


// При загрузке данных в таблицу формы
Для Каждого СтрокаТЗ Из Объект.ТаблицаДанных Цикл
    СтруктураСостояния = Новый Структура;
    СтруктураСостояния.Вставить("Колонка1", СтрокаТЗ.Колонка1);
    СтруктураСостояния.Вставить("Колонка2", СтрокаТЗ.Колонка2);
    СтруктураСостояния.Вставить("Колонка3", СтрокаТЗ.Колонка3);
    
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, СтруктураСостояния);
    СтрокаТЗ.ДанныеОригинал = ЗаписьJSON.Закрыть();
КонецЦикла;

Теперь выясним, как проверить изменения перед записью в регистр:


Для Каждого СтрокаТЗ Из Объект.ТаблицаДанных Цикл
    СтруктураТекущая = Новый Структура("Колонка1, Колонка2, Колонка3", 
        СтрокаТЗ.Колонка1, СтрокаТЗ.Колонка2, СтрокаТЗ.Колонка3);
    
    // Читаем оригинал
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(СтрокаТЗ.ДанныеОригинал);
    СтруктураОригинал = ПрочитатьJSON(ЧтениеJSON);
    
    Если Не ДанныеРавны(СтруктураТекущая, СтруктураОригинал) Тогда
        // Запись изменена — выполняем запись в регистр и отправку в MySQL
        ЗаписатьВРегистрСведений(СтрокаТЗ);
        ОтправитьВMySQL(СтрокаТЗ);
    КонецЕсли;
КонецЦикла;

Метод 2. Использование механизмов платформы: Планы обмена

Проанализируем более профессиональный подход. Если ваша задача — синхронизация с внешней базой, то «правильнее» всего использовать штатный механизм регистрации изменений. Для этого независимый регистр сведений включается в состав ПланаОбмена.

Посмотрим, какие преимущества это дает: платформе безразлично, как именно была изменена запись (через вашу обработку, вручную в интерфейсе или другим программным кодом). Как только происходит вызов НаборЗаписей.Записать(), платформа фиксирует изменения для узла обмена.

Алгоритм работы в этом случае будет следующим:

  1. В конфигураторе создаем новый ПланОбмена, назовем его СинхронизацияСMySQL.
  2. В составе плана обмена отмечаем наш регистр сведений и устанавливаем Авторегистрация в значение Разрешить.
  3. В обработке просто записываем данные.
  4. Отдельная процедура (или фоновое задание) выбирает зарегистрированные изменения по этому узлу и отправляет их во внешнюю базу.

Пример получения измененных данных через план обмена:


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

Метод 3. Хеширование данных для быстрой проверки

Если в регистре очень много колонок (ресурсов и реквизитов), попарное сравнение в цикле может работать медленно. Разберем метод контрольных сумм. При загрузке данных мы вычисляем MD5-хеш или простую контрольную сумму по строке всех значений.

Посмотрим на пример функции вычисления хеша:


Функция ПолучитьХешСтроки(СтрокаДанных)
    ТекстДляХеша = Строка(СтрокаДанных.Колонка1) + "|" + 
                   Строка(СтрокаДанных.Колонка2) + "|" + 
                   Строка(СтрокаДанных.Колонка3);
    
    Хеширование = Новый ХешированиеДанных(ХешФункция.MD5);
    Хеширование.Добавить(ТекстДляХеша);
    Возврат Хеширование.ХешСумма;
КонецФункции

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

Метод 4. Сравнение через временные таблицы в запросе

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

  1. Передаем таблицу измененных данных из обработки на сервер.
  2. Помещаем эту таблицу во ВременнуюТаблицу в запросе.
  3. Делаем ЛЕВОЕ СОЕДИНЕНИЕ этой таблицы с реальной таблицей регистра сведений по измерениям.
  4. В секции ГДЕ прописываем условие: ГДЕ Таблица.Ресурс1 <> Регистр.Ресурс1 ИЛИ Таблица.Ресурс2 <> Регистр.Ресурс2.

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

Метод 5. Использование свойства ДополнительныеСвойства

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

Пример передачи флага из обработки:


Набор = РегистрыСведений.НашРегистр.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(ВыбраннаяНоменклатура);
// ... заполнение набора ...

// Устанавливаем флаг источника
Набор.ДополнительныеСвойства.Вставить("ЭтоОбменMySQL", Истина);
Набор.Записать();

В модуле набора записей регистра:


Процедура ПередЗаписью(Отказ, Замещение)
    Если ДополнительныеСвойства.Свойство("ЭтоОбменMySQL") Тогда
        // Мы точно знаем, что запись инициирована нашей обработкой
        // Здесь можно реализовать дополнительную логику логирования
    КонецЕсли;
КонецПроцедуры

Подведем итоги: для решения задачи проверки изменений наиболее гибким является Метод 1 (скрытая колонка), так как он работает полностью в контексте формы. Если же вы строите масштабную систему синхронизации, рекомендуем обратить внимание на Метод 2 (планы обмена), который является стандартом платформы 1С:Предприятие 8.3 для отслеживания изменений данных.

← На главную