В процессе разработки и поддержки конфигураций на платформе 1С:Предприятие 7.7 программисты часто сталкиваются с неочевидным поведением системы при попытке изменить или распровести документ. Классическая ситуация: в коде создается объект, ему присваивается ссылка на документ, но при вызове методов модификации (например, СделатьНеПроведенным() или Записать()) платформа возвращает ошибку «Не выбран документ!». При этом в отладчике значение переменной выглядит абсолютно корректно.
В этой статье мы подробно разберем, почему так происходит, выясним внутренние причины такого поведения платформы 1С 7.7, проанализируем разницу между ссылочной и объектной моделью и рассмотрим правильные варианты написания кода.
Рассмотрим по шагам два примера кода, которые вызывают у начинающих разработчиков массу вопросов.
Пример 1 (нерабочий вариант):
Док43 = СоздатьОбъект("Документ");
Док43 = Оп.Документ;
Док43.СделатьНеПроведенным();
Пример 2 (рабочий вариант):
Док43 = СоздатьОбъект("Документ");
Док43.НайтиДокумент(Оп.Документ);
Док43.СделатьНеПроведенным();
Проанализируем ситуацию, которая происходит в первом примере. На первой строчке кода с помощью функции СоздатьОбъект("Документ") в оперативной памяти выделяется специальная структура — объектный контекст. Этот объект обладает всеми необходимыми методами для работы с базой данных: он умеет записывать изменения, проводить или распроводить документ, изменять его табличную часть и реквизиты шапки.
Однако на второй строчке Док43 = Оп.Документ; происходит критическая ошибка. Вместо того чтобы передать ссылку внутрь ранее созданного объекта, мы полностью перезаписываем значение переменной Док43. Теперь в этой переменной хранится обычное значение типа «Документ» — то есть простая ссылка на запись в базе данных. Выделенный ранее объектный контекст безвозвратно теряется в памяти (уничтожается сборщиком мусора).
Поскольку у простой ссылки нет контекста для модификации данных в СУБД, попытка вызвать метод СделатьНеПроведенным() на третьей строчке приводит к закономерной системной ошибке «Не выбран документ!».
Для разработчиков, пришедших из сферы 1С:Предприятие 8.х, эта проблема кажется странной, так как в «восьмерке» типы данных разделены явно: есть ДокументСсылка (для чтения и передачи в реквизиты) и ДокументОбъект (для изменения данных). В платформе 1С 7.7, созданной много лет назад, это разделение реализовано скрыто, на уровне внутренней логики работы платформы, а типы данных в дебагере отображаются одинаково.
Давайте структурируем различия между этими понятиями в 1С 7.7:
Оп.Документ), передать в качестве параметра в процедуры и функции, использовать для сравнения или вывести в печатную форму. Ссылка предназначена исключительно для чтения базовых свойств и идентификации записи. Изменить документ через ссылку невозможно.СоздатьОбъект). Это полноценный программный инструмент, предоставляющий интерфейс для изменения данных. Чтобы объект понял, с какой именно записью в базе данных ему предстоит работать, его необходимо позиционировать.Именно для позиционирования объекта и связывания его с конкретной ссылкой служит метод НайтиДокумент(), который мы видим в рабочем примере №2. Этот метод загружает данные конкретного документа из базы данных в оперативный объектный контекст, подготавливая его к редактированию или проведению.
Помимо метода НайтиДокумент(), в платформе 1С 7.7 существуют и другие способы спозиционировать созданный объект на нужную запись в базе данных:
НайтиПоНомеру(Номер, Дата, [ВидДокумента]). Позволяет найти документ по его текстовому или числовому представлению на определенную дату.ВыбратьДокументы() и ПолучитьДокумент() в цикле. Каждый вызов ПолучитьДокумент() последовательно позиционирует объект на следующую запись из сформированной выборки.Если объект не был спозиционирован ни одним из этих методов, то даже попытка прочитать его реквизиты (например, написать Сообщить(Док43.НомерДок);) вернет пустое значение.
Разберем важный нюанс работы с методом СделатьНеПроведенным(), который часто становится причиной рассинхронизации данных в 1С 7.7.
Когда мы вызываем метод СделатьНеПроведенным(), платформа напрямую в базе данных меняет статус документа на «непроведенный» и очищает все его движения по регистрам (накопления, бухгалтерии или расчета). Однако состояние самого объекта в оперативной памяти компьютера автоматически не обновляется.
Если вы решите сразу после отмены проведения изменить какой-либо реквизит документа в коде и выполнить метод Записать(), то в базу данных могут записаться устаревшие данные, которые были в памяти до момента распроведения. Чтобы избежать подобных ошибок и актуализировать состояние объекта в коде, хорошей практикой считается повторное позиционирование объекта с помощью вызова НайтиДокумент() сразу после изменения его статуса проведения.
Обратим внимание на то, как именно создается объект в примерах:
Док43 = СоздатьОбъект("Документ");
Такой вызов создает объект неопределенного вида. С точки зрения архитектуры СУБД (неважно, используется ли файловая версия DBF или клиент-серверная MS SQL), это заставляет платформу обращаться к общей таблице журнала документов — 1SJOURN. При поиске конкретного документа системе приходится тратить дополнительные ресурсы на анализ метаданных всех существующих в системе видов документов.
Если вам заранее известен вид обрабатываемого документа, мы настоятельно рекомендуем использовать строгую типизацию при создании объекта:
Док43 = СоздатьОбъект("Документ.РасходнаяНакладная");
Рассмотрим, какие преимущества дает такой подход:
DH*) и табличной части (DT*) конкретного вида документа, минуя лишние запросы к общему журналу.Подводя итог нашему разбору, зафиксируем основные правила работы с документами в 1С 7.7, которые уберегут вас от ошибок:
СоздатьОбъект), значение ссылки напрямую через оператор присваивания =, если планируете изменять этот документ.НайтиДокумент() -> внесение изменений -> запись/проведение.СоздатьОбъект() для оптимизации производительности вашей информационной базы.