Разработка и отладка внешних печатных форм в среде «1С:Предприятие» — это стандартная задача для любого программиста. Однако в процессе работы можно столкнуться с неочевидным поведением системы, когда платформа выдает ошибку синтаксиса в запросе, даже если визуально код кажется абсолютно верным. Особое недоумение вызывает ситуация, когда после удаления проблемного поля из текста запроса ошибка продолжает воспроизводиться.
В данной статье мы подробно разберем, почему возникает ошибка «Неправильный псевдоним» при выполнении запроса во внешней печатной форме, как правильно обращаться к реквизитам табличных частей документов и каким образом победить кэширование временных файлов 1С при тестировании обработок.
Прежде чем перейти к исправлению кода, давайте разберем саму природу возникновения ошибки Неправильный псевдоним. В платформе 1С этот сбой чаще всего происходит по трем причинам:
КАК. Язык запросов 1С категорически запрещает использовать символ точки в названии результирующего поля выборки. Например, конструкция АктПриемкиНаРемонт.ПланируемаяСтоимость КАК АктПриемкиНаРемонт.ПланируемаяСтоимость вызовет жесткий отказ системы. Допускается только плоская структура имени, например КАК ПланируемаяСтоимость или КАК Акт_ПланируемаяСтоимость.ПланируемаяСтоимость расположено в табличной части (например, в табличной части СтатусыАкта), к нему нельзя обратиться из шапки как к простому реквизиту. Попытка написать АктПриемкиНаРемонт.ПланируемаяСтоимость приведет к ошибке, так как система не понимает, из какой именно строки таблицы нужно получить данные.ПредпологаемаяСтоимость (с орфографической ошибкой, зашитой в метаданные разработчиком конфигурации) и ПланируемаяСтоимость. Несоответствие реального имени реквизита в конфигураторе и имени в тексте запроса гарантирует падение метода Выполнить().Давайте проанализируем ситуацию, описанную автором темы. Он уверяет, что удалил поле из текста запроса, но система все равно продолжает ругаться на псевдоним АктПриемкиНаРемонт.ПланируемаяСтоимость. Выясним причину этого феномена.
Когда мы регистрируем внешнюю печатную форму в справочнике «Дополнительные внешние отчеты и обработки», платформа записывает двоичные данные файла .epf непосредственно в информационную базу. При нажатии кнопки «Печать» в документе система выполняет следующие действия:
Если вы открыли внешнюю обработку через конфигуратор, внесли изменения, сохранили файл на диске, но не обновили его в справочнике информационной базы, система продолжит запускать старую версию файла из своего кэша (хранилища значений). Именно поэтому у автора темы возникала иллюзия того, что поле из запроса удалено, а ошибка все равно появляется.
Для решения этой проблемы при отладке всегда используйте меню «Файл — Открыть» в режиме «1С:Предприятие» либо обновляйте файл в справочнике принудительно после каждого сохранения кода.
Внимательно изучим код перебора строк из сообщения автора:
Для каждого Строка Из СсылкаНаОбъект.СтатусыАкта Цикл
Если Строка.СтатусАкта = Перечисления.СтатусыАктовПриемки.Выдано Тогда
ОбластьМакета.Параметры["Выдал"] = Строка.Ответственный.Наименование;
ОбластьМакета.Параметры["ДатаВыдачи"] = Формат(Строка.Дата,"ДЛФ=Д");
//ОбластьМакета.Параметры["ПланСтоим"] = Формат(Строка.ПланируемаяСтоимость,"ЧЦ=10; ЧДЦ=2");
КонецЕсли;
КонецЦикла;
Этот фрагмент кода наглядно доказывает, что реквизит ПланируемаяСтоимость принадлежит табличной части СтатусыАкта. Если нам необходимо вывести этот показатель в печатную форму, у нас есть два пути решения задачи.
Если печатная форма формируется для одного конкретного документа, и объем данных в табличной части невелик, мы можем обойтись без усложнения основного запроса. Мы просто раскомментируем строку в цикле по табличной части и передадим данные напрямую в параметры макета:
Для каждого Строка Из СсылкаНаОбъект.СтатусыАкта Цикл
Если Строка.СтатусАкта = Перечисления.СтатусыАктовПриемки.Выдано Тогда
ОбластьМакета.Параметры["Выдал"] = Строка.Ответственный.Наименование;
ОбластьМакета.Параметры["ДатаВыдачи"] = Формат(Строка.Дата, "ДЛФ=Д");
// Безопасное получение и форматирование планируемой стоимости
Если ЗначениеЗаполнено(Строка.ПланируемаяСтоимость) Тогда
ОбластьМакета.Параметры["ПланСтоим"] = Формат(Строка.ПланируемаяСтоимость, "ЧЦ=10; ЧДЦ=2");
Иначе
ОбластьМакета.Параметры["ПланСтоим"] = 0;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если мы хотим спроектировать максимально быстродействующую и правильную с точки зрения стандартов разработки печатную форму, все данные необходимо получать одним запросом. Для этого мы свяжем основную таблицу документа с табличной частью СтатусыАкта с помощью операции ЛЕВОЕ СОЕДИНЕНИЕ по условию равенства ссылок и статуса.
Рассмотрим пример построения такого запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| АктПриемки.Номер КАК Номер,
| АктПриемки.Дата КАК Дата,
| АктПриемки.Организация КАК Организация,
| АктПриемки.Контрагент КАК Контрагент,
| АктПриемки.Точка КАК Точка,
| АктПриемки.ПредпологаемаяСтоимость КАК ПредполагаемаяСтоимость,
| ТабЧастьСтатусы.ПланируемаяСтоимость КАК ПланируемаяСтоимость,
| ТабЧастьСтатусы.Ответственный КАК ВыдалОтветственный,
| ТабЧастьСтатусы.Дата КАК ДатаВыдачиСтатус
|ИЗ
| Документ.АктПриемкиНаРемонт КАК АктПриемки
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.АктПриемкиНаРемонт.СтатусыАкта КАК ТабЧастьСтатусы
| ПО АктПриемки.Ссылка = ТабЧастьСтатусы.Ссылка
| И ТабЧастьСтатусы.СтатусАкта = &СтатусВыдано
|ГДЕ
| АктПриемки.Ссылка = &СсылкаНаДокумент";
Запрос.УстановитьПараметр("СсылкаНаДокумент", СсылкаНаОбъект);
Запрос.УстановитьПараметр("СтатусВыдано", Перечисления.СтатусыАктовПриемки.Выдано);
Выборка = Запрос.Выполнить().Выбрать();
Проанализируем преимущества этого подхода. Мы избавляемся от лишних циклов в коде и за одно обращение к базе данных получаем и реквизиты шапки документа, и необходимые нам сведения о планируемой стоимости из конкретной строки табличной части, соответствующей статусу Выдано.
В процессе анализа исходного кода функции Печать() была обнаружена грубая архитектурная ошибка, которая существенно снижает производительность системы при массовой печати документов:
Если (СсылкаНаОбъект.ПолучитьОбъект().ЭтоНовый()) ИЛИ (СсылкаНаОбъект.ПолучитьОбъект().Модифицированность()) Тогда
Возврат(0);
КонецЕсли;
Метод ПолучитьОбъект() выполняет полное считывание всех таблиц документа из базы данных в оперативную память. Вызов этого метода дважды подряд для проверки того, записан ли документ, крайне неэффективен. Перепишем эту проверку правильно:
// Проверяем, записан ли документ в базу данных без обращения к его объектной модели
Если Не ЗначениеЗаполнено(СсылкаНаОбъект) Или СсылкаНаОбъект.Пустая() Тогда
Предупреждение("Документ должен быть записан перед печатью!");
Возврат Неопределено;
КонецЕсли;
Также обратим внимание на работу с контактной информацией клиента. Код получения телефона и адреса написан достаточно громоздко. В типовых конфигурациях 1С 8.2 на обычных формах (таких как «Управление торговлей 10.3») предусмотрены встроенные механизмы работы со справочниками контактной информации.
Давайте соберем итоговый вариант функции создания табличного документа, который будет работать быстро, надежно и не вызовет ошибок компиляции запроса:
Функция СоздатьТабличныйДокументПечатиАкта()
ТабДокумент = Новый ТабличныйДокумент;
ТабДокумент.ИмяПараметровПечати = "ПАРАМЕТРЫ_ПЕЧАТИ_АктПриемкиВРемонт";
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| АктПриемки.Номер КАК Номер,
| АктПриемки.Дата КАК Дата,
| АктПриемки.Организация КАК Организация,
| АктПриемки.Склад КАК Склад,
| АктПриемки.Контрагент КАК Контрагент,
| АктПриемки.Ответственный КАК Ответственный,
| АктПриемки.Подразделение КАК Подразделение,
| АктПриемки.Комментарий КАК Комментарий,
| АктПриемки.ДокументОснование.Дата КАК ДатаПродажи,
| АктПриемки.ПричинаОбращения КАК ПричинаОбращения,
| АктПриемки.ВидРемонта КАК ВидРемонта,
| АктПриемки.Техник КАК Техник,
| АктПриемки.ЗаключениеТехника КАК ЗаключениеТехника,
| АктПриемки.Приемщик КАК Приемщик,
| АктПриемки.СтатусАкта КАК СтатусАкта,
| АктПриемки.Номенклатура КАК Номенклатура,
| АктПриемки.СерийныйНомер КАК СерийныеНомера,
| АктПриемки.НомерБатареи КАК НомерБатареи,
| АктПриемки.IMEI_Телефона КАК IMEI_Телефона,
| АктПриемки.Оператор КАК Оператор,
| АктПриемки.Комплектность КАК Комплектность,
| АктПриемки.ОграничениеСтоимости КАК ОграниченияСтоимости,
| АктПриемки.КодПароль КАК КодПароль,
| АктПриемки.НеисправностьСоСловВладельца КАК НеисправностьСоСловВладельца,
| АктПриемки.РезультатПроверки КАК РезультатПроверки,
| АктПриемки.ДокументОснование.Номер КАК НомерОснования,
| АктПриемки.Точка КАК Точка,
| АктПриемки.Замечания КАК Замечания,
| АктПриемки.ПредпологаемаяСтоимость КАК ПредполагаемаяСтоимость,
| СтатусыВыдачи.Дата КАК ДатаВыдачи,
| СтатусыВыдачи.Ответственный КАК Выдал,
| СтатусыВыдачи.ПланируемаяСтоимость КАК ПланСтоим
|ИЗ
| Документ.АктПриемкиНаРемонт КАК АктПриемки
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.АктПриемкиНаРемонт.СтатусыАкта КАК СтатусыВыдачи
| ПО АктПриемки.Ссылка = СтатусыВыдачи.Ссылка
| И СтатусыВыдачи.СтатусАкта = &СтатусВыдано
|ГДЕ
| АктПриемки.Ссылка = &СсылкаНаДокумент";
Запрос.УстановитьПараметр("СсылкаНаДокумент", СсылкаНаОбъект);
Запрос.УстановитьПараметр("СтатусВыдано", Перечисления.СтатусыАктовПриемки.Выдано);
Выборка = Запрос.Выполнить().Выбрать();
Если Не Выборка.Следующий() Тогда
Возврат ТабДокумент;
КонецЕсли;
Макет = ПолучитьМакет("Акт");
// Параметры страницы
ТабДокумент.Очистить();
ТабДокумент.ПолеСверху = 10;
ТабДокумент.ПолеСлева = 10;
ТабДокумент.ПолеСправа = 10;
ТабДокумент.РазмерКолонтитулаСверху = 0;
ТабДокумент.РазмерКолонтитулаСнизу = 0;
ТабДокумент.АвтоМасштаб = Истина;
ТабДокумент.РазмерСтраницы = "A4";
ТабДокумент.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
ОбластьМакета = Макет.ПолучитьОбласть("Основной");
ОбластьМакета.Parameters.Заполнить(Выборка);
// Форматирование дат
ОбластьМакета.Параметры["Дата"] = Формат(Выборка.Дата, "ДЛФ=Д");
ОбластьМакета.Параметры["ДатаПродажи"] = Формат(Выборка.ДатаПродажи, "ДЛФ=Д");
Если ЗначениеЗаполнено(Выборка.ДатаВыдачи) Тогда
ОбластьМакета.Параметры["ДатаВыдачи"] = Формат(Выборка.ДатаВыдачи, "ДЛФ=Д");
Иначе
ОбластьМакета.Параметры["ДатаВыдачи"] = "";
КонецЕсли;
Если ЗначениеЗаполнено(Выборка.Выдал) Тогда
ОбластьМакета.Параметры["Выдал"] = Выборка.Выдал.Наименование;
КонецЕсли;
// Вывод планируемой стоимости
Если ЗначениеЗаполнено(Выборка.ПланСтоим) Тогда
ОбластьМакета.Параметры["ПланСтоим"] = Формат(Выборка.ПланСтоим, "ЧЦ=10; ЧДЦ=2");
Иначе
ОбластьМакета.Параметры["ПланСтоим"] = "0.00";
КонецЕсли;
// Чтение контактной информации
Телефон = "";
АдресКлиента = "";
НаборЗаписей = Новый ТаблицаЗначений;
УправлениеКонтактнойИнформацией.ПрочитатьКонтактнуюИнформацию(НаборЗаписей, Выборка.Контрагент);
Для Каждого СтрокаКонтакт Из НаборЗаписей Цикл
Если СтрокаКонтакт.Вид = Справочники.ВидыКонтактнойИнформации.ТелефонКонтрагента Тогда
Телефон = Строка(СтрокаКонтакт.Представление);
ИначеЕсли СтрокаКонтакт.Вид = Справочники.ВидыКонтактнойИнформации.ФактАдресКонтрагента Тогда
АдресКлиента = Строка(СтрокаКонтакт.Представление);
КонецЕсли;
КонецЦикла;
ОбластьМакета.Параметры["ТелефонСотовый"] = Телефон;
ОбластьМакета.Параметры["Адрес"] = Выборка.Точка.Адресс; // Проверьте написание реквизита "Адресс" (с одной или двумя "с" в метаданных)
ТабДокумент.Вывести(ОбластьМакета);
Возврат ТабДокумент;
КонецФункции
Часто разработчики жалуются, что точки останова в модуле внешней печатной формы «не цепляются» отладчиком. Проанализируем, почему это происходит и как настроить рабочее окружение:
-debug.Применяя предложенные рекомендации и используя корректные запросы с левыми соединениями, вы сможете избежать досадных ошибок компиляции и существенно повысить скорость работы ваших печатных форм в системе 1С.