Иногда при работе с несколькими расширениями можно с удивлением обнаружить неочевидное поведение, которое или не было описано в документации, или описано значительно позже момента, когда большинство специалистов ознакомились с технологией и сейчас ошибочно считают свои знания актуальными.
Типы расширений и порядок применения
Те, кто изучал расширения в первых версиях, возможно, будут удивлены факту, что 1С ввело классификацию по ожидаемому применению:
- Исправление - как понятно из названия, для исправления ошибок и временных правок, которые со временем должны удаляться;
- Адаптация - для доработок, которые на постоянной основе изменяют базовую логики работы (желательно только одно подобное на всю базу);
- Дополнение - для добавления новой функциональности, которая оказывает минимальное влияние на логику системы (например, новые отчеты).
Контроль использования не предусмотрен. Можно в "исправлениях" делать целые подсистемы с документами и регистрами, а в дополнениях вести правки ошибок в ожидании обновлений от вендора. Таким образом, вся ответственность за варианты применения лежит на разработчике расширений, которые, согласно моим наблюдениям на различных проектах, не заморачиваются и оставляют "адаптацию", которая устанавливается по умолчанию.
Пусть такое разделение сильно условно и не охватывает все варианты желаемого применения, но оно дает разработчикам небольшое преимущество - это возможность управления порядком применения расширений. Подключение функционала всегда происходит в строгой последовательности: Исправление, Адаптация и последним Дополнение.
Порядком применения расширений равен порядку в списке расширения. Можно сказать, что список фактически поделен на три сегмента и каждое новое расширение добавляется в конец соответствующего сегмента.
Пример. Пусть в базе есть расширение Адаптация1 и Дополнение1. Тогда расширение Адаптация2 будет добавлено на второе место, а Дополнение1 перейдет со второй позиции на третью. А если добавить Исправление1, то оно будет вставлено первым, а все остальные будут сдвинуты на одну позицию вниз.
К сожалению, на сегодняшний день помимо указания "назначения" не существует других штатных и удобных инструментов изменения порядка подключения расширений, если он по каким-то причинам не устраивает. Единственный доступный способ требует очень значительных усилий:
- Нужно выгрузить в файлы все расширения нужного типа (оставить только те, порядок которых удовлетворяет требованиям)
- Отключить им активность, после чего удалить эти расширения из базы
- Создать расширения-пустышки в нужном количестве (тип не важен)
- Загрузить в пустышки расширения из файлов в требуемом порядке.
Только при выполнении описанной последовательности шагов следует учесть, что если в расширениях были добавлены новые объекты или реквизиты к существующим, то эти данные будут безвозвратно удалены из БД при удалении расширений. Следовательно, сперва нужно создать дополнительное служебное расширение, в котором продублировать все нужные к сохранению метаданные, а потом в режиме предприятия скопировать в них все требуемые данные. После восстановления в базе расширений, нужно будет выполнить обратную операцию и восстановить данные из копий на оригинальные места.
Результат применения порядка подключения расширений
Для визуальных объектов, таких как формы и макеты, результат "расширения" зачастую равен эффекту сравнения-объединения с режимом сравнения по Именам. Поэтому желательно не надеяться на отсутствие конфликтов с другими расширениями и будущими обновлениями основной конфигурации, а безопасно вносить требуемые изменения с помощью добавления и/или расширения кода. В обоих случаях возможны проблемы :)
Добавление кода в расширяемый модуль
В расширенный модуль можно добавлять любой код, но проверка уникальности наименований выполнится только в сравнении с кодом основной конфигурации.
Для примера создадим несколько расширений, расширим в каждое из них форму и поместим в модуль каждой из форм процедуру с одним и тем же именем, но разным содержимым.
Если в основной конфигурации будет эта же процедура, то получим ошибку:
Если же убрать процедуру из основного модуля формы, то все ОК и никаких ошибок. Почему так? Неужели в расширениях возможна изоляция кода?!
Как оказалось, последнее из очереди применяемых к форме расширений оставит только свой вариант реализации метода!
И пусть теперь кто-то посмеет заявлять, что в 1С нет ООП-наследования :)
Важно. Даже если кажется, что ваши процедуры и функции из расширений на 100% уникальны и точно нигде не могут повториться, все равно сделайте лишнее движение и добавьте к названию метода префикс расширения - минус к красоте кода, но плюс к его предсказуемости!
Расширение существующего кода
У процедур как изолированного места для выполнения кода и у возвращающих значение функций из-за указанных различий есть отличия в механике их расширения.
Для процедур доступны четыре режима: "Вызвать перед", "Вызвать после", "Вызвать вместо" и "Вызвать вместо (с контролем)". Для функций доступны только два последних варианта: "Вызвать вместо" и "Вызвать вместо (с контролем)".
С уникальными вариантами вставки кода в начало и конец процедур все понятно и нужно только учитывать, что в точки "перед всем кодом" и "после всего кода" вставляют все расширения по порядку из списка расширений. Т.е. для второго по порядку расширения будет добавление кода в конец ПОСЛЕ кода первого расширения, но в начале добавление уже ПЕРЕД основным кодом и кодом первого расширения:
Режимы "Вызвать перед", "Вызвать после" максимально безопасны в применении, а вот со вторыми двумя нужно быть очень аккуратными!
Если расширять процедуры или функции с помощью "Вызвать вместо (с контролем)" в более чем в одном расширении, то будет применяться только первое из списка, а остальные будут проигнорированы, а пользователю будет выдано уведомление в нижнем правом углу (где все время пишут про загрузку курсов валют, обновление классификаторов, про необходимость настроить бэкапирование и прочий спам, к которому все давно привыкли и не читают):
<ИмяРасширения>: Обычная: Ошибка применения модуля "<ИмяРасширения> <ИмяМодуля>". Аннотация "ИзменениеИКонтроль" уже применена к методу "<ИмяМетода>"
Реакция на множественное расширение в режиме "Вызвать вместо" будет с точностью до наоборот - будет вызван код не из первого, а только из последнего по порядку расширения, где с помощью вызова метода ПродолжитьВызов() можно подняться сквозь расширения до основного кода:
Важно. Если применять режим расширения методов "Вызвать вместо" и забыть про возвратный вызов ПродолжитьВызов(), то выполнится код только из самого последнего расширения из списка. Если же используется режим "Вызвать вместо (с контролем)", то выполняться будет только код из первого расширения!
Различная реализация для клиента и сервера
Какой режим устанавливается для новых расширений по умолчанию и какой режим не сменит ваш сисадмин, которому поручат установить в базу новое расширение? Верно - "Безопасный режим".
Особенностью расширения методов в безопасном режиме является то, что клиентские модули продолжают работать штатно, а весь серверный код расширений (за некоторыми исключениями) просто игнорируются. В конфигураторе никакие проверки об этом не скажут. И только при попытке непосредственного вызова пользователю выдаются сообщения в "спамерском уголке":
<ИмяРасширения>: Обычная: Ошибка расширения модуля <ИмяМодуля>': расширение модуля запрещено из-за того, что расширение <ИмяРасширения>' подключено в безопасном режиме
но в то же время:
Важно. Если в вашем расширении есть любые серверные методы, кроме обработчиков событий формы (почему-то на них ограничения безопасного режима не работают), то желательно организовать проверку, что расширение работает в нормальном, а не в ограниченном "безопасном" режиме.
Итоги
Работая с расширениями, я сталкивался с различными проблемами - неотключаемые права на непосредственное удаление у справочников из расширения, переставшие работать команды для расширенных объектов и еще несколько подобных мелочей. Но баги на то и баги, что их исправляют, и они больше не доставляют неприятностей. Я же хотел поговорить не про баги, а про документированные фичи.
Если у вас есть ряд расширений, которые добавляют новые элементы формы и расширение по управлению статусами документа, которая должна сделать "только чтение" на все реквизиты кроме, скажем, Комментария. Разделение по типам "использования" тут идеально подойдет. Если все расширения по умолчанию на "адаптации", то расширение со статусами делаем "дополнением" - хотя это с точность до наоборот к рекомендациям от 1С, но зато сразу работает, и не нужно во все расширения копировать знание про существование статусов.
Если после добавления расширений от третьих лиц код из вашего расширения внезапно выборочно перестал работать, то делаем поиск по всем расширениям имени проблемного расширяемого метода и смотрим на то, чтобы в новых расширениях не использовали аннотацию &ИзменениеИКонтроль или второй вариант - аннотацию &Вместо без возвратного ПродолжитьВызов().
А вот опасность безопасного режима в том, что вы о проблемах можете даже не догадываться, если ваша правка серверной функции заключается в переопределении значений возвращаемых структур и при этом расширенные клиентские процедуры будут все так же давать нужный визуальный эффект, то догадаться о причинах проблемы будет очень сложно. Получаем классическое "а на моем компьютере работает".
И если в файловой версии с галочкой безопасного режима еще все просто, то в серверной версии намного хуже с их профилями безопасности, где на уровне кластера серверов нужно текстом прописывать имена модулей, в которых разрешены/запрещены расширения серверных методов. Ведь с одной стороны в десятках баз на кластере могут совпадать имена модулей (какие-то нужно расширять, какие-то нет), а с другой стороны общие модули имеют свойство менять названия - сомневаюсь, что этим широко пользуются (разве что от безнадеги):