Дело было так. Проверки табличной части при проведении документа были добавлены в неподходящий для этих целей обработчик события "ПередЗаписью". Для самой проверки вызывался серверный код. Отладка на сервере отключена. В алгоритм проверки закралась ошибка, вызывающая исключение при наличии в табличной части некоторых элементов справочника. Эта бомба замедленного действия сработала на документе, в который было добавлено несколько сотен строк. Как обычно в процессе заполнения кнопка "Сохранить" ни разу не нажималась. В результате сохранение с трудом заполненного документа оказалось невозможным, а ручной поиск строк, мешающих записи, по времени был сопоставим с повторным заполнением документа.
Запросы к БД были бесполезны - документ не записан. Внешних обработок заполнения к документу подключено не было и нечего было подменить, чтобы обработать табличную часть уже открытого документа. Работа велась в толстом клиенте обычном приложении, что делало невозможным применение метода ПолучитьОкна(). Документ нужно было спасать другим методом.
Решение оказалось простым. Каждая форма документа, в том числе обычная, связана с ее ключом - ссылкой на документ. То же справедливо для форм и других объектных типов 1С. У нового документа эта ссылка пустая. Платформа 1С имеет особенность. Если для ссылки-ключа производится попытка получения/открытия формы, которая уже была открыта, то новая форма не создается. Возвращается уже открытая форма! Дальше остается только обратиться к ее свойствам и прочитать хранимый в ней объект, создать на его основе новый и записать, по ходу дела удаляя из него информацию, мешающую записи.
То есть ключевая идея выражается всего в одной строчке кода:
Форма = ПолучитьФорму("Документ."+Ссылка.Метаданные().Имя+".ФормаОбъекта", Новый Структура("Ключ", Ссылка));
При этом Ссылка может быть и пустой. Дальше остается обратиться к свойству Форма.Объект или Форма.ДокументОбъект. В справочник внешних обработок была загружена обработка, получавшая данные формы. Документ был спасен путем создания его копии на основе данных формы без строк мешающих записи. Информация по удаленным из него строкам сохранена в файл и после исправления ошибки алгоритма занесена обратно.
Понятно, что такое стечение обстоятельств вряд ли может еще когда-нибудь повториться и практическая ценность описанного метода почти нулевая. Однако он демонстрирует интересную возможность работы с формами. Ниже приводится пример кода , который позволяет получить форму открытого документа и вытянуть из нее все данные. Код может работать как в обычной, так и в управляемой форме обработки без каких либо изменений, копируя данные как из обычной, так и управляемой формы открытого документа. Для применения кода нужно создать внешнюю обработку, добавить в нее реквизит СсылкаНаДокумент (ссылка на любой документ) и назначить обработчиком нашей команды метод "КнопкаВыполнитьНажатие(Команда)".
Заметьте, что использование директив компиляции &НаКлиенте и &НаСервере не запрещено в том числе и в обычной форме обработки. Если интересно посмотреть на работу кода, но лень создавать обработку, можно скачать приложенный к публикации файл :)
&НаКлиенте
Процедура КнопкаВыполнитьНажатие(Команда)
ЭтоУправляемаяФормаОбработки = ТипЗнч(ЭтаФорма) = Тип("УправляемаяФорма");
Если НЕ ЭтоУправляемаяФормаОбработки Тогда//в обычной форме основной реквизит называется ОбработкаОбъект,
Объект = ЭтаФорма.ОбработкаОбъект; //для однотипности кода будем всегда работать с переменной Объект
КонецЕсли;
Если Объект.СсылкаНаДокумент = Неопределено Тогда
Сообщить("Необходимо выбрать тип документа или указать конкретный документ");
Возврат;
КонецЕсли;
Если ЭтоУправляемаяФормаОбработки Тогда
#Если ТолстыйКлиентУправляемоеПриложение ИЛИ ТонкийКлиент Тогда
ИмяМетаданных = ПолучитьИмяМетаданныхНаСервереУправляемойФормы(Объект.СсылкаНаДокумент);
#КонецЕсли
Иначе
#Если ТолстыйКлиентОбычноеПриложение Тогда
ИмяМетаданных = Объект.СсылкаНаДокумент.Метаданные().Имя;
#КонецЕсли
КонецЕсли;
Форма = ПолучитьФорму("Документ."+ИмяМетаданных+".ФормаОбъекта", Новый Структура("Ключ", Объект.СсылкаНаДокумент));
Если НЕ Форма.Открыта() Тогда
Сообщить("Вы выбрали документ, форма которого не открыта. Сначала откройте форму документа.");
Возврат;
КонецЕсли;
Если ТипЗнч(Форма) = Тип("УправляемаяФорма") Тогда
ОбъектКопирования = Форма.Объект; //здесь не учитываем, что в редких случаях объект может быть переименован
ИначеЕсли ТипЗнч(Форма) = Тип("Форма") Тогда
ОбъектКопирования = Форма.ДокументОбъект; //здесь не учитываем, что в редких случаях объект может быть переименован
КонецЕсли;
Если ЭтоУправляемаяФормаОбработки Тогда
#Если ТолстыйКлиентУправляемоеПриложение ИЛИ ТонкийКлиент Тогда
СсылкаНаНовыйДокумент = СкопироватьОбъектНаСервереВУправляемомПриложении(ОбъектКопирования, ИмяМетаданных);
ОткрытьЗначение(СсылкаНаНовыйДокумент);
#КонецЕсли
Иначе
#Если ТолстыйКлиентОбычноеПриложение Тогда
СкопироватьОбъектНаКлиентеВОбычномПриложении(ОбъектКопирования, ИмяМетаданных);
#КонецЕсли
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция ПолучитьИмяМетаданныхНаСервереУправляемойФормы(Ссылка)
Возврат Ссылка.Метаданные().Имя;
КонецФункции
&НаСервере
Функция СкопироватьОбъектНаСервереВУправляемомПриложении(Объект, ИмяМетаданных)
//В управляемом приложении будем расчитывать на данные
ДокументОбъект = ДанныеФормыВЗначение(Объект, Тип("ДокументОбъект."+ИмяМетаданных));
НовыйОбъект = ДокументОбъект.Скопировать();
//Здесь можно провести программную модификацию документа, например удалить строки, мешающие записи
НовыйОбъект.Дата = ДокументОбъект.Дата;
НовыйОбъект.Записать();
Возврат НовыйОбъект.Ссылка;
КонецФункции
#Если ТолстыйКлиентОбычноеПриложение Тогда
Функция СкопироватьОбъектНаКлиентеВОбычномПриложении(Объект, ИмяМетаданных)
Если ТипЗнч(Объект) = Тип("ДанныеФормыСтруктура") Тогда //данные упр.формы
ДокументОбъект = ДанныеФормыВЗначение(Объект, Тип("ДокументОбъект."+ИмяМетаданных));
НовыйОбъект = ДокументОбъект.Скопировать();
Иначе //объект из обычной формы
НовыйОбъект = Объект.Скопировать();
КонецЕсли;
//Здесь можно провести программную модификацию документа, например удалить строки, мешающие записи
ОткрытьЗначение(НовыйОбъект); //В обычном приложении документ можно открыть не записывая в базу данных
КонецФункции
#КонецЕсли