Почему возникает ошибка «Данные были изменены или удалены другим пользователем» и как ее исправить?

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

При разработке внешних обработок заполнения табличных частей для управляемых форм 1С разработчики часто сталкиваются с блокирующим окном и ошибкой: «Данные были изменены или удалены другим пользователем». Выясним причину этого поведения и рассмотрим правильные варианты реализации алгоритма заполнения.

Выясним причину ошибки

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

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

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

Вариант 1. Использование стандартной команды БСП «ЗаполнениеФормы» (Рекомендуемый)

Если внешняя обработка подключается через механизм Библиотеки Стандартных Подсистем (БСП), наиболее корректным решением является использование типа команды ЗаполнениеФормы. В этом случае обработка изменяет данные непосредственно в контексте открытой формы без записи объекта в базу данных.

Рассмотрим пример настройки модуля объекта внешней обработки:


Функция СведенияОВнешнейОбработке() Экспорт

    ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке();
    ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиЗаполнениеОбъекта();
    ПараметрыРегистрации.Версия = "1.0";
    
    Команда = ПараметрыРегистрации.Команды.Добавить();
    Команда.Представление = "Заполнить характеристики";
    Команда.Идентификатор = "ЗаполнитьХарактеристики";
    Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыЗаполнениеФормы();
    
    Возврат ПараметрыРегистрации;

КонецФункции

// Процедура вызывается платформой БСП автоматически
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ОбъектыНазначения, ПараметрыВыполнения) Экспорт

    ЭтаФорма = ПараметрыВыполнения.ЭтаФорма;
    
    // Получаем данные формы документа
    ДокументОбъектДанныеФормы = ЭтаФорма.Объект;
    
    // Вызываем серверный алгоритм заполнения
    ЗаполнитьТабличнуюЧастьНаСервере(ДокументОбъектДанныеФормы);
    
    // Уведомляем форму об изменениях
    ЭтаФорма.Модифицированность = Истина;

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

На сервере передаваемый объект модифицируется напрямую как ДанныеФормыСтруктура:


&НаСервере
Процедура ЗаполнитьТабличнуюЧастьНаСервере(ДокументОбъектДанныеФормы)

    Для Каждого СтрТч Из ДокументОбъектДанныеФормы.Товары Цикл
        // Алгоритм поиска и заполнения характеристик
        // Изменения вносятся непосредственно в строки ТЧ формы
        СтрТч.Характеристика = НайденнаяХарактеристика;
    КонецЦикла;

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

Вариант 2. Преобразование данных формы в объект и обратно без записи в БД

Если для заполнения требуется вызов прикладных методов самого объекта (например, процедур из модуля объекта документа), мы можем преобразовать ДанныеФормыСтруктура в ДокументОбъект, выполнить необходимые действия и загрузить данные обратно в форму без вызова метода Записать().

Разберем по шагам данный алгоритм:


&НаСервере
Процедура ЗаполнитьОбъектИзФормы(ДанныеФормы)

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

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

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

Вариант 3. Обновление формы через Прочитать(), если запись в базу неизбежна

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

Посмотрим на пример обновления формы с клиента:


&НаКлиенте
Процедура ПослеЗаписиНаСервере(ФормаДокумента, СсылкаНаДокумент)

    // Способ 1: Оповещаем систему об изменении объекта
    ОповеститьИзменение(СсылкаНаДокумент);
    
    // Способ 2: Перечитываем данные формы из базы данных
    Если ФормаДокумента <> Неопределено Тогда
        ФормаДокумента.Прочитать();
    КонецЕсли;

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

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

← На главную