Хардкод в 1С — это значения, вбитые прямо в код: Справочники.Склады.НайтиПоКоду("000000005"), сравнение с наименованием организации, номер договора в условии. Такие «гвозди» работают до первой перенумерации или переименования, после чего алгоритм молча ломается. Решение — вынести ключевые значения в данные: в предопределённые элементы, справочник настроек или регистр соответствий, к которому код обращается по осмысленному ключу.

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

Почему «гвозди» в коде 1С ломаются

Типичная картина при аудите доработок:

1С
// Так делать не нужно
СкладБрака = Справочники.Склады.НайтиПоКоду("000000005");
Если Документ.Организация.Наименование = "ООО Ромашка" Тогда
	// особая логика
КонецЕсли;

Что может пойти не так:

  • Перенумерация справочника — код «000000005» теперь у другого склада.
  • Переименование — организацию переименовали, условие перестало срабатывать, ошибки нет.
  • Другая база — в копии или новой базе такого кода нет, НайтиПоКоду возвращает пустую ссылку, и дальше код работает с ней.
  • Непрозрачность — через год никто не помнит, что это за склад и почему именно он.

Худшее здесь — отсутствие ошибки. Алгоритм продолжает работать, но неправильно.

Какие есть способы вместо хардкода

СпособКогда подходитМинусы
КонстантаОдно значение на всю базуНа каждую настройку — объект конфигурации
Предопределённый элементСтабильные значения, которые нужны всегдаНовый элемент — изменение конфигурации; риск дублей при обмене
Справочник «Настройки» с предопределёнными элементамиНабор ссылок, заполняемых пользователемТоже требует обновления конфигурации на каждый новый ключ
Регистр сведений с ключамиМного настроек, в том числе в разрезе объектовНужна дисциплина в именовании ключей
Функциональная опцияВключение и выключение возможностейТолько для управления функциональностью, не для хранения ссылок

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

1С
ВидЦеныЗакупочная = ОбщегоНазначения.ПредопределенныйЭлемент("Справочник.ВидыЦен.Закупочная");
Если ВидЦеныЗакупочная = Неопределено Тогда
	ВызватьИсключение НСтр("ru = 'Не найден предопределённый вид цены «Закупочная».'");
КонецЕсли;

Вариант 1. Справочник «Настройки» с предопределёнными элементами

Простой и наглядный способ. Создаётся справочник, например НастройкиАлгоритмов, с реквизитами:

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

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

1С
СкладБрака = Справочники.НастройкиАлгоритмов.СкладБрака.Значение;
Если НЕ ЗначениеЗаполнено(СкладБрака) Тогда
	ВызватьИсключение НСтр("ru = 'Не заполнена настройка «Склад брака».'");
КонецЕсли;

Плюсы: всё видно в одном списке, у каждого значения есть пояснение. Минус — каждая новая настройка требует изменения конфигурации. Для небольших самописных систем этого достаточно.

Вариант 2. Регистр соответствий с составным ключом

Когда настроек много или они зависят от объекта (организации, подразделения, вида операции), удобнее регистр сведений. Новая настройка добавляется записью, а не изменением метаданных.

Хардкод в 1С: регистр соответствий хранит значения по составному ключу
Хардкод в 1С: регистр соответствий хранит значения по составному ключу

Структура регистра

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

ПолеВидТипНазначение
ПервичныйКлючИзмерениеСтрока(100)Назначение настройки: «СкладБрака», «Директор»
ВторичныйКлючИзмерениеСтрока, Число, ЛюбаяСсылкаРазрез: организация, подразделение; может быть пустым
ЗначениеРесурсЛюбаяСсылка, Строка, Число, Булево, ДатаСамо значение
ОписаниеРеквизитСтрока(200)Где и зачем используется

Строковое измерение должно быть ограниченной длины — платформа не позволяет использовать в измерениях строки неограниченной длины.

Зачем вторичный ключ: без него ключи быстро превращаются в «ДиректорРомашка», «ДиректорВасилек», «ГлавбухРомашка». С составным ключом первичный — «Директор», вторичный — ссылка на организацию. И можно одним запросом получить все записи по первичному ключу, не зная заранее список организаций.

Методы в модуле менеджера

1С
#Область ПрограммныйИнтерфейс

// Возвращает значение настройки по ключам.
//
// Параметры:
//  ПервичныйКлюч - Строка - назначение настройки.
//  ВторичныйКлюч - Произвольный - разрез настройки, Неопределено - без разреза.
//  ДатаСреза     - Дата - дата, на которую нужно значение; по умолчанию текущая дата сеанса.
//
// Возвращаемое значение:
//  Произвольный - значение настройки или Неопределено, если запись не найдена.
//
Функция Значение(ПервичныйКлюч, ВторичныйКлюч = Неопределено, ДатаСреза = Неопределено) Экспорт

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

	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Количество() > 1 Тогда
		ВызватьИсключение СтрШаблон(
			НСтр("ru = 'Неоднозначная настройка «%1»: найдено несколько записей.'"), ПервичныйКлюч);
	КонецЕсли;

	Возврат ?(Выборка.Следующий(), Выборка.Значение, Неопределено);

КонецФункции

// Проверяет, что все перечисленные первичные ключи заполнены.
//
// Параметры:
//  ПервичныеКлючи - Массив из Строка - ключи, которые нужны алгоритму.
//
// Возвращаемое значение:
//  Массив из Строка - незаполненные ключи; пустой массив, если всё заполнено.
//
Функция НезаполненныеКлючи(ПервичныеКлючи) Экспорт

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

	Заполненные = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("ПервичныйКлюч");

	Результат = Новый Массив;
	Для Каждого Ключ Из ПервичныеКлючи Цикл
		Если Заполненные.Найти(Ключ) = Неопределено Тогда
			Результат.Добавить(Ключ);
		КонецЕсли;
	КонецЦикла;

	Возврат Результат;

КонецФункции

#КонецОбласти

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

Использование в алгоритме

1С
Незаполненные = РегистрыСведений.СоответствияОбъектов.НезаполненныеКлючи(
	СтрРазделить("СкладБрака,ВидЦеныСписания", ","));
Если Незаполненные.Количество() > 0 Тогда
	ВызватьИсключение СтрШаблон(НСтр("ru = 'Не заполнены настройки: %1'"),
		СтрСоединить(Незаполненные, ", "));
КонецЕсли;

СкладБрака = РегистрыСведений.СоответствияОбъектов.Значение("СкладБрака");
Директор   = РегистрыСведений.СоответствияОбъектов.Значение("Директор", Документ.Организация);

Проверка в начале алгоритма даёт понятную ошибку сразу, а не неверный результат через двадцать строк.

Правила, без которых регистр превращается в свалку

  • Ключи — в одном месте. Заведите общий модуль или область с функциями-обёртками: НастройкиАлгоритмов.СкладБрака(). Строковые ключи по всему коду — тот же хардкод, только в другой форме.
  • Описание обязательно. Запись без описания через полгода никто не решится удалить.
  • Права. Изменять регистр должны только администраторы или ответственные; читать — через привилегированный режим или с ролью на чтение.
  • Не храните здесь учётные данные и большие объёмы — для этого есть отдельные механизмы.
  • Перенос между базами. При развёртывании новой базы записи регистра нужно перенести: держите выгрузку или обработку заполнения.

Типичные ошибки

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

Если в доработанной конфигурации «гвоздей» уже десятки, их удобно собрать и вынести за одну итерацию в рамках аудита 1С. Новые алгоритмы сразу проектирую с вынесенными настройками — это стандартная часть доработки 1С.

Итог

  • Хардкод в 1С опасен тем, что ломается без ошибки при перенумерации, переименовании или в другой базе.
  • Для небольшого числа стабильных значений подойдут константы и предопределённые элементы, обращение — через ОбщегоНазначения.ПредопределенныйЭлемент().
  • Для множества настроек в разрезе объектов удобен регистр сведений с первичным и вторичным ключом.
  • Проверяйте заполненность настроек в начале алгоритма и выдавайте понятную ошибку.
  • Строковые ключи держите в одном модуле, иначе хардкод просто переедет в другое место.