Почему 1С ругается на точку после пустого значения и как это исправить
Содержание
Ошибка Значение не является значением объектного типа (Наименование) в 1С означает одно: код поставил точку после значения, у которого нет свойств. Чаще всего это Неопределено, NULL, число, строка или дата. В скобках платформа пишет имя свойства, к которому пытались обратиться, а в подробном тексте — модуль и номер строки. Исправить можно двумя путями: проверить значение перед обращением через точку или сделать так, чтобы туда вообще не попадало пустое значение.
Пользователю, который не правит код, путь короче: записать полный текст ошибки, проверить расширения и внешние обработки, обновить конфигурацию и передать текст разработчику. Программисту нужен подробный текст и отладчик — по ним строка находится за пару минут.
Откуда берётся ошибка: объектные и примитивные типы
В языке 1С есть значения, у которых можно спросить свойство через точку, и значения, у которых нельзя.
| Значение | Обращение через точку | Что будет |
|---|---|---|
СправочникСсылка, ДокументСсылка | Ссылка.Наименование | работает |
Структура, ТаблицаЗначений, строка таблицы | Строка.Сумма | работает, если свойство есть |
Неопределено | Значение.Наименование | наша ошибка |
NULL | Выборка.Контрагент.ИНН при пустом поле | наша ошибка |
| Число, Строка, Дата, Булево | Сумма.Значение | наша ошибка |
Значения из трёх последних строк называют примитивными. У них нет реквизитов, табличных частей и методов, вызываемых через точку. Как только код встречает конструкцию Х.Свойство, а в Х лежит примитив, выполнение останавливается.
Отличайте её от похожих сообщений. Текст подсказывает, где искать.
| Текст ошибки | Что не так |
|---|---|
Значение не является значением объектного типа (ИНН) | слева от точки пусто или примитив |
Поле объекта не обнаружено (ИНН) | объект есть, но такого свойства у него нет |
Метод объекта не обнаружен (Записать) | объект есть, метода у него нет |
Индекс находится за границами диапазона | обращение к несуществующему элементу коллекции |
💡 Пустая ссылка — не примитив. Справочники.Контрагенты.ПустаяСсылка().Наименование ошибку не вызывает и возвращает пустую строку. Поэтому чаще всего виновник — именно Неопределено или NULL, а не незаполненный реквизит ссылочного типа.
Где в коде и запросах чаще всего ломается точка
Сценарии из типовых и доработанных конфигураций. Почти каждый сводится к тому, что функция или поле вернули пустоту, а код на это не рассчитывал.
- 🔍 Поиск, который ничего не нашёл.
ТаблицаЗначений.Найти()возвращаетНеопределено, если строки нет. Следующая строкаНайденнаяСтрока.Количествопадает. - 📦 Соответствие без ключа.
Соответствие[Ключ]при отсутствии ключа даётНеопределено. - 🔗 Левое соединение в запросе. Поле из присоединённой таблицы равно
NULL, если пары не нашлось. Сам запрос отрабатывает, а падает код, который обходит выборку. - 🧩 Составной тип реквизита. Реквизит может хранить ссылку, число или строку. Пока там ссылка — всё работает, первая же строка в нём ломает обработку.
- ↩️ Функция без
Возвратна всех ветках. Если функция выходит без возврата значения, вызывающий код получаетНеопределено. - 🖥️ Клиентский код управляемой формы. На клиенте ссылка не «раскрывается» в реквизиты базы. Конструкции вида
Объект.Контрагент.ИННв процедуре с&НаКлиентек данным не доберутся — их получают серверным вызовом.
Типичный пример с поиском:
НайденнаяСтрока = ТаблицаТоваров.Найти(Номенклатура, "Номенклатура");
Количество = НайденнаяСтрока.Количество; // ошибка, если строки нет
Исправленный вариант:
НайденнаяСтрока = ТаблицаТоваров.Найти(Номенклатура, "Номенклатура");
Если НайденнаяСтрока = Неопределено Тогда
Количество = 0;
Иначе
Количество = НайденнаяСтрока.Количество;
КонецЕсли;
В самом языке запросов разыменование NULL ошибку не даёт: Контрагенты.Ссылка.ИНН от пустого поля вернёт NULL. Проблема переезжает в код, который читает результат.
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ИНН = Выборка.Контрагент.ИНН; // ошибка, если Контрагент = NULL
КонецЦикла;
Надёжнее сразу получить нужный реквизит в запросе и подстраховать пустоту функцией ЕСТЬNULL:
ВЫБРАТЬ
Заказы.Ссылка КАК Заказ,
ЕСТЬNULL(Договоры.Владелец.ИНН, "") КАК ИНН
ИЗ
Документ.ЗаказКлиента КАК Заказы
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ДоговорыКонтрагентов КАК Договоры
ПО Заказы.Договор = Договоры.Ссылка
Имена таблиц здесь условные — подставьте свои. Важен приём: реквизит забирается в запросе одним полем, а не дочитывается через точку в цикле. Это ещё и быстрее: каждое обращение Ссылка.Реквизит в цикле — отдельное чтение из базы.
Как найти строку с ошибкой и исправить модуль
Порядок действий для разработчика, у которого есть доступ к Конфигуратору или EDT.
1. Получите подробный текст
В окне ошибки нажмите «Подробно» или «Подробный текст ошибки». В нём будет стек вида {Документ.РеализацияТоваровУслуг.МодульОбъекта(214)}: имя модуля и номер строки. Если окно уже закрыто, то же самое лежит в журнале регистрации: Администрирование → Обслуживание → Журнал регистрации, отбор по уровню «Ошибка» и времени.
2. Поймайте ошибку отладчиком
- Откройте Конфигуратор и запустите отладку (
F5). - Включите
Отладка → Остановка по ошибке. - Повторите действие пользователя в запущенном сеансе.
- Отладчик остановится на проблемной строке. Откройте «Табло» или выделите выражение слева от точки и посмотрите его тип.
Если слева Неопределено, поднимайтесь по коду вверх и ищите, где переменная должна была получить значение. Если NULL — ищите запрос и соединение, откуда пришло поле.
3. Выберите способ исправления
| Способ | Когда подходит | Риск |
|---|---|---|
Проверка = Неопределено или ЗначениеЗаполнено() | результат поиска или функции может быть пустым по смыслу | низкий |
Проверка ТипЗнч(Х) = Тип("СправочникСсылка.Контрагенты") | реквизит составного типа | низкий |
ЕСТЬNULL и выборка реквизита прямо в запросе | данные приходят из запроса с соединениями | низкий |
| Исправить источник: заполнить реквизит, вернуть значение из функции | пустоты там быть не должно по логике | средний, нужно понять бизнес-логику |
Попытка ... Исключение | почти никогда | высокий: ошибка прячется, данные считаются неверно |
Пример для составного типа:
Если ТипЗнч(Документ.Контрагент) = Тип("СправочникСсылка.Контрагенты") Тогда
ИНН = Документ.Контрагент.ИНН;
Иначе
ИНН = "";
КонецЕсли;
Для клиентского кода формы — перенос чтения на сервер:
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
ИНН = ИННКонтрагента(Объект.Контрагент);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ИННКонтрагента(Контрагент)
Если Не ЗначениеЗаполнено(Контрагент) Тогда
Возврат "";
КонецЕсли;
Возврат Контрагент.ИНН;
КонецФункции
В конфигурациях на БСП для того же есть ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "ИНН") — вызывайте его в серверной процедуре.
Не правьте модули типовой конфигурации на рабочей базе без копии. Сначала выгрузите информационную базу в .dt или сделайте копию средствами СУБД, а изменения переносите расширением — так они переживут следующее обновление.
Ошибка в отчётах и схеме компоновки данных
В отчёте на СКД ошибка обычно появляется при формировании или при расшифровке. Сам запрос набора данных, как уже сказано, к NULL терпим. Ломается встроенный язык вокруг схемы.
Где искать:
- 📄 Модуль объекта отчёта. Обработчик
ПриКомпоновкеРезультатаи процедуры, которые донастраивают параметры: например, берутПараметр.Значение.Организация, когда параметр не заполнен. - 🧮 Функции общих модулей в выражениях. Вычисляемое поле или ресурс вызывает экспортную функцию, а та обращается к реквизиту переданного значения. Для итоговой строки или пустой группировки туда приходит
NULL. - 🔎 Обработка расшифровки. Код получает значение поля из расшифровки и берёт его реквизит, а пользователь щёлкнул по ячейке итога, где поле пустое.
- 🖨️ Печатные формы на макетах. Поле макета заполняется выражением вида
Шапка.Договор.Номер, а в документе договор не указан.
Что делать:
- Сформируйте отчёт с минимальными отборами и повторите ошибку — так проще понять, на какой строке данных она возникает.
- В подробном тексте посмотрите модуль: если это
МодульОбъектаотчёта или общий модуль, ищите функцию из выражения. - В функции общего модуля добавьте проверку входного параметра первой строкой:
Если Не ЗначениеЗаполнено(Значение) Тогда Возврат .... - Если реквизит нужен только для вывода, перенесите его в запрос набора данных отдельным полем. Тогда вычисляемое поле с функцией станет не нужно.
💡 В выражениях вычисляемых полей СКД тоже работает ЕстьNULL(). Обернуть в неё аргумент функции — быстрый способ проверить догадку, прежде чем переписывать отчёт.
Почему ошибка появилась после обновления или установки расширения
Сценарий частый: вчера документ проводился, сегодня после обновления релиза — ошибка. Код типовой конфигурации поставщик проверяет, поэтому подозреваемых обычно три.
| Что изменилось | Как проявляется | Что проверить |
|---|---|---|
| Расширение написано под старый релиз | ошибка в модуле расширения | подробный текст: имя модуля и расширения в стеке |
| Реквизит сменил тип на составной или переехал в табличную часть | старый код берёт реквизит через точку и получает не то | структуру объекта в новом релизе |
| Внешняя печатная форма или обработка | ошибка только при печати или заполнении | отключить обработку и повторить действие |
Для пользователя без доступа к Конфигуратору:
- Запишите полный текст ошибки вместе со строкой
{...}из подробностей. - Найдите список расширений. В типовых конфигурациях на БСП он открывается из раздела
Администрирование → Печатные формы, отчеты и обработки. - Посмотрите, нет ли в стеке ошибки имени расширения. Если есть — временно снимите у него флажок «Активно» и перезапустите сеанс 1С.
- Если ошибка ушла — обратитесь к автору расширения за версией под новый релиз.
Отключённое расширение перестаёт работать у всех пользователей базы, а изменение применяется после перезапуска сеансов. Делайте это вне рабочего пика и предупредите коллег.
Для разработчика расширения. Методы с аннотацией &Вместо переписывают типовую процедуру целиком. После обновления типовая процедура могла начать заполнять переменную по-другому, а ваша копия живёт по старым правилам. Сравните тело метода расширения с актуальным кодом типовой конфигурации. Там, где хватает, замените &Вместо на &Перед, &После или &ИзменениеИКонтроль — последний вариант сам подсветит расхождение при изменении исходного метода.
Расширения конфигурации
появились в платформе 8.3.6, аннотация &ИзменениеИКонтроль — в более поздних версиях. Если база работает на старой платформе, проверьте номер в справке О программе.
Если исправить не получилось
Бывает, что код правильный, а ошибка всё равно возникает на конкретных документах.
- 🧾 Данные с «битыми» значениями. После неудачного обмена или ручной загрузки в реквизит составного типа могло попасть значение другого типа. Найдите такие объекты запросом с отбором
ТИПЗНАЧЕНИЯ(Поле)и исправьте их. - 🔄 Кэш конфигурации на клиенте. После обновления у отдельных пользователей встречаются странные ошибки старого кода. Запустите 1С с параметром
/ClearCache— это сбрасывает локальный кэш клиента и не трогает данные. - 🗂️ Разные версии у пользователей. В клиент-серверном варианте проверьте, что обновление конфигурации базы данных применено, а не только загружено в основную конфигурацию.
Если ни одна гипотеза не подтвердилась, отправьте разработчику три вещи: полный текст ошибки со стеком, ссылку или номер документа, на котором она повторяется, и последовательность действий.
Как не допускать таких ошибок при разработке
Ошибка ловится заранее, если код не доверяет пустым значениям.
Ещё несколько правил из практики:
- Получайте реквизиты ссылок запросом, а не цепочкой точек в цикле. Меньше обращений к базе и меньше мест для
NULL. - Функция, которая ищет, должна явно возвращать понятное пустое значение: пустую ссылку, ноль или
Неопределенос проверкой у вызывающего. - Для составных типов держите одну проверку
ТипЗнчв начале обработки, а не разбросанные по коду обращения. - Включайте
Отладка → Остановка по ошибкепри тестировании, а не только при поиске бага: так видно ошибки, которые съедаетПопытка. - Проверяйте доработки на пустых документах. Создайте документ, заполните только обязательные поля и проведите — сразу всплывут обращения к незаполненным реквизитам.
- В расширениях предпочитайте
&Переди&Послевместо&Вместо: меньше кода, который устареет с новым релизом.
Частые вопросы
Почему ошибка возникает у одного пользователя, а у другого нет?
Помогает ли тестирование и исправление информационной базы?
Чем ЗначениеЗаполнено отличается от проверки на Неопределено?
ЗначениеЗаполнено() возвращает Ложь и для Неопределено, и для NULL, и для пустой ссылки, ноля, пустой строки. Проверка = Неопределено ловит только одно значение. Для защиты от нашей ошибки удобнее первая.