Справочники

Справочник — это списки, которые живут в базе месяцами: товары, клиенты, склады, сотрудники.

Справочник — прикладной объект 1С для хранения условно-постоянной информации: списков однотипных сущностей, которые меняются редко и используются многократно.

Представьте магазин. Товары в нём заводят один раз, а потом ссылаются на них в каждой продаже месяцами. Клиенты, поставщики, склады, статьи затрат — всё это заводится заранее и переиспользуется. Именно такие данные и хранят справочники. Их называют условно-постоянными: не константа на всю жизнь, но и не меняется от операции к операции. В типовой «1С:Бухгалтерии» справочников десятки — «Номенклатура», «Контрагенты», «Организации», «Физические лица», «Валюты».

Зачем это нужно на практике

Без справочников пришлось бы в каждом документе продажи заново набирать название товара, его артикул и единицу измерения. Ошиблись в букве — и отчёт «съест» товар как отдельную позицию. Справочник решает это: товар заводится один раз, а везде дальше мы ставим ссылку на элемент справочника. Изменили наименование в одном месте — оно поменялось во всех документах, потому что они хранят не текст, а ссылку.

Код и наименование

У каждого справочника по умолчанию есть два стандартных реквизита: Код и Наименование. Код — короткий идентификатор (часто автонумеруемый: 000000001, 000000002), наименование — то, что видит пользователь. Оба можно настроить: длину кода, тип (числовой или строковый), автонумерацию.

Обратиться к элементу справочника из кода можно через глобальное свойство Справочники — это менеджер, точка входа ко всем справочникам конфигурации:

// Находим товар по коду
Товар = Справочники.Номенклатура.НайтиПоКоду("000000042");

Если ЗначениеЗаполнено(Товар) Тогда
    Сообщить("Нашли: " + Товар.Наименование);
Иначе
    Сообщить("Товар с таким кодом не найден");
КонецЕсли;

Результат:

Нашли: Ноутбук Lenovo IdeaPad

Важный момент: НайтиПоКоду и НайтиПоНаименованию всегда возвращают ссылку. Если ничего не нашлось — вернётся пустая ссылка, а не ошибка. Поэтому результат проверяют через ЗначениеЗаполнено(), а не сравнением с Неопределено.

Реквизиты

Кода и наименования почти всегда мало. Реквизит — это дополнительное поле элемента справочника. У «Номенклатуры» это может быть «Артикул», «ЕдиницаИзмерения», «СтавкаНДС», «Производитель». У «Контрагентов» — «ИНН», «КПП», «ЮридическийАдрес». Реквизиты добавляются в конфигураторе, а из кода читаются как свойства через точку:

Товар = Справочники.Номенклатура.НайтиПоНаименованию("Ноутбук Lenovo IdeaPad");

Сообщить("Артикул: " + Товар.Артикул);
Сообщить("Ставка НДС: " + Товар.СтавкаНДС);

// Проверим обязательное поле перед использованием
Если НЕ ЗначениеЗаполнено(Товар.ЕдиницаИзмерения) Тогда
    Сообщить("У товара не заполнена единица измерения!");
КонецЕсли;

Создание нового элемента

Завести элемент кодом можно так: у менеджера справочника вызываем СоздатьЭлемент(), заполняем реквизиты и вызываем Записать(). Метод Записать() сохраняет объект в базу.

НовыйТовар = Справочники.Номенклатура.СоздатьЭлемент();
НовыйТовар.Наименование = "Мышь беспроводная Logitech";
НовыйТовар.Артикул = "MS-2024";
НовыйТовар.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
НовыйТовар.Записать();

Сообщить("Создан элемент, код присвоен автоматически: " + НовыйТовар.Код);

Здесь всплывает деталь, о которую спотыкаются новички: реквизит СтавкаНДС у нас — значение перечисления, поэтому и присваиваем мы ему не строку "20%", а конкретное значение Перечисления.СтавкиНДС.НДС20. Тип реквизита задаётся в конфигураторе, и код обязан этому типу соответствовать: строке — строку, ссылке на справочник — ссылку, числу — число.

Ссылка и представление

Ссылка на элемент — это внутренний идентификатор, а не читаемый текст. Когда ссылку выводят пользователю (в списке, в поле формы), 1С показывает не её, а представление — обычно наименование. В коде представление можно получить явно, не читая реквизит вручную:

Товар = Справочники.Номенклатура.НайтиПоКоду("000000042");

// Универсальный способ получить читаемый текст по любой ссылке
Текст = Строка(Товар);          // вызовет представление объекта
Сообщить("Представление: " + Текст);

// То же самое даёт стандартная функция
Сообщить(ПредставлениеОбъекта(Товар.Ссылка));

Это удобно в универсальном коде, который работает с разными объектами и не знает заранее, как именно у них называется «главное» поле. Для справочника это, как правило, наименование, но настройкой представления можно управлять.

Табличные части

Иногда одному элементу нужно хранить список вложенных строк. Например, у контрагента может быть несколько банковских счетов, а у товара — несколько штрихкодов. Для этого есть табличная часть — таблица внутри элемента справочника со своим набором колонок.

Контрагент = Справочники.Контрагенты.НайтиПоНаименованию("ООО Ромашка").ПолучитьОбъект();

// Перебираем строки табличной части "БанковскиеСчета"
Для Каждого СтрокаСчёта Из Контрагент.БанковскиеСчета Цикл
    Сообщить(СтрокаСчёта.Банк.Наименование + ": " + СтрокаСчёта.НомерСчёта);
КонецЦикла;

// Добавим новый счёт
НоваяСтрока = Контрагент.БанковскиеСчета.Добавить();
НоваяСтрока.НомерСчёта = "40702810000000012345";
Контрагент.Записать();

Обратите внимание на ПолучитьОбъект(): чтобы менять элемент, ссылки недостаточно — нужен объект. Об этом подробнее ниже.

Иерархия

Справочник можно сделать иерархическим — тогда элементы группируются в папки. «Номенклатуру» удобно разложить по группам «Ноутбуки», «Мыши», «Клавиатуры». В 1С два вида иерархии: иерархия групп и элементов (папки отдельно, товары отдельно) и иерархия элементов (родителем может быть любой элемент). У элемента появляется стандартный реквизит Родитель, а у групп — свойство ЭтоГруппа.

Выборка = Справочники.Номенклатура.Выбрать();

Пока Выборка.Следующий() Цикл
    Если Выборка.ЭтоГруппа Тогда
        Сообщить("Группа: " + Выборка.Наименование);
    Иначе
        Сообщить("  Товар: " + Выборка.Наименование
            + " (в группе " + Выборка.Родитель.Наименование + ")");
    КонецЕсли;
КонецЦикла;

Как это работает

Под капотом каждый справочник — это таблица в СУБД (PostgreSQL, MS SQL и др.), которую платформа создаёт автоматически по описанию из конфигуратора. У каждого элемента есть невидимая уникальная ссылка — GUID из 16 байт. Когда документ «ссылается» на товар, в его поле хранится именно этот GUID, а не текст наименования. Поэтому переименование товара мгновенно отражается всюду — данные-то не дублируются.

Ключевое различие, которое путают новички: ссылка (тип СправочникСсылка.Номенклатура) — это указатель «только для чтения», по нему можно читать реквизиты, но нельзя менять. Объект (тип СправочникОбъект.Номенклатура) — это загруженный в память элемент, который можно править и записывать. Из ссылки объект получают методом ПолучитьОбъект(), обратно — свойством .Ссылка.

Частые ошибки

  • Пытаться изменить реквизит через ссылку. Строка Товар.Артикул = "новый", где Товар — ссылка, вызовет ошибку: у ссылки поля доступны только на чтение. Сначала Объект = Товар.ПолучитьОбъект(), потом правим и Записать().
  • Забыть Записать(). Изменили объект в памяти, но не записали — база осталась прежней. Частая причина «почему не сохранилось».
  • Сравнивать результат поиска с Неопределено. НайтиПоКоду при неудаче возвращает пустую ссылку, а не Неопределено. Правильная проверка — ЗначениеЗаполнено(результат).
  • Искать по наименованию как по уникальному ключу. Наименования могут повторяться, а НайтиПоНаименованию вернёт только первый совпавший элемент. Для гарантированной уникальности используйте код или отдельный реквизит.

Итоги

  • Справочник хранит условно-постоянные списки: товары, контрагенты, склады — заводятся редко, используются часто.
  • Стандартные реквизиты — Код и Наименование; свои поля добавляют как реквизиты, вложенные списки — как табличные части.
  • Поиск: НайтиПоКоду() и НайтиПоНаименованию() возвращают ссылку; проверяйте её через ЗначениеЗаполнено().
  • Ссылка — только чтение; чтобы менять элемент, берите объект через ПолучитьОбъект() и сохраняйте Записать().
  • Иерархия превращает плоский список в дерево групп; признак папки — ЭтоГруппа, родитель — реквизит Родитель.
Проверьте себя
1. Что вернёт Справочники.Номенклатура.НайтиПоКоду("999"), если товара с таким кодом в базе нет?
AЗначение Неопределено
BПустую ссылку на справочник Номенклатура
CОшибку выполнения (исключение)
DПустую строку ""
2. Вы хотите изменить артикул уже существующего товара из кода. С чего начать?
AПрисвоить новое значение прямо через ссылку: Товар.Артикул = "..."
BПолучить объект через Товар.ПолучитьОбъект(), изменить артикул и вызвать Записать()
CУдалить элемент и создать заново через СоздатьЭлемент()
DИзменить наименование — артикул пересчитается сам