Работа с платформой 1С:Предприятие 7.7 часто преподносит сюрпризы даже опытным разработчикам, особенно когда речь заходит о тонкостях обновления контекста формы и работы с объектной моделью в момент записи документа. Рассмотрим ситуацию, когда при записи документа мы программно устанавливаем значение реквизита в табличной части (например, партию или серию), но при попытке тут же прочитать свойства этого реквизита через Контекст получаем старые данные. При этом в отладчике все выглядит корректно, а в реальном режиме работы программа «ошибается».
Проанализируем причины такого поведения и разберем по шагам рабочие методы решения этой задачи.
Разберем типичный сценарий. В документе имеется табличная часть, где в одной из колонок выбирается элемент справочника (например, Серия). Перед окончательной записью документа нам необходимо проверить определенное свойство этой серии (реквизит реквизита — Серия.МДЛП). Проблема возникает в обработчике ПриЗаписи(), когда программа обращается к данным через переданный контекст.
Посмотрим на пример функции, которая вызывается для проверки:
Функция глЕстьСерииСМДЛП(Конт) Экспорт
Конт.ВыбратьСтроки();
Пока Конт.ПолучитьСтроку() = 1 Цикл
// Обращение к реквизиту реквизита ТЧ
Если Конт.Серия.МДЛП = 1 Тогда
Возврат 1;
КонецЕсли;
КонецЦикла;
Возврат 0;
КонецФункции
Проблема заключается в том, что если значение в поле Серия было только что изменено программно, обращение Конт.Серия.МДЛП может вернуть состояние, актуальное для предыдущего значения серии. Это происходит из-за особенностей внутреннего кэширования объектов в 1С 7.7.
Многих разработчиков сбивает с толку то, что при остановке в отладчике код начинает работать правильно. Это происходит из-за так называемого «побочного эффекта» инспекции переменных. Когда вы открываете табло или нажимаете Shift+F9 на переменной Конт.Серия, отладчик принудительно обращается к базе данных, чтобы получить актуальное представление объекта. Это действие заставляет платформу обновить внутренний указатель на объект, и последующее выполнение кода проходит корректно.
В обычном же режиме выполнения (без остановок) платформа оптимизирует обращения к связанным объектам. Если объект Серия уже был когда-то считан в контекст данной формы, платформа может использовать закэшированную версию его свойств, не учитывая, что сама ссылка в строке ТЧ уже указывает на другой элемент.
Самый простой, хотя и не всегда очевидный способ синхронизировать данные визуального контекста с внутренней структурой объекта — это использование метода Форма.Обновить(). Вызов этого метода перед выполнением проверок в процедуре ПриЗаписи() заставляет платформу перечитать значения из элементов управления и сбросить кэшированные связи реквизитов.
Пример использования в модуле формы документа:
Процедура ПриЗаписи()
// Принудительно синхронизируем контекст
Форма.Обновить();
// Теперь вызываем проверку
Если глЕстьСерииСМДЛП(Контекст) = 1 Тогда
// Логика обработки
КонецЕсли;
КонецПроцедуры
Важный момент: в 1С 7.7 процедура ПриЗаписи() фактически является событием «Перед записью». Объект еще не зафиксирован в базе данных, и метод ТекущийДокумент() для нового документа вернет пустое значение, поэтому работа через Контекст является единственным способом получить данные, введенные пользователем.
Чтобы полностью исключить влияние механизмов кэширования экранных форм и контекста, профессиональные разработчики рекомендуют работать с данными табличной части через промежуточную структуру — Таблицу Значений. Метод ВыгрузитьТабличнуюЧасть() создает независимую копию данных в памяти, обращение к которой происходит гораздо стабильнее.
Рассмотрим, как изменить подход к проверке:
// В модуле документа
ТЗ_Данных = СоздатьОбъект("ТаблицаЗначений");
ВыгрузитьТабличнуюЧасть(ТЗ_Данных);
// Передаем таблицу в функцию вместо контекста
Если глПроверитьДанныеТЗ(ТЗ_Данных) = 1 Тогда
// Действия
КонецЕсли;
Такой подход гарантирует, что вы работаете с текущими значениями ссылок, которые находятся в объекте «Документ» в данный конкретный момент, минуя особенности визуального обновления полей формы.
Если вам нужно получить реквизит связанного объекта (реквизит реквизита), и вы не хотите полагаться на «точечную» нотацию Конт.Реквизит.Свойство, можно прибегнуть к явному созданию объекта справочника. Это наиболее «чистый» с точки зрения архитектуры 7.7 способ, так как он гарантирует получение данных непосредственно из базы или транзакционного кэша.
Проанализируем модифицированный код функции:
Функция глЕстьСерииСМДЛП_Надежная(Конт) Экспорт
Перем СпрСерии;
СпрСерии = СоздатьОбъект("Справочник.Серии");
Конт.ВыбратьСтроки();
Пока Конт.ПолучитьСтроку() = 1 Цикл
// Вместо прямого обращения через точку
// Находим элемент в отдельном объекте
Если СпрСерии.НайтиЭлемент(Конт.Серия) = 1 Тогда
Если СпрСерии.МДЛП = 1 Тогда
Возврат 1;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат 0;
КонецФункции
Преимущества этого метода:
СпрСерии всегда содержит актуальные данные для переданной ссылки.Подводя итог, можно сказать, что «непонятное» поведение платформы 1С 7.7 при обращении к реквизитам через контекст — это следствие оптимизации работы с памятью и экранными формами. Для решения подобных проблем мы рекомендуем:
Контекст в момент записи может содержать несинхронизированные данные для связанных объектов (реквизитов реквизитов).Форма.Обновить(), если требуется быстрое исправление в существующем коде, работающем через контекст.ВыгрузитьТабличнуюЧасть() для массовой обработки строк ТЧ перед записью, чтобы избежать «глюков» итерирования по строкам через контекст.СоздатьОбъект) и метод НайтиЭлемент.Соблюдение этих правил позволит создавать более надежные конфигурации на платформе 7.7 и избегать трудноуловимых ошибок, которые проявляются только в реальной работе пользователей.