Как правильно спроектировать справочник услуг в 1С: перечисление, предопределенные элементы или регистр сведений?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При проектировании конфигураций на платформе 1С:Предприятие 8.3 разработчики часто сталкиваются с классической дилеммой: использовать Перечисление или Справочник для хранения аналитики. С одной стороны, набор элементов может быть небольшим и фиксированным на этапе разработки, что подталкивает к использованию перечислений. С другой стороны, бизнес-требования часто требуют расширения этого набора пользователями в режиме «1С:Предприятие», добавления дополнительных реквизитов (например, для печатных форм) и гибкого управления поведением программы.

В этой статье мы подробно рассмотрим, почему поиск элементов по коду является грубой ошибкой проектирования, разберем архитектурные паттерны решения этой задачи и выясним, как правильно организовать обращение к элементам из программного кода в соответствии со стандартами разработки фирмы «1С».

Почему поиск по коду — это плохая практика?

В исходном обсуждении автор темы упомянул, что использует конструкцию Справочники.ВидыУслуг.НайтиПоКоду("000001"). Давайте разберем, почему так делать категорически не рекомендуется:

  1. Ненадежность данных: Пользователь или администратор в режиме предприятия может случайно или намеренно изменить код элемента справочника. В этот момент весь программный код, завязанный на этот код, перестанет работать.
  2. Проблемы при переносе данных: При обмене данными, распределенных базах (РИБ) или переносе конфигурации коды могут дублироваться, перезаписываться или смещаться.
  3. Сложность поддержки кода: Магические строки вида "000001" ни о чем не говорят разработчику, который будет сопровождать ваш код в будущем. Приходится постоянно открывать базу данных и сверять, какая именно услуга скрывается под этим кодом.

Выясним, какие альтернативные варианты проектирования существуют и как их правильно реализовать.

Вариант 1. Использование предопределенных элементов справочника

Если набор базовых услуг (например, «Электроснабжение», «Водоснабжение», «Отопление») известен заранее, но пользователям необходимо дать возможность добавлять свои собственные услуги и настраивать их свойства, лучшим решением будет использование предопределенных элементов справочника.

Предопределенные элементы создаются в конфигураторе. Платформа гарантирует их наличие в базе данных, связывает их по уникальному идентификатору (GUID) и позволяет напрямую обращаться к ним из программного кода по имени, заданному в конфигураторе.

Рассмотрим, как правильно обращаться к таким элементам. Прямое обращение через точку от менеджера справочника выглядит следующим образом:

СсылкаНаУслугу = Справочники.ВидыУслуг.Электроснабжение;

Однако при разработке в современных конфигурациях на базе Библиотеки стандартных подсистем (БСП) или при работе в распределенных информационных базах (РИБ) прямой вызов может привести к ошибкам, если элемент еще не создался физически в базе данных (например, при первом запуске). Поэтому стандарты разработки рекомендуют использовать безопасный метод получения предопределенных элементов:


// Получение предопределенного элемента через БСП
СсылкаНаУслугу = ОбщегоНазначения.ПредопределенныйЭлемент("Справочник.ВидыУслуг.Электроснабжение");

Если ЗначениеЗаполнено(СсылкаНаУслугу) Тогда
    // Выполняем логику расчета для электроснабжения
КонецЕсли;

Использование предопределенных элементов позволяет совместить жесткую привязку логики в коде с возможностью расширения справочника пользователями.

Вариант 2. Архитектурный паттерн «Стратегия» (Разделение услуги и алгоритма)

Попытка завязать конкретный алгоритм расчета прямо на элемент справочника (даже предопределенный) — это классическая архитектурная ошибка. Представим ситуацию: у вас есть услуга «Электроснабжение», расчет которой идет по показаниям приборов учета. Завтра пользователям потребуется завести услугу «Электроснабжение (день)» и «Электроснабжение (ночь)». Если ваш код жестко завязан на элемент «Электроснабжение», вам придется переписывать конфигурацию.

Чтобы избежать этого, применим паттерн «Стратегия»:

  1. Создадим Перечисление `СпособыРасчетаУслуг` со значениями: ПоПоказаниямПриборов, ПоПлощадиПомещения, ФиксированнойСуммой. Это перечисление определяет правила игры и алгоритмы, оно меняется только разработчиком.
  2. В Справочнике `ВидыУслуг` создадим реквизит СпособРасчета с типом ссылки на наше перечисление.
  3. В программном коде будем анализировать не саму услугу, а установленный для нее способ расчета.

Проанализируем пример реализации такого подхода в модуле расчета:


Процедура РассчитатьСтоимостьУслуги(СтрокаДокумента)
    
    ТекущаяУслуга = СтрокаДокумента.Услуга;
    
    // Проверяем способ расчета, а не конкретную ссылку на справочник
    Если ТекущаяУслуга.СпособРасчета = Перечисления.СпособыРасчетаУслуг.ПоПоказаниямПриборов Тогда
        ВыполнитьРасчетПоПриборамУчета(СтрокаДокумента);
    ИначеЕсли ТекущаяУслуга.СпособРасчета = Перечисления.СпособыРасчетаУслуг.ПоПлощадиПомещения Тогда
        ВыполнитьРасчетПоПлощади(СтрокаДокумента);
    КонецЕсли;
    
КонецПроцедуры

При таком подходе пользователи могут плодить десятки видов услуг с разными наименованиями и свойствами для отчетов. Главное — они укажут правильный способ расчета, и система отработает корректно без вмешательства программиста.

Вариант 3. Использование регистра сведений для связи (настройка «на лету»)

Если вам категорически нельзя менять конфигурацию (добавлять предопределенные элементы или реквизиты), либо если в разных организациях одной базы правила соответствия услуг должны быть разными, рассмотрим вариант с использованием Регистра сведений.

Создадим регистр сведений СоответствиеВидовУслуг со следующей структурой:

В режиме «1С:Предприятие» пользователь сам один раз сопоставляет системную роль услуги с конкретным элементом своего справочника. В коде мы обращаемся к регистру, чтобы получить нужную ссылку:


Функция ПолучитьСсылкуНаУслугуПоРоли(СистемнаяРоль) Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	СоответствиеВидовУслуг.ВидУслуги КАК ВидУслуги
        |ИЗ
        |	РегистрСведений.СоответствиеВидовУслуг КАК СоответствиеВидовУслуг
        |ГДЕ
        |	СоответствиеВидовУслуг.ИдентификаторУслуги = &СистемнаяРоль";
    
    Запрос.УстановитьПараметр("СистемнаяРоль", СистемнаяРоль);
    УстановитьПривилегированныйРежим(Истина);
    РезультатЗапроса = Запрос.Выполнить();
    УстановитьПривилегированныйРежим(Ложь);
    
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.ВидУслуги;
    КонецЕсли;
    
    Возврат Справочники.ВидыУслуг.ПустаяСсылка();
    
КонецФункции

Этот способ является максимально гибким. Он полностью исключает жесткое кодирование конкретных элементов справочника и переносит ответственность за сопоставление на администратора системы.

Рекомендации по реализации дополнительных реквизитов

Автор темы упомянул необходимость «подвесить дополнительные поля» к услугам, например, написание в различных отчетах или макеты печати. Разберем, как сделать это красиво:

Резюме: как сделать правильно?

Подведем итог нашего анализа. Выбор архитектурного решения зависит от сложности системы:

  1. Если логика завязана на физический смысл услуги, но состав справочника относительно стабилен — используйте Справочник с предопределенными элементами. Доступ к ним в коде осуществляйте через безопасные методы БСП.
  2. Если услуги могут масштабироваться, а алгоритмы расчета типовые — используйте Справочник + Перечисление способов расчета (паттерн «Стратегия»). Это наиболее профессиональный и масштабируемый подход.
  3. Если база мультиклиентская (обслуживает разные организации с уникальными справочниками) или конфигурация находится на поддержке — используйте сопоставление через Регистр сведений.
← На главную