В практике работы с «1С:Управление торговлей 10» (на обычных формах) программисты часто сталкиваются с неприятной ситуацией: пользователь открывает документ, работает с ним некоторое время, но при попытке сохранения получает системную ошибку: «Версия объекта изменилась». Это явный признак того, что пока форма была открыта, данные в базе данных были изменены другим сеансом или фоновым процессом. Разберем по шагам, как эффективно провести расследование и найти «виновника» этих изменений.
Прежде чем переходить к сложным техническим инструментам, проверим стандартный механизм. Однако делать это нужно правильно. Рассмотрим, как настроить фильтры, чтобы увидеть скрытые процессы.
ЗК).Проанализируем ситуацию, если Журнал регистрации пуст или не дает ответа. Такое бывает, если запись производится с отключенной регистрацией в журнале (хотя это редкость для типовых механизмов) или если изменения касаются не самого документа, а связанных с ним наборов записей, которые в коде могут вызывать перезапись «владельца».
Как справедливо отмечено в обсуждении, ошибка версии не всегда означает, что документ изменил другой пользователь. Рассмотрим ситуацию, когда сама форма «подкладывает свинью» программисту. Если в модуле формы или в общих модулях, вызываемых из формы (например, в обработчике ожидания или при изменении реквизита), написан код, создающий новый объект на основании текущей ссылки, произойдет конфликт версий.
Выясним причину через поиск по коду. Ищем конструкцию ПолучитьОбъект(). Если мы видим нечто подобное:
ОбъектДляЗаписи = Ссылка.ПолучитьОбъект();
// ... какие-то манипуляции с реквизитами ...
ОбъектДляЗаписи.Записать();
В этом случае в базе данных версия объекта увеличивается. Однако объект ЭтотОбъект, с которым работает открытая форма пользователя, об этом «не знает». В его памяти хранится старый номер версии. При нажатии кнопки «ОК» форма пытается записать свои данные, сравнивает версию в памяти с версией в БД, видит расхождение и блокирует запись.
Если визуальный поиск по коду не помог, создадим диагностическую ловушку. Этот метод считается одним из самых надежных, так как он срабатывает непосредственно в момент записи в базу данных, независимо от того, откуда пришел вызов.
Перейдем в модуль объекта документа ЗК и добавим код в процедуру ПередЗаписью. Нам нужно зафиксировать стек вызовов — цепочку процедур, которая привела к выполнению метода Записать().
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Включаем лог только для конкретного случая или пользователя, чтобы не спамить
Если НЕ ОбменДанными.Загрузка Тогда
ТекстЛога = "Документ " + Ссылка + " записывается. Пользователь: " + ИмяПользователя();
ТекстЛога = ТекстЛога + Символы.ПС + "Стек вызовов:" + Символы.ПС + ПолучитьСтекВызовов();
// Запись в текстовый файл для анализа
ЗаписьТекста = Новый ЗаписьТекста("C:\temp\log_zk.txt", КодировкаТекста.UTF8, , Истина);
ЗаписьТекста.ЗаписатьСтроку(ТекущаяДата() + ": " + ТекстЛога);
ЗаписьТекста.Закрыть();
КонецЕсли;
КонецПроцедуры
Функция ПолучитьСтекВызовов()
// В старых версиях платформы 8.2/8.3 (Обычные формы)
// прямой функции получения стека может не быть.
// Используем хитрость с вызовом исключения:
ТекстСтека = "";
Попытка
ВызватьИсключение "ЛовимСтек";
Исключение
ТекстСтека = ИнформацияОбОшибке().Описание;
КонецПопытки;
Возврат ТекстСтека;
КонецФункции
После внедрения этого кода достаточно дождаться воспроизведения ошибки у пользователя. В файле log_zk.txt вы увидите полную картину: какая подписка на событие, регламентное задание или общий модуль инициировали запись документа.
Если база данных работает в клиент-серверном варианте, мы можем использовать мощный инструмент — Технологический журнал. Это позволит поймать событие записи даже без изменения конфигурации.
Настроим файл logcfg.xml для сбора событий DBV8DBENG (обращения к СУБД) или SDBL (внутренний язык запросов 1С). Разберем пример настройки фильтра:
_Document123).CALL, чтобы видеть стек вызовов на языке 1С.UPDATE в таблице, но и контекст выполнения на стороне сервера 1С.Этот метод хорош тем, что он показывает изменения, вносимые даже внешними системами через COM-интерфейс или Web-сервисы, которые могут «проскакивать» мимо некоторых проверок в коде.
В перепиленных конфигурациях УТ 10 часто встречаются «самописные» механизмы актуализации данных. Посмотрим на наиболее вероятные места:
ДокументОбъект.ЗК на событие ПередЗаписью и ПриЗаписи. Также проверим подписки на регистры, по которым делает движения этот документ. Бывает, что при записи движения по регистру накопления «умная» подписка решает обновить какой-нибудь статус в самом документе.ПодключитьОбработчикОжидания, который раз в энное количество секунд проверяет что-то в базе и обновляет текущий объект.Проанализируем ситуацию комплексно. Если вы обнаружили, что документ меняет регламентное задание, стоит пересмотреть логику: действительно ли нужно перезаписывать весь объект? Часто достаточно изменить состояние в регистре сведений, не трогая сам документ, чтобы не блокировать работу пользователей.
Для эффективного поиска выполним следующие действия:
ПолучитьОбъект().Записать().Используя эти методы, вы быстро определите, кто именно меняет версию объекта, и сможете либо исправить некорректный код, либо договориться с пользователями о регламенте работы с документами.