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

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

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

Разделение контекстов: модуль менеджера и модуль объекта

Выясним причину, по которой возникает ошибка «Метод объекта не обнаружен». В платформе 1С строго разделены контексты работы со ссылками, менеджерами и экземплярами объектов:

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

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

Ошибочный вызов:


// Ошибка: "Метод объекта не обнаружен", так как метод объявлен в модуле объекта,
// а вызывается через менеджер документов
Документы.РеализацияТоваровУслуг.ОбновитьТабличнуюЧасть(СсылкаНаДокумент);

Корректный вызов метода объекта:


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

Можно ли изменить табличную часть без получения объекта?

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

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

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


ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();

НоваяСтрока = ДокументОбъект.ДополнительныеРеквизиты.Добавить();
НоваяСтрока.Свойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Согласовано");
НоваяСтрока.Значение = Истина;

ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);

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

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

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

Способы устранения конфликтов версий и блокировок

Рассмотрим основные подходы для предотвращения и обработки ошибок параллельной записи.

1. Использование механизма повторных попыток (Retry)

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


КоличествоПопыток = 3;
УспешноЗаписано = Ложь;

Для Счетчик = 1 По КоличествоПопыток Цикл
    Попытка
        ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
        
        НоваяСтрока = ДокументОбъект.ДополнительныеРеквизиты.Добавить();
        НоваяСтрока.Свойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Статус");
        НоваяСтрока.Значение = "Обработан";
        
        ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
        УспешноЗаписано = Истина;
        Прервать;
    Исключение
        Если Счетчик = КоличествоПопыток Тогда
            ВызватьИсключение "Не удалось записать документ из-за конфликта блокировок: " + ОписаниеОшибки();
        КонецЕсли;
    КонецПопытки;
КонецЦикла;

2. Управляемые блокировки в транзакции

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


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

3. Архитектурная альтернатива: использование регистров сведений

Если перед нами стоит задача частого обновления дополнительных параметров или статусов документа независимыми процессами, хранить эти данные в табличной части документа не рекомендуется. Каждая запись документа полностью блокирует его, требует пересчета итогов и может приводить к конфликтам блокировок. В таких случаях данные выносят в независимый регистр сведений, где измерением выступает ссылка на документ. Запись в регистр выполняется быстро и не требует перезаписи самого документа:


МенеджерЗаписи = РегистрыСведений.ДополнительныеСведения.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Объект = СсылкаНаДокумент;
МенеджерЗаписи.Свойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("СтатусОбработки");
МенеджерЗаписи.Значение = "Выполнено";
МенеджерЗаписи.Записать(Истина);
← На главную