1C-Code → Основы 1С → Справочники: списки того, что существует
Первый объект, который заводит любой разработчик. Ссылка и объект, реквизиты и предопределённые элементы, иерархия и владелец.
Глава 17 из 36 · чтение примерно 26 минут
Справочник отвечает на вопрос «что существует». Товары, контрагенты, склады, сотрудники, валюты — всё это списки, которые живут независимо от того, происходило сегодня что-нибудь или нет.
С него начинают почти всякую задачу: «нам нужно учитывать договоры» — значит, скорее всего, нужен справочник.
Заводя справочник, вы не начинаете с пустого места. Платформа сразу даёт набор полей, которые есть у всех справочников:
Код — короткий идентификатор, обычно нумеруется сам. Наименование — то, что человек видит в списке. ПометкаУдаления — признак «помечен на удаление». Ссылка — то, чем на элемент ссылаются отовсюду.
Длину кода и наименования задают в свойствах. Ноль в длине кода означает «кода нет вовсе» — так делают, когда нумерация не нужна.
Ссылка и объект — разные вещи
Это различие путают чаще всего, и из него растёт половина ошибок первого месяца.
Ссылка — это указание «вот тот элемент». Она лёгкая, её кладут в реквизиты документов, передают, сравнивают. Читать через неё можно: Товар.Артикул.
Объект — это сам элемент, взятый для изменения. Получают его отдельно: Товар.ПолучитьОбъект(). Только у объекта есть Записать().
Попытка написать Товар.Наименование = "Новое" прямо по ссылке — самая частая ошибка новичка. Ссылка на запись не рассчитана.
Название = Товар.Наименование;
Объект = Товар.ПолучитьОбъект();
Объект.Наименование = "Новое";
Объект.Записать();
| Часть | Что означает |
|---|---|
Товар.Наименование | чтение по ссылке — так можно и нужно |
ПолучитьОбъект() | берём элемент для изменения; это поход в базу |
Объект.Наименование = | меняем поле объекта, а не ссылки |
Записать() | без него изменения останутся только в памяти |
Три способа, и выбор между ними — вопрос не вкуса, а скорости.
НайтиПоКоду и НайтиПоНаименованию — просты и привычны. Возвращают пустую ссылку, если не нашли: проверять надо через ЗначениеЗаполнено().
НайтиПоРеквизиту — по любому своему полю, например по артикулу или ИНН.
Запрос — когда искать надо много раз или по условию сложнее одного поля. В цикле «найти по наименованию» на тысяче строк даёт тысячу походов в базу; один запрос — один поход.
На чём спотыкаются
Ищут по наименованию там, где нужен код. Наименования повторяются: «Гвозди» может быть два. НайтиПоНаименованию вернёт первый попавшийся, и ошибка всплывёт через месяц.
Забывают проверить, что нашлось. Не найдено — вернётся пустая ссылка, а не Неопределено. Обращение к её реквизиту не упадёт, а молча вернёт пустоту: ошибка уедет дальше по коду.
Ставят НайтиПоНаименованию в цикл. Классическая причина «обработка идёт сорок минут». Один запрос вместо тысячи поисков.
Иногда элемент нужен коду по имени: «основной склад», «наша организация», «ставка НДС 20%». Такие заводят предопределёнными — прямо в конфигураторе, и они попадают в базу при первом запуске.
Обращаются к ним по имени: Справочники.Склады.Основной. Это надёжнее поиска по наименованию: наименование пользователь может переименовать, а имя предопределённого — нет.
Ловушка предопределённых
Пользователь может пометить предопределённый элемент на удаление — платформа не запрещает. Код, который на него рассчитывает, после этого работает с помеченным элементом и ничего не замечает.
Второе: удалить предопределённый элемент из конфигурации и оставить в базе — обычное дело при обновлении. Тогда обращение по имени упадёт. Отсюда правило: предопределённых заводят мало и только тех, без которых код не работает вовсе.
Два способа связать элементы, и их постоянно путают.
Иерархия — папки внутри того же справочника. Номенклатура лежит в группах «Инструмент», «Крепёж». Включается свойством «Иерархический». Бывает двух видов: «групп и элементов» (папка не товар) и «элементов» (любой элемент может быть родителем).
Владелец — связь с другим справочником. Договоры принадлежат контрагенту: у справочника «ДоговорыКонтрагентов» владелец — «Контрагенты». Элемент без владельца там не заводится.
Простое правило: «внутри себя» — иерархия, «принадлежит чужому» — владелец.
У иерархического справочника строки бывают двух сортов, и лежат они в одной таблице. Отличает их флаг ЭтоГруппа.
Отсюда ошибка, которая встречается в каждом втором отчёте: в выборку попадают группы. «Список товаров» показывает папки вперемешку с товарами, а «сумма по всем позициям» считает их дважды — если в группе есть свои реквизиты.
Поэтому в запросах по иерархическим справочникам почти всегда стоит ГДЕ НЕ ЭтоГруппа. В демо-базе групп две — а в рабочей их бывает больше, чем элементов.
| Вид | Кто кому подчинён | Пример |
|---|---|---|
| Иерархия групп и элементов | элемент лежит в группе, группа — в группе | Номенклатура: папка «Крепёж» и в ней гвозди |
| Иерархия элементов | элемент подчинён элементу, групп нет | Подразделения: отдел внутри отдела |
| Подчинение (владелец) | справочник подчинён ДРУГОМУ справочнику | Договоры подчинены Контрагентам |
Владелец — это не родитель
Два похожих поля, и путают их постоянно.
Родитель — это иерархия внутри того же справочника: группа, в которой лежит элемент.
Владелец — это другой справочник: договор принадлежит контрагенту, и без контрагента договора не существует.
Практическое следствие: искать договор по наименованию без указания владельца бессмысленно — «Основной договор» есть у половины контрагентов. Поэтому у подчинённого справочника ищут парой: владелец плюс имя.
Вопрос «все товары этой группы, включая вложенные» решается не рекурсией, а одним словом в условии.
ГДЕ Ссылка В ИЕРАРХИИ (&Группа) — и платформа сама спустится по всем уровням. Обходить подгруппы кодом, как это делают по незнанию, и дольше, и ошибётся на глубоких деревьях.
Для обратного вопроса — «в какой группе лежит этот элемент» — есть поле Родитель; чтобы подняться до самого верха, в типовых есть готовые функции, а в запросе — соединение с собой по родителю.
У справочника есть свои реквизиты — и с ними связана самая дорогая ошибка новичка в работе с данными.
Ссылка.Реквизит — это обращение к базе. Одно на каждый реквизит, каждый раз. В цикле по тысяче элементов это тысяча запросов, и выглядит она как невинная точка в коде.
В запросе точка ведёт себя иначе: Товар.Производитель.Страна платформа превращает в соединения — это дёшево, но левые соединения, и на пустой ссылке даст NULL. Поэтому в отчётах точку в запросе используют смело, а в коде — осторожно.
Упражнение 2. Только элементы, без групп
Напишите ТолькоТовары(): сколько в справочнике Номенклатура строк, которые не являются группами. Всего строк 25, групп из них две.
Решается в тренажёре: код запускается и проверяется сразу.
Кнопка «удалить» в 1С ничего не удаляет: она ставит пометку. Строка остаётся в базе, её видно зачёркнутой, и все ссылки на неё продолжают работать.
Так сделано не из осторожности, а из необходимости: на элемент могут ссылаться документы за десять лет. Удалить его молча — значит оставить в этих документах «битую ссылку», которую невозможно прочитать.
Настоящее удаление делает отдельная обработка — «Удаление помеченных объектов». Она сначала ищет ссылки и удаляет только то, на что никто не ссылается.
Из этого следует практическое: в запросах почти всегда нужно ГДЕ НЕ ПометкаУдаления. Иначе в отчёт попадут вычеркнутые элементы, и это заметит бухгалтер, а не вы.
Битая ссылка: как выглядит и откуда берётся
Если удалить объект в обход проверок — например, прямым удалением из кода, — ссылки на него останутся. Выглядит это в интерфейсе как «<Объект не найден> (12:8a3f…)».
Чинится только поиском и заменой ссылок, а по-хорошему не допускается вовсе: Объект.Удалить() в коде применяют осознанно и редко, а обычный путь — УстановитьПометкуУдаления(Истина).
Вопрос «чем пометка удаления отличается от удаления» задают на каждом втором собеседовании, и правильный ответ начинается со ссылочной целостности, а не с «одно зачёркивает, другое стирает».
Упражнение 1. Найти и прочитать
Функция АртикулТовара(Наименование) должна найти товар в справочнике Номенклатура по наименованию и вернуть его артикул. Если товара нет — вернуть строку "не найден".
Решается в тренажёре: код запускается и проверяется сразу.
Справочник — список того, что существует. Ссылка читает, объект меняет: ПолучитьОбъект() и Записать(). Найденное всегда проверяют через ЗначениеЗаполнено(). Поиск в цикле — главная причина медленных обработок. Предопределённые заводят мало и только те, без которых код не работает. Иерархия — внутри себя, владелец — принадлежность чужому справочнику.
Читать про код и писать код — разные умения. В тренажёре к этой главе идут упражнения: вы пишете решение, оно запускается на настоящем интерпретаторе 1С и проверяется сразу, без установки платформы.
← Конфигуратор и дерево метаданных
→ Документы и проведение: что случилось