Если вы встречали в коде конструкции типа
Справочники.Номенклатура.НайтиПоНаименованию("Большая круглая фиговина 1мм");
Документы.РеализацияТоваровУслуг.НайтиПоКоду("00000000001");
то вы, возможно, вкушали последствия использования таких "образцов идеального кода".
Лично я не один и даже не десять раз наталкивался на последствия хардкодинга в виде использования функций НайтиПоКоду(), НайтиПоНаименованию() и НайтиПоНомеру() с зашитыми в коде значениями. Последствия всегда плачевные, их диапазон весьма широк - недоумение "почему оно не работает" или "почему в документе пустые реквизиты, вчера всё работало", необходимость куда-то лезть и менять прописанные значения, красные глаза от поиска в километрах кода той самой заветной строчки, а иногда и возможность интересно провести ночь за удалением/исправлением неправильно перенесённых/сгенерированных данных.
Всю гамму чувств от таких последствий не передать, могу только понадеяться, что для программистов, которые пишут код, не задумываясь о последствиях, есть отдельный котёл в аду.
Лучше напишу о том, как избежать использования этих конструкций в коде.
Первый способ,
который подсказывает Капитан Очевидность - это использование предопределённых элементов. Если вам в коде нужно сослаться на валюту Рубль и ни на какую другую - создайте предопределённый Рубль в справочнике "Валюты". На этом способе даже не останавливаюсь, потому что он, надеюсь, понятен всем. Минус такого способа в том, что он не применим в базе, которая уже наполнена данными. Т.е. если в базе уже есть непредопределённый Рубль, то вы же не будете добавлять предопределённый и перебивать все ссылки в базе с одного Рубля на другой? Очень надеюсь, что такая мысль не придёт вам в голову.
Второй способ.
Для версий ниже 8.3. Долгое время и весьма успешно для искоренения НайтиПо...() из написанного до меня кода я использовал ПланВидовХарактеристик и РегистрСведений.
Допустим, какой-то нехороший человек до вас в коде написал:
Документ.Валюта = Справочники.Валюты.НайтиПоНаименованию("Рубль");
Тогда вы берёте и, не переставая молиться за спасение души этого человека, создаёте ПланВидовХарактеристик "СлужебныеПеременные" (типы значений выбираете в нашем случае справочник "Валюты") и РегистрСведений "ЗначенияСлужебныхПеременных" (непериодический, неподчинённый) с измерениями "Переменная" (тип ПВХСсылка.СлужебныеПеременные) и "Значение" (тип Характеристика.СлужебныеПеременные). Дальше в ПВХ создаёте предопределённый элемент Валюта_Рубль, а в РС запись - Валюта_Рубль = Рубль (выбираете из справочника "Валюты"). После этого идёте в код и пишете
//Документ.Валюта = Справочники.Валюты.НайтиПоНаименованию("Рубль");
Документ.Валюта = ПолучитьЗначениеСлужебнойПеременной(ПредопределенноеЗначение("ПланВидовХарактеристик.СлужебныеПеременные.Валюта_Рубль"));
// функция по получению значения служебной переменной
Функция ПолучитьЗначениеСлужебнойПеременной(Переменная, ЗначениеПоУмолчанию = Неопределено) Экспорт
// вернём, если ничего не найдём
прВернуть = ЗначениеПоУмолчанию;
// в запросе получаем значение переменной
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ЗначенияСлужебныхПеременных.Значение
|ИЗ
| РегистрСведений.ЗначенияСлужебныхПеременных КАК ЗначенияСлужебныхПеременных
|ГДЕ
| ЗначенияСлужебныхПеременных.Переменная = &Переменная";
Запрос.УстановитьПараметр("Переменная", Переменная);
Результат = Запрос.Выполнить().Выбрать();
// если запрос что-то вернул, то это и есть значение переменной
Если Результат.Следующий() Тогда
прВернуть = Результат.Значение;
КонецЕсли;
Возврат прВернуть;
КонецФункции
Вот и всё. Теперь вы всегда знаете, что ваши механизмы не рассыпятся как карточные домики от изменения пользователем какого-нибудь нолика где-то в дебрях справочников-документов.
Плюс такого подхода - это совершенно отдельно стоящий механизм, который не влияет на типовые механизмы и не затрагивает типовые объекты метаданных.
Минус - да, должен быть заполнен регистр сведений, без него никуда. Здесь можно посоветовать при начале работы системы под админскими правами сверять количество служебных переменных в плане видов характеристик и количество записей в регистре "Значения служебных переменных" и, если какие-то переменные не заполнены - выдавать что-нибудь грозное и устрашающее, пусть заполняют. Ещё можно создать обработку автоматического заполнения регистра. Как заполнять? А с помощью тех самых ненавистных мне НайтиПоКоду("000001") - а что делать, ради такого благого дела можно их и потерпеть ещё разик :) Для особо пугливых можно просто использовать этот механизм как основной, а НайтиПо...() не удалять, а оставить как дублирующий, на всякий случай.
Третий способ.
Для версий от 8.3 - неожиданное продолжение первого способа с помощью новых технологий.
В платформе 8.3 появилась интересная возможность переназначать, удалять и всячески извращаться над предопределёнными элементами. Т.е. тот самый Рубль, который непредопределённый, можно просто сделать предопределённым! Не знаю как вы, я вначале не поверил, когда узнал, что так можно. Но оказалось, что да, действительно, докатились, теперь и в мире 1С нет ничего постоянного и надёжного. Но в данном случае это даже хорошо - мы можем, обойдясь подножными средствами, отделаться от НайтиПо...()
Итак, что нужно?
Идём в конфигуратор, открываем нужный объект метаданных (наш справочник "Валюты"), идём в предопределённые, добавляем новый предопределённый Рубль, обязательно задаём ему вручную какой-нибудь незанятый код.
Теперь создаём новую обработку, в ней форму, в форме создаём процедуру ПриСозданииНаСервере(), там пишем
&НаСервере
Процедура ПриСозданииНаСервере(Отказ)
// лишаем предопределённый рубль его предопределённости
Валюта = Справочники.Валюты.НайтиПоКоду([Код_Предопределённого_Рубля]).ПолучитьОбъект();
Валюта.ИмяПредопределенныхДанных = “”;
Валюта.Записать();
// освободившуюся предопределённость отдаём старому рублю
Валюта = Справочники.Валюты.НайтиПоКоду([Код_НЕ_Предопределённого_Рубля]).ПолучитьОбъект();
Валюта.ИмяПредопределенныхДанных = “Рубль”;
Валюта.Записать();
КонецПроцедуры
После этого удаляем тот Рубль, который стал нам не нужен. Всё, теперь на старый, потёртый, использовавшийся в базе Рубль можно ссылаться в коде как на предопределённый элемент. Плюс такого способа - ничего нигде не надо заполнять, включая любые пустые релизы вашей конфигурации, там всё уже будет по умолчанию. Минус - может возникнуть неразбериха при обновлениях, так же, в отличие от предыдущего способа, не может использоваться как отдельно стоящий механизм, который не затрагивает типовые объекты.
UPD:
Навеяно комментариями к статье.
В комментариях многие предлагают использовать метод ПолучитьСсылку("уид-того-что-нам-надо") с зашитым в тексте программы УИДом. Но, как и любая разновидность Г-кода, хардкодинг (зашивание значений в код) имеет множество недостатков и кажется мне непримлиемой. Фактически предлагается избавиться от одного хардкодинга другим, мотивируя это тем, что, якобы уид ссылки никогда не меняется. На деле же, как и любой Г-код, это - быстрая и дешёвая заплатка, сделанная на коленке.
Во-первых, УИД может поменяться - не УИД той ссылки, которую мы используем, а сам элемент, который мы используем. Например, раньше нам нужен был УИД элемента справочника Типы цен "Розничная", а теперь нужен "Розничная СПБ". Конечно, нужно лезть в код и менять, а до того система будет работать не так, как нужно.
Во-вторых, можно забыть о тестовых базах - не получится просто взять cf от конфигурации и накатить её на демо-базу и вести разработку там, просто перенося изменения в рабочую. Как раз сейчас работаю на проекте, где изначально было две базы - рабочая и тестовая, создавались они не из одного dt, а независимо друг от друга, соответственно, все УИДы разные. Пытаюсь вести разработку в демо-базе, но каждый раз, когда на тыкаюсь на все эти УИДы, не могу не пожелать долгой и мучительной смерти тому, кто всё это рисовал.
В-третьих, строка
ПолучитьЗначениеПеременной(ПредопределенноеЗначение("ПВХ.Переменные.ТипЦены_Розничная"))
явно читабельнее, чем строка
Справочники.ТипыЦен.ПолучитьСсылку("с3фацфмыф-ф23аф23аф0-2й3укфафц0-23к2к")
Кто-то писал, что это исправляется добавлением комментария, но всё это из области фантастики - я в своей работе ни разу не встречал комментарий рядом с УИДом.