При разработке механизмов проведения документов в системе «1С:Предприятие 8.3» программисты часто сталкиваются с ошибками в запросах, использующих временные таблицы. Одной из самых коварных является ошибка «Поле не найдено», которая возникает даже в тех случаях, когда разработчику кажется, что поле в таблице точно существует. В этой статье мы подробно разберем, почему возникает такая ситуация, проанализируем конкретный программный код и изучим способы оптимизации запросов для исключения подобных проблем в будущем.
Рассмотрим сценарий: в модуле объекта документа ОказаниеУслуги выполняется обработчик ОбработкаПроведения. Для расчета остатков и себестоимости используются два последовательных запроса, связанных через МенеджерВременныхТаблиц. Система выдает следующую ошибку:
{Документ.ОказаниеУслуги.МодульОбъекта(70)}: Ошибка при вызове метода контекста (Выполнить) РезультатЗапроса = Запрос2.Выполнить(); по причине: {(5, 24)}: Поле не найдено "НоменклатураДокумента.СуммаВДокументе"
Эта ошибка однозначно указывает на то, что во временной таблице НоменклатураДокумента отсутствует колонка с именем СуммаВДокументе. Чтобы понять причину, нам необходимо проанализировать первый запрос, в котором эта таблица создается и наполняется данными.
Давайте посмотрим на исходный код первого запроса, предоставленный автором вопроса:
// Создать менеджер временных таблиц
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст =
"ВЫБРАТЬ
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура,
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры,
| СУММА(ОказаниеУслугиПереченьНоменклатуры.Количество) КАК КоличествоВДокументе,
| ОказаниеУслугиПереченьНоменклатуры.Сумма
|ПОМЕСТИТЬ НоменклатураДокумента
|ИЗ
| Документ.ОказаниеУслуги.ПереченьНоменклатуры КАК ОказаниеУслугиПереченьНоменклатуры
|ГДЕ
| ОказаниеУслугиПереченьНоменклатуры.Ссылка = &Ссылка
|СГРУППИРОВАТЬ ПО
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура,
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры,
| ОказаниеУслугиПереченьНоменклатуры.Сумма";
Запрос.Выполнить();
Проанализируем внимательно секцию ВЫБРАТЬ. Мы видим, что поле Сумма выбирается без псевдонима (алиаса). Это означает, что во временной таблице НоменклатураДокумента колонка будет называться просто Сумма.
Теперь обратимся ко второму запросу, который и вызывает ошибку:
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос2.Текст = "ВЫБРАТЬ
| НоменклатураДокумента.Номенклатура,
| НоменклатураДокумента.ВидНоменклатуры,
| НоменклатураДокумента.КоличествоВДокументе,
| НоменклатураДокумента.СуммаВДокументе,
...
|ИЗ
| НоменклатураДокумента КАК НоменклатураДокумента
...";
Здесь мы видим обращение к полю НоменклатураДокумента.СуммаВДокументе. Поскольку в первом запросе мы не задали псевдоним, платформа не может найти это поле. Решение: необходимо явно назначить псевдоним полю во временной таблице.
Исправленный фрагмент первого запроса должен выглядеть так:
| СУММА(ОказаниеУслугиПереченьНоменклатуры.Количество) КАК КоличествоВДокументе,
| ОказаниеУслугиПереченьНоменклатуры.Сумма КАК СуммаВДокументе
При изучении кода первого запроса можно заметить еще одну серьезную проблему, которая приведет к неверным расчетам, даже если ошибка с именем поля исчезнет. Рассмотрим секцию СГРУППИРОВАТЬ ПО:
|СГРУППИРОВАТЬ ПО
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура,
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры,
| ОказаниеУслугиПереченьНоменклатуры.Сумма
В документе «Оказание услуги» одна и та же номенклатура может быть введена несколькими строками (например, с разной ценой). Если мы группируем данные по полю Сумма, то строки с одинаковой номенклатурой, но разными суммами не свернутся в одну строку. Это критично для алгоритма списания остатков и расчета себестоимости.
Правильный подход — суммировать значения ресурса. Исправим код первого запроса полностью:
Запрос.Текст =
"ВЫБРАТЬ
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура КАК Номенклатура,
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры,
| СУММА(ОказаниеУслугиПереченьНоменклатуры.Количество) КАК КоличествоВДокументе,
| СУММА(ОказаниеУслугиПереченьНоменклатуры.Сумма) КАК СуммаВДокументе
|ПОМЕСТИТЬ НоменклатураДокумента
|ИЗ
| Документ.ОказаниеУслуги.ПереченьНоменклатуры КАК ОказаниеУслугиПереченьНоменклатуры
|ГДЕ
| ОказаниеУслугиПереченьНоменклатуры.Ссылка = &Ссылка
|СГРУППИРОВАТЬ ПО
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура,
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры";
Чтобы избежать подобных ошибок в будущем и сделать код более производительным, рекомендуем придерживаться следующих правил:
Запрос и их ручной связки через менеджер временных таблиц, лучше объединить их в один пакетный запрос. В тексте запроса части разделяются символом точки с запятой (;). Это упрощает чтение кода и отладку.КАК), даже если имя поля совпадает с именем реквизита. Это защитит ваш код от ошибок при изменении структуры метаданных.Движения.ИмяРегистра.Записать()). Это гарантирует, что запрос не увидит старые движения этого же документа при перепроведении.РегистрНакопления.ОстаткиМатериалов.Остатки) всегда передавайте в параметры виртуальной таблицы момент времени и условия по измерениям. Это значительно повышает скорость выполнения запроса на больших базах данных.Разберем пример передачи параметров в виртуальную таблицу:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Остатки(&МоментВремени,
Материал В (ВЫБРАТЬ Номенклатура ИЗ НоменклатураДокумента)) КАК ОстаткиМатериаловОстатки
ПО НоменклатураДокумента.Номенклатура = ОстаткиМатериаловОстатки.Материал
Следуя этим правилам, вы обеспечите стабильную работу механизмов проведения и минимизируете риск возникновения трудноуловимых ошибок в запросах.