При программной работе с документами в среде 1С:Предприятие 7.7 разработчики (особенно те, кто переходит с платформы 1С 8.x) часто сталкиваются с крайне неприятной ошибкой: Не выбран документ!. Эта ошибка возникает при попытке вызвать методы модификации табличной части, такие как НоваяСтрока(), УдалитьСтроку() или при попытке заполнить реквизиты. При этом визуально документ может быть успешно создан, записан в базу данных и даже открываться в интерфейсе.
В этой статье мы подробно разберем, почему возникает данная ошибка, проанализируем разницу в объектных моделях платформ и рассмотрим два правильных сценария работы: создание нового документа с нуля и редактирование уже существующего документа в базе данных.
Для того чтобы понять природу этой ошибки, нам необходимо обратиться к архитектуре платформы 1С 7.7. В отличие от современных версий 1С 8.x, где понятия ссылки (ДокументСсылка) и объекта модификации (ДокументОбъект) строго разделены на уровне типов данных, в 1С 7.7 они объединены в один общий тип данных. Однако их внутреннее поведение кардинально различается в зависимости от того, как именно вы обратились к документу.
Давайте выясним разницу между этими состояниями:
СоздатьОбъект("Документ.ИмяВида"). Этот объект обладает памятью для редактирования. Только у него есть активный буфер, в который можно добавлять новые строки с помощью метода НоваяСтрока(), менять реквизиты шапки и вызывать метод Записать().ТекущийДокумент(). Это статический указатель на конкретную физическую запись в базе данных. Ссылка предназначена для чтения реквизитов, передачи документа в качестве субконто, реквизита других документов или вывода в печатные формы.Важное правило 1С 7.7: Попытка вызвать любой метод изменения данных (такой как НоваяСтрока(), Записать(), УстановитьНовыйНомер()) у переменной, которая хранит ссылку (полученную через ТекущийДокумент()), всегда будет приводить к системному сбою и ошибке Не выбран документ!.
Проанализируем некорректный код автора темы:
дВып = СоздатьОбъект("Документ.БанковскаяВыписка");
дВып.Новый();
// ... заполнение шапки ...
дВып.Записать();
текВып = докВып.ТекущийДокумент(); // Здесь переменная текВып становится ССЫЛКОЙ
// ...
текВып.НоваяСтрока(); // ОШИБКА! Попытка модификации ссылки
В данном фрагменте кода разработчик сначала создал полноценный объект дВып, затем записал его, а после этого попытался работать через переменную текВып, которой присвоил ссылку на документ. Из-за этого платформа потеряла контекст редактирования.
---
Если ваша задача заключается в том, чтобы программно создать новый документ, заполнить его шапку, добавить строки в табличную часть и сохранить все это в базе данных, вам абсолютно не нужно перезаписывать и переоткрывать документ несколько раз. Все действия должны выполняться в рамках одного и того же объекта до его итоговой записи.
Рассмотрим по шагам правильный, транзакционно безопасный и быстрый способ создания документа:
// Шаг 1: Создаем объект модификации конкретного вида документа
дВып = СоздатьОбъект("Документ.БанковскаяВыписка");
// Шаг 2: Инициализируем новый документ в оперативной памяти
дВып.Новый();
// Шаг 3: Заполняем реквизиты шапки документа
дВып.РСчет = НДС.ТекущийЭлемент();
дВып.СчетУчета = НДС.СчетУчета;
Если дВып.СчетУчета.Выбран() = 0 Тогда
дВып.СчетУчета = СчетПоКоду(?(дВып.РСчет.Валюта = Гривня, "31.1", "31.2"));
КонецЕсли;
дВып.СчетНДС = дВып.РСчет.СпецСчетНДС;
дВып.Фирма = НДС.Фирма;
дВып.ДатаДок = ДатаДвижения;
// Шаг 4: Программно добавляем строки в табличную часть ДО записи документа
// Цикл для демонстрации добавления нескольких строк
Для НомерСтроки = 1 По 3 Цикл
дВып.НоваяСтрока(); // Метод вызывается у ОБЪЕКТА, созданного через СоздатьОбъект!
// Заполняем реквизиты внутри созданной строки
дВып.Субконто1 = ЗначениеСубконто;
дВып.Сумма = 100 * НомерСтроки;
КонецЦикла;
// Шаг 5: Выполняем однократную запись всего документа целиком в базу данных
дВып.Записать();
При таком подходе исключаются лишние обращения к СУБД, а весь документ (вместе с шапкой и табличной частью) записывается за одну транзакцию. Это гарантирует целостность данных и полностью предотвращает появление пустых "документов-призраков" без табличной части.
---
Что делать, если документ уже существует в базе данных (был создан ранее), и вам необходимо программно открыть его и добавить новые строки? Для этого нельзя использовать ссылку напрямую. Нам потребуется создать новый объект, спозиционировать его на нужной ссылке, внести изменения и сохранить их.
Давайте разберем этот процесс по шагам:
// Предположим, у нас есть ссылка на документ в переменной СсылкаНаДок
СсылкаНаДок = ВыбранныйРанееДокумент.ТекущийДокумент();
// Шаг 1: Создаем пустой объект для работы с документами нужного вида
ДокМодификации = СоздатьОбъект("Документ.БанковскаяВыписка");
// Шаг 2: Позиционируем объект на существующем документе по его ссылке
Если ДокМодификации.НайтиДокумент(СсылкаНаДок) = 1 Тогда
// Шаг 3: Объект успешно найден и выбран. Теперь мы можем его изменять!
ДокМодификации.НоваяСтрока();
// Заполняем новые реквизиты табличной части
ДокМодификации.Сумма = 500;
// Шаг 4: Обязательно записываем внесенные изменения
ДокМодификации.Записать();
Сообщить("Строка успешно добавлена в существующий документ!");
Иначе
Предупреждение("Не удалось найти указанный документ в базе данных!");
КонецЕсли;
В этом примере мы использовали метод НайтиДокумент(). Он связывает пустой объект ДокМодификации с конкретной записью в базе данных. После успешного поиска объект переходит в состояние "Выбран", что открывает возможность использования метода НоваяСтрока() и записи изменений.
---
Иногда разработчики сталкиваются с этой же ошибкой, даже когда они уверяют, что используют объект, а не ссылку. Проанализируем ситуации, когда платформа может сбросить позиционирование объекта:
СоздатьОбъект("Документ"), то для него будут недоступны многие методы модификации (включая Новый()) до тех пор, пока вы явно не найдете документ определенного вида через поиск. Старайтесь всегда указывать вид документа при создании объекта, если это возможно: СоздатьОбъект("Документ.ИмяВида").ВыбратьДокументы() и ПолучитьДокумент(), а затем внутри этого же цикла пытаетесь записать изменения в текущий объект выборки, позиционирование выборки может сбиться. Правильный подход — считывать ссылку на документ из выборки, передавать ее во вспомогательный объект, созданный отдельно через СоздатьОбъект(), изменять его и записывать.ОбработкаПроведения() вызовет системную ошибку блокировки. Во время проведения документ уже заблокирован платформой для записи, и менять его табличную часть запрещено.Резюмируем: Для исключения ошибки Не выбран документ! всегда четко разделяйте сущности. Заполнение данных и вызовы метода НоваяСтрока() делайте только для объектов, полученных через СоздатьОбъект(), и избегайте необоснованного использования метода ТекущийДокумент() в цепочках модификации данных.