- Основные правила
- Ролевая модель доступа
- Стандартные библиотеки и типовой функционал конфигурации
- Использование расширений
- Интеграции и обмены
- Дизайн форм
- Прочее
1.1 Мы применяем базовые стандарты разработки - Система стандартов и методик разработки конфигураций для платформы 1С:Предприятие 8
Мы допускаем расширение базовых стандартов с обязательным их описанием в данном документе.
1.2 Мы используем стиль написания CamelCase для имен процедур, функций, переменных, элементов, метаданных и тд
Неправильно: массРЗ, Заказы_ВыполнитьПроверкуДляСменыСтатусаЗаказа() Правильно: РезультатыЗапроса, ПроверкаПередСменойСтатусаЗаказаКлиента()
1.3 Мы не используем более одного пробела или табуляцию между операндами или параметрами методов в коде
1.4 Мы придерживаемся следующих правил оформления модулей:
- Длинна строки не более 160 символов;
- Не более 1 подряд идущей пустой строки.
1.5 Мы добавляем префикс "_" для всех объектов метаданных собственной разработки. Префикс добавляется только для метаданных верхнего уровня.
1.6 Мы не используем жаргон в наименованиях методов, реквизитов и метаданных
Неправильно: Висяк Правильно: Низколиквидный
1.7 Мы не используем сокращения в наименованиях методов, реквизитов и метаданных, переменных и тд
Неправильно: НомХарка, обДок, Рез, ДК Правильно: ХарактеристикаНоменклатуры, ДокументОбъект, Результат, ДисконтнаяКарта Исключение устоявшаяся терминология, например: ИНН, ОГРН
1.8 Мы не называем имена элементов форм, имена реквизитов, полей запросов, имен временных таблиц запросов, переменных с цифрами
Неправильно: ChangeDC_v4(), Группа2
1.9 Мы не используем в наименованиях отчетов слово "отчет", в наименованиях обработок слово "обработка", в наименованиях справочников "справочник", в наименованиях документов "документ", в наименовании ролей слово "роль" и тд
1.10 Мы не используем общие переменные модуля объекта и модуля формы. Вместо этого используем ДополнительныеСвойства для объекта и реквизиты для формы
Исключение: использование должно быть обосновано невозможностью решить задачу без использования общих переменных либо существенным ухудшением производительности; Требуется обязательное согласование с архитектором
1.11 Мы не используем тип ЛюбаяСсылка или любой ДокументСсылка, любой СправочникСсылка и тд включающий сразу всю ветку метаданных.
Исключение: Используется в универсальном механизме который подключен ко всей ветке метаданных. Требуется обязательное согласование с архитектором.
1.12 Мы не используем небезопасные вызовы Выполнить, вместо этого используем:
- ОбщегоНазначения.ВыполнитьМетодКонфигурации
- ОбщегоНазначения.ВыполнитьВБезопасномРежиме
1.13 Мы не используем небезопасные вызовы Вычислить, вместо этого используем:
- ОбщегоНазначения.ВычислитьВБезопасномРежиме
1.14 В запросах мы всегда именуем вложенные запросы и временные таблицы. Запрещено оставлять имя вложенного запроса по умолчанию "ВложенныйЗапрос"
Рекомендуется для вложенных запросов использовать префикс "Вз", а для временных таблиц "Вт"
1.15 Мы не используем в запросах строковые константы
Пример некорректного запроса:
"ВЫБРАТЬ | Контрагенты.Ссылка КАК Ссылка, | "Я так решил" КАК ВажноеПоле |ИЗ | Справочник.Контрагенты КАК Контрагенты |ГДЕ | Контрагенты.Наименование <> ""ХХХХХХХ"""
1.16 Мы не используем сборку текста запроса путем соединения его частей. Текст запроса должен открываться конструктором запросов.
Пример некорректного запроса:
"ВЫБРАТЬ | Контрагенты.Ссылка КАК Ссылка |ИЗ | Справочник.Контрагенты КАК Контрагенты |ГДЕ | " + ТекстУсловийВыборки;В то же время разрешается соединять текст запроса при условии что каждый метод возвращает целый пакет или набор пакетов запросов. Пример корректного запроса:
Запрос.Текст = ТекстВыборкиНоменклатуры() + ТекстВыборкиОстатковПоСкладам();
1.17 Для изменения текста запроса мы придерживаемся правил: Если необходимо заменить одно условие\поле возможно использование СтрЗаменить, если больше одного условия ПостроительЗапроса, СхемаЗапроса, если требуются сложные условия включающие "или" используется система компоновки данных или СхемаЗапроса
1.18 Мы используем конструктор запроса для написания текстов запроса и исправления существующих запросов
1.19 Мы всегда в явном виде указываем связи между таблицами в запросе, даже в случае декартового произведения (все ко всем)
1.20 Мы не используем вложенные схемы в СКД
1.21 В схеме системы компоновки данных мы не снимаем флаг "Автозаполнение"
1.22 Мы не называем предопределенные варианты отчетов "Основной"
1.23 Мы всегда указываем синоним в реквизите\свойстве метаданных, а не в заголовке элемента формы
Исключение: такое поведение прямо противоречит постановки задачи, например этот реквизит должен называться так только на одной форме.
1.24 Мы используем шаблон для синонимов добавляемых объектов конфигурации верхнего уровня: <Синоним объекта> (название компании)
- Пример: Правила замены символов в артикулах (название компании)
- Исключение: синонимы общих команд должны формироваться без (название компании)
1.25 В обработчиках событий мы делаем только вызовы процедур и функций.
Писать функциональный код запрещается в обработчики:
- ПередЗаписью,
- ПриЗаписи,
- ОбработкаПроведения,
- ОбработкаЗаполнения,
- ОбработкаПроверкиЗаполнения,
- ПриСозданииНаСервере,
- ПриЧтенииНаСервере,
- ПриОткрытии,
- ПередЗаписьюНаСервере,
- ПриЗаписиНаСервере,
- ПослеЗаписиНаСервере,
- ПослеЗаписи
Пример:
&НаСервере Процедура ПриЧтенииНаСервере(ТекущийОбъект) ПрочитатьДоступныеРезультаты(); КонецПроцедуры
1.26 Мы всегда выполняем описание процедур и функций общего назначения
1.27 Мы не вызываем интеграции и длительные операции внутри открытых транзакций. Например запрещена отправка писем, вызов внешних сервисов и тд внутри процедур ПриЗаписи, ОбработкаПроведения
Рекомендуется использовать регистрацию в плане обмена или очереди и отдельное регламентное задание его обработки
1.28 При разработке методов и необходимости передачи параметров типа Структура, ТаблицаЗначений, ДеревоЗначений мы обязуемся соблюдать стандарт ИТС:Особенности использования структур в качестве параметров процедур и функций и разрабатывать функцию конструктор структуры параметра
1.29 Мы придерживаемся принципа единой ответственности методов, то есть метод может выполнять только лишь одно действие отраженное в его наименовании
1.30 В создаваемых формах списка ссылочных документов мы всегда выносим ссылку в табличное поле с наименованием "Ссылка" и отключаем ей пользовательскую видимость по умолчанию
1.31 В регистрах сведений мы всегда снимаем флаг "Использовать стандартные команды"
1.32 Мы используем новые функции работы с асинхронностью вместо устаревших ПоказатьВопрос, ПоказатьВводЗначения, ПоказатьВыборИзСписка, ПоказатьЗначение и тд используем ВопросАсинх, ВвестиЗначениеАсинх, ВыбратьИзСпискаАсинх, ОткрытьЗначениеАсинх и тд
1.33 Мы не используем функцию НСтр
1.34 Мы соблюдаем структуру модулей в соответствии с шаблонами
1.35 Мы не пишем код в одну строку
Неправильно:
Если Переменная = Истина Тогда Возврат; КонецЕсли;Правильно:
Если Переменная = Истина Тогда Возврат; КонецЕсли;
1.36 При копировании кода из конфигурации или библиотеки большей версии мы делаем обязательный комментарий из какой версии скопировано решение
1.37 Мы не добавляем новые методы в следующие модули. Изменять или удалять существующие методы разрешается
здесь список ваших модулей которые превратились в мусорку кода
1.38 Мы не используем метод РольДоступна(), вместо нее используем Пользователи.РолиДоступны(), Пользователи.ЭтоПолноправныйПользователь()
1.39 Мы не используем механизм Дополнительных обработок и отчетов. Все обработки и отчеты должны находиться в составе основной конфигурации
Разрешается использование внешних отчетов и обработок через команду Файл - Открыть. Только на этапе MVP
1.40 Мы не записываем объекты с включением ОбменДанными.Загрузка = Истина; Исключение - обмены с внешними системами
1.41 Мы всегда записываем объекты с включением ОбменДанными.Загрузка = Истина если это обмен с внешними системами
1.42 Мы не используем настройку видимости элементов форм через пользовательскую видимость и использование по ролям для команд формы, за исключением п1.30
1.43 Мы устанавливаем параметры запроса только после установки текста запроса
1.54 При использовании вложенных областей мы располагаем их выше кода находящегося вне вложенной области
1.55 Рекомендуемые шаблоны для оформления областей модулей находятся тут
1.56 В коде мы не используем методы НайтиПоНаименованию, НайтиПоКоду, НайтиПоРеквизиту, а так же проверку на наименование или код или уникальный идентификатор
1.57 При доработке существующих методов мы всегда стремимся вставить свой код внизу метода, если это позволяет логика изменений
1.58 Для управления видимостью\доступностью элементов формы мы создаем или используем существующий обработчик ОбновитьВидимостьДоступностьЭлементовФормы() или аналогичный по смыслу
1.59 Для управления видимостью\доступностью элементов формы мы используем булеву алгебру
Неправильно:
Если ТипСклада = Перечисления.ТипыСкладов.Оптовый Тогда Элементы.НастройкиОптовогоСклада.Видимость = Истина; Элементы.НастройкиРозничногоСклада.Видимость = Ложь; Иначе Элементы.НастройкиОптовогоСклада.Видимость = Ложь; Элементы.НастройкиРозничногоСклада.Видимость = Истина; КонецЕсли;Правильно:
Элементы.НастройкиОптовогоСклада.Видимость = ТипСклада = Перечисления.ТипыСкладов.Оптовый; Элементы.НастройкиРозничногоСклада.Видимость = ТипСклада = Перечисления.ТипыСкладов.Розничный;
1.60 При подключении стандартных подсистем мы размещаем их вызовы в верху обработчиков событий
Пример:
&НаСервере Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка) // СтандартныеПодсистемы.ВерсионированиеОбъектов ВерсионированиеОбъектов.ПриСозданииНаСервере(ЭтотОбъект); // Конец СтандартныеПодсистемы.ВерсионированиеОбъектов // СтандартныеПодсистемы.ПодключаемыеКоманды ПодключаемыеКоманды.ПриСозданииНаСервере(ЭтотОбъект); // Конец СтандартныеПодсистемы.ПодключаемыеКоманды ИнициализацияДанныхФормы(); ОбновитьВидимостьДоступностьЭлементовФормы(); КонецПроцедуры
1.61 При написании события ОбработкаПроведения мы используем шаблон
Процедура ОбработкаПроведения(Отказ, РежимПроведения) _ПроведениеДокументов.ИнициализироватьДополнительныеСвойстваДляПроведения(Ссылка, ДополнительныеСвойства, РежимПроведения); Документы._НашДокумент.ИнициализироватьДанныеДокумента(Ссылка, ДополнительныеСвойства); _ПроведениеДокументов.ПодготовитьНаборыЗаписейКРегистрацииДвижений(ЭтотОбъект); _ПроведениеДокументов.ОтразитьДвижения(ДополнительныеСвойства,Движения,Отказ); СформироватьСписокРегистровДляКонтроля(); // эта строка если требуется контроль _ПроведениеДокументов.ЗаписатьНаборыЗаписей(ЭтотОбъект); _ПроведениеДокументов.ВыполнитьКонтрольРезультатовПроведения(ЭтотОбъект, Отказ); _ПроведениеДокументов.ОчиститьДополнительныеСвойстваДляПроведения(ДополнительныеСвойства); КонецПроцедуры
1.62 Мы не используем комментарии блоков кода при изменении собственных модулей (не стоящих на поддержке). Разрешаются комментарии поясняющие логику процессов или почему выбрано такое решение
1.63 Для выборки данных мы не используем объектную модель. Например неправильно:
РегистрыСведений.КурсыВалют.Выбрать() РегистрыСведений.КурсыВалют.Получить() РегистрыСведений.КурсыВалют.СрезПервых() РегистрыСведений.КурсыВалют.СрезПоследних() РегистрыСведений.КурсыВалют.ПолучитьПервое() РегистрыСведений.КурсыВалют.ПолучитьПоследнее()
1.64 Мы не используем чтение набора записей объектной моделью через НаборЗаписей.Прочитать(), мы используем только запросы для чтения данных
Исключение: разрешено применять метод Прочитать() внутри явно обьявленной транзации с предварительной исключительной блокировкой данных по записям набора
1.65 При создании регламентных заданий количество повторов при аварийном завершении мы устанавливаем = 0
1.66 Мы всегда получаем значения констант в привелигированном режиме
1.67 Мы всегда задаем вопрос пользователю с возможностью отмены или подтверждения действия перед необратимыми действиями или записью в базу данных
1.68 Мы не используем объявление переменной через оператор "Перем"
1.69 Утратил силу
1.70 Утратил силу
1.71 Для вложенных областей мы всегда пишем комментарий рядом с КонецОбласти
Пример:
#Область СлужебныеПроцедурыИФункции #Область Печать #КонецОбласти // Печать #КонецОбласти
1.72 Мы не используем области внутри методов, мы выделяем такой код в отдельные методы
1.73 Для полей запроса результатом вычисления которых является булево мы не применяем ВЫБОР КОГДА, а используем булеву алгебру
Неправильно: ВЫБОР КОГДА Таблица.Поле = Значение(Справочник.МойСправочник.ПустаяСсылка) ТОГДА Истина ИНАЧЕ Ложь КОНЕЦ КАК МоеПоле
Правильно: Таблица.Поле = Значение(Справочник.МойСправочник.ПустаяСсылка) КАК МоеПоле
1.74 Метод установки видимости и доступности элементов формы мы всегда располагаем последней строкой в вызывающем методе
1.75 Все константы мы получаем в привелигированном режиме
Пример:
Функция ПартионныйУчетВключен() УстановитьПривелигированныйРежим(Истина); Результат = Константы.МояКонстанта.Получить(); УстановитьПривелигированныйРежим(Ложь); Возврат Результат; КонецФункции
1.76 В каждой проверке на корректность данных мы делаем флаг в Объект.ДополнительныеСвойства для программного отключения проверки
Пример:
Процедура ПроверитьЗаполнениеСостава() Если Объект.ДополнительныеСвойства.Свойство("ОтключитьПроверкуЗаполненияСостава") Тогда Возврат; КонецЕсли; // далее проверка заполнения КонецПроцедуры;
1.77 Мы не отключаем проверки sonarcube директивами в коде
1.78 При объявлении структур из строки мы всегда разделяем пробелом ключи структуры
Неправильно:
Новый Структура("Клиент,Результат");Правильно:
Новый Структура("Клиент, Результат");
1.79 Мы не используем COM-объекты
1.80 При выводе сообщений о ошибках пользователю мы всегда привязываем эти сообщения к объекту и к полю в котором произошла ошибка
Неправильно:
ОбщегоНазначения.СообщитьПользователю("Не заполнено поле Склад");Правильно:
ОбщегоНазначения.СообщитьПользователю("Не заполнено поле Склад", ЭтотОбъект, , "Объект.Склад", Отказ);
1.81 Мы не используем номер строки в тексте ошибки при проверке в табличных частях
Неправильно:
ОбщегоНазначения.СообщитьПользователю("В строке " + НомерСтроки + не указана характеристика);Правильно:
ПульКПолю = ОбщегоНазначенияКлиентСервер.ПутьКТабличнойЧасти("Номенклатура", НомерСтроки, "Характеристика"); ТекстСообщения = СтрШаблон("Заполните характеристику для номенклатуры %1", Номенклатура); ОбщегоНазначения.СообщитьПользователю(ТекстСообщения, ЭтотОбъект, ПульКПолю, , Отказ);
1.82 Мы всегда используем параметр отказ при выводе ошибок через интерфейс БСП, если нужно прервать операцию
Неправильно:
ОбщегоНазначения.СообщитьПользователю("Не заполнено поле Склад"); Отказ = Истина;Правильно:
ОбщегоНазначения.СообщитьПользователю("Не заполнено поле Склад", ЭтотОбъект, , "Объект.Склад", Отказ);
1.83 Для вывода ошибок и сообщений пользователю мы всегда используем интерфейс БСП
При создании новых ролей мы используем стандарт наименования:
На ссылочные типы и регистры:
Чтение<ИмяФункции> содержит права:
- Чтение, Просмотр, Ввод по строке.
ДобавлениеИзменение<ИмяФункции> содержит:
- Изменение, Редактирование;
- Проведение, Отмена проведения, Интерактивное проведение, Интерактивная отмена проведения, Интерактивное изменение проведенных (для документов);
- Управление итогами (для регистров).
- Добавление, Интерактивное добавление;
- Интерактивная пометка удаления, Интерактивное снятие пометки удаления.
На подсистемы, отображаемые в главном командном интерфейсе
- Для каждой подсистемы верхнего уровня должна быть создана роль Подсистема<ИмяПодсистемы>, дающая право на просмотр
На отчеты:
- Необходимо создать роль ПросмотрОтчета<ИмяОтчета>, дающую права использования и просмотра.
На обработки:
- Должна быть создана роль ИспользованиеОбработки<ИмяОбработки>, дающая права на просмотр и использование.
Права, не связанные с доступом к объектам:
- В случае если возникает необходимость давать пользователям какие-то дополнительные права, не связанные с доступом к объектам, нужно создавать роль <НаименованиеПрава>, не дающую доступ ни к одному объекту. При этом в наименовании не нужно использовать слово «Право».
3.1 Мы не вносим изменения в стандартные библиотеки.
Исключения:
- Невозможно выполнить задачу не изменив стандартную библиотеку;
- Решение без изменения стандартной библиотеки ведет к деградации системы по производительности или пользовательскому интерфейсу.
- Требуется обязательное согласование с руководителем отдела и архитектором.
3.2 Все добавляемые метаданные, формы, команды, макеты, процедуры, функции, переменные и прочее обозначаются префиксом "_"
3.3 Мы не добавляем новые процедуры и функции в общие модули, модули менеджера, модули объекта, модули набора записей, модули команд стандартных библиотек и типового функционала конфигурации.
3.4 При добавлении методов в модули форм\объектов\менеджеров стандартных библиотек, при невозможности доработать в расширении мы помещаем код в специальную область.
Область располагается в конце модулей. Внутри специальной области код оформляется в областях с префиксом НазваниеКомпании
#Область НазваниеКомпании #Область НазваниеКомпании_ОбработчикиСобытийФормы #КонецОбласти #Область НазваниеКомпании_ОбработчикиСобытийЭлементовФормы #КонецОбласти #Область НазваниеКомпании_ОбработчикиКомандФормы #КонецОбласти #Область НазваниеКомпании_СлужебныеПроцедурыИФункции #КонецОбласти #КонецОбласти
3.5 При изменении кода стандартных библиотек, мы оставляем комментарии
// ++ Название компании // [Описание вносимых изменений] // [Закомментированный прежний код] [Новый код] // -- Название компанииили допускаются однострочные комментарии
// ++-- Название компании
3.6 Программный интерфейс функциональных блоков мы размещаем в общих модулях Имя модуля должно содержать наименование функционального блока, аналогично наименованию подсистемы\подчиненной подсистемы и соответствовать правилам.
Пример:
_СкидкиКлиент,_СкидкиКлиентСервер,_СкидкиСервер
3.7 Cлужебный программный интерфейс отдельных объектов, который относится только к этому объекту мы размещаем в модуле менеджера объекта
Пример:
РегистрыСведений._ЖурналВходящихЗапросовHTTP.ЗарегистрироватьЗапрос(); // Добавляет запись в регистре _ЖурналВходящихЗапросовHTTP
3.8 Мы не используем подсистему "Свойства", так же запрещается добавлять новые реквизиты и сведения в план видов характеристик ДополнительныеРеквизитыИСведения в том числе в пользовательском режиме
3.9 Мы не изменяем настройки поддержки
Редактирование настроек поддрежки объектов производится техническим архитектором по запросу
3.10 Мы не изменяем номер версии конфигурации
Номер версии может быть изменен в отдельной ветке при подготовке к релизу
4.1 Мы не разрабатываем новых расширений
4.2 Все доработки стандартного функционала конфигурации мы делаем в расширении ИмяРасширения
4.3 При доработке существующих расширений мы руководствуемся рекомендациями Адаптация прикладных решений
4.4 Мы не дорабатываем в расширениях добавленные нами объекты (с префиксом _ или без признака "объект поставщика"). Все такие доработки должны производиться в основной конфигурации
4.5 Мы не добавляем новых объектов (метаданных, реквизитов и тд) в расширения, а только изменяем существующий типовой функционал
4.6 При доработке модуля менеджера, модуля объекта, модуля набора записей мы создаем в основной конфигурации серверный общий модуль по шаблону _НаименованиеОбъекта, в области СлужебныйПрограммныйИнтерфейс делаем обработчики событий где первый параметр - Объект и вызываем из расширения этот модуль. Имена методов должны формироваться по шаблону <ИмяМетода><ТипВызова>
Например модуль объекта в расширении:
&После("ПередЗаписью") Процедура _ПередЗаписью(Отказ) _ХарактеристикиНоменклатуры.ПередЗаписьюПосле(ЭтотОбъект, Отказ); КонецПроцедуры &После("ПриЗаписи") Процедура _ПриЗаписи(Отказ) _ХарактеристикиНоменклатуры.ПриЗаписиПосле(ЭтотОбъект, Отказ); КонецПроцедурыНапример модуль объекта в основной конфигурации вызываемой из расширения:
#Область СлужебныйПрограммныйИнтерфейс #Область ОбработчикиСобытий Процедура ПередЗаписьюПосле(Объект, Отказ) Экспорт УстановитьАналитическийРазмер(Объект); КонецПроцедуры Процедура ПриЗаписиПосле(Объект, Отказ) Экспорт СоздатьВнутреннийШтрихКод(Объект); КонецПроцедуры #КонецОбласти // ОбработчикиСобытий #КонецОбласти
4.7 Мы не заимствуем роли в расширения, все доработки ролей должны быть в основной конфигурации
4.8 Мы определаяем следующий приоритет заимствования методов в расширении. Подробнее о стратегии выбора типа расширяющих методов
- После
- Перед
- ИзменениеИКонтроль
- Вместо
4.9 Мы определяем следующий приоритет доработки заимствованных форм
- Доработка с помощью дизайнера форм
- Программное создание\доработка элементов форм, если нет возможности внести изменения через дизайнер формы, либо если доработка должна осуществляться во многих формах (для универсальных механизмов)
4.10 Мы не заимствуем макеты печатных форм, а всегда копируем макет с новым именем и изменяем его
4.11 Мы не заимствуем подсистемы, а меняем их в составе основной конфигурации
5.1 Мы используем стиль написания cebab-case для шаблонов методов, параметров, результатов http и web сервисов
Неправильно: GetGoodQuantity Правильно: good-quantity
5.2 Мы используем латиницу для наименования методов, параметров, результатов http и web сервисов
Неправильно: ВыгрузитьЗагрузитьОдносторонний Правильно: one-sided-exchange
5.3 Все объекты отвечающие за интеграцию с внешними системами мы называем с префикса _Интеграция<ИмяВнешнейСистемы>
5.4 Мы не пишем функциональный код в модуле http-сервиса, а только лишь вызываем общий модуль с методом обмена
5.5 При проектировании http-сервисов мы руководствуемся практиками построения REST интерфейсов
5.6 В названиях шаблонов URL http-сервисов мы не применяем слова глаголы, например: add, get, delete, patch, update, post, change, find
Неправильно: GetZakaz, ChangeDC Правильно: order + метод GET, discount-card + метод POST
5.7 Для описания документации сервисов мы используем формат swagger-документов. Документация размещается в папке doc\api в репозитории проекта
5.8 При разработке новых сервисов по API мы всегда используем шаблон
6.1 Мы используем наставления по дизайну форм из Дизайн форм 1С
6.2 Мы всегда проверяем растягиваемость формы
6.3 Мы всегда разрабатываем и проверяем формы в разрешении экрана 1920х1080
7.1 Утратил силу
7.2 Для проверки изменений мы используем режим запуска тонкий клиент
7.3 Стандарты обязательные к ознакомлению
ИТС:Использование признака ОбменДанными.Загрузка в обработчиках событий объекта
ИТС:Особенности использования структур в качестве параметров процедур и функций
ИТС:Работа с параметром «Отказ» в обработчиках событий
7.4 Прочие ссылки для ознакомления