diff --git a/README.md b/README.md
index 5825753..4f86a63 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,7 @@
- `СортировкаДереваМетаданных` - упорядочивает объекты метаданных верхнего уровня по алфавиту в файле описания конфигурации (кроме подсистем), удаляет дубли. Помещает объекты с префиксом в низ списка, если настроено. Поддерживается как файл в формате выгрузки конфигуратора (`Configuration.xml`), так и в формате EDT (`Configuration.mdo`).
- `СортировкаСоставаОпределяемыхТипов` - сортирует состав определяемых типов. [См. подробнее](/docs/СортировкаСоставаОпределяемыхТипов.md)
- `СортировкаСоставаПодсистем` - сортирует состав подсистем. [См. подробнее](/docs/СортировкаСоставаПодсистем.md)
+- `СортировкаСоставаФункциональныхОпций` - сортирует состав функциональных опций. [См. подробнее](/docs/СортировкаСоставаФункциональныхОпций.md)
- `УдалениеДублейМетаданных` - удаляет дубли объектов метаданных в файле описания конфигурации (могут образоваться при объединениях). Поддерживается как файл в формате выгрузки конфигуратора (`Configuration.xml`), так и в формате EDT (`Configuration.mdo`)..
- `УдалениеЛишнихКонцевыхПробелов` - удаляет лишние пробелы и табы в конце не пустых строк в файлах модулей.
- `УдалениеЛишнихПустыхСтрок` - удаляет лишние пустые строки в модулях (лишними считаются 2 и более идущих подряд пустых строк).
diff --git a/docs/СортировкаСоставаФункциональныхОпций.md b/docs/СортировкаСоставаФункциональныхОпций.md
new file mode 100644
index 0000000..048c233
--- /dev/null
+++ b/docs/СортировкаСоставаФункциональныхОпций.md
@@ -0,0 +1,22 @@
+# Сценарий `СортировкаСоставаФункциональныхОпций`
+
+Упорядочивает объекты состава по алфавиту в файлах описаний функциональных опций, удаляет дубли и битые ссылки. Помещает объекты с префиксами в конец списка, если настроено. Поддерживаются как файлы в формате выгрузки конфигуратора, так и в формате EDT.
+
+Пример настройки:
+
+```JSON
+{
+ "Precommt4onecСценарии": {
+ "НастройкиСценариев": {
+ "СортировкаСоставаФункциональныхОпций": {
+ "УчитываяПрефикс": [
+ "ИР_",
+ "БСП_"
+ ]
+ }
+ }
+ }
+}
+```
+
+Значение `УчитываяПрефикс` может быть массивом или строкой, содержащей один или несколько префиксов, разделенных запятыми. Между собой префиксы сортируются в порядке следования в настройке.
diff --git a/features/ИнтерактивнаяНастройка.feature b/features/ИнтерактивнаяНастройка.feature
index e013df6..8916226 100644
--- a/features/ИнтерактивнаяНастройка.feature
+++ b/features/ИнтерактивнаяНастройка.feature
@@ -42,6 +42,7 @@ y
y
y
y
+y
local
n
"""
@@ -70,6 +71,7 @@ n
"СортировкаДереваМетаданных.os",
"СортировкаСоставаОпределяемыхТипов.os",
"СортировкаСоставаПодсистем.os",
+ "СортировкаСоставаФункциональныхОпций.os",
"УдалениеДублейМетаданных.os",
"УдалениеЛишнихКонцевыхПробелов.os",
"УдалениеЛишнихПустыхСтрок.os"
diff --git a/features/Конфигурирование.feature b/features/Конфигурирование.feature
index 873e92a..fcc3451 100644
--- a/features/Конфигурирование.feature
+++ b/features/Конфигурирование.feature
@@ -40,7 +40,7 @@
Установленные настройки: Базовые настройки
ИспользоватьСценарииРепозитория = Нет
КаталогЛокальныхСценариев =
- ГлобальныеСценарии = ДобавлениеПробеловПередКлючевымиСловами.os,ЗапретИспользованияПерейти.os,ИсправлениеНеКаноническогоНаписания.os,КорректировкаXMLФорм.os,ОбработкаЮнитТестов.os,ОтключениеПолнотекстовогоПоиска.os,ОтключениеРазрешенияИзменятьФорму.os,ПроверкаДублейПроцедурИФункций.os,ПроверкаКорректностиИнструкцийПрепроцессора.os,ПроверкаКорректностиОбластей.os,ПроверкаНецензурныхСлов.os,РазборОбычныхФормНаИсходники.os,РазборОтчетовОбработокРасширений.os,СинхронизацияОбъектовМетаданныхИФайлов.os,СортировкаДереваМетаданных.os,СортировкаСоставаОпределяемыхТипов.os,СортировкаСоставаПодсистем.os,УдалениеДублейМетаданных.os,УдалениеЛишнихКонцевыхПробелов.os,УдалениеЛишнихПустыхСтрок.os
+ ГлобальныеСценарии = ДобавлениеПробеловПередКлючевымиСловами.os,ЗапретИспользованияПерейти.os,ИсправлениеНеКаноническогоНаписания.os,КорректировкаXMLФорм.os,ОбработкаЮнитТестов.os,ОтключениеПолнотекстовогоПоиска.os,ОтключениеРазрешенияИзменятьФорму.os,ПроверкаДублейПроцедурИФункций.os,ПроверкаКорректностиИнструкцийПрепроцессора.os,ПроверкаКорректностиОбластей.os,ПроверкаНецензурныхСлов.os,РазборОбычныхФормНаИсходники.os,РазборОтчетовОбработокРасширений.os,СинхронизацияОбъектовМетаданныхИФайлов.os,СортировкаДереваМетаданных.os,СортировкаСоставаОпределяемыхТипов.os,СортировкаСоставаПодсистем.os,СортировкаСоставаФункциональныхОпций.os,УдалениеДублейМетаданных.os,УдалениеЛишнихКонцевыхПробелов.os,УдалениеЛишнихПустыхСтрок.os
ОтключенныеСценарии =
НастройкиСценариев = Соответствие
ОтключениеПолнотекстовогоПоиска = Соответствие
diff --git a/src/Модули/ТипыФайлов.os b/src/Модули/ТипыФайлов.os
index 2d26438..c2563d3 100644
--- a/src/Модули/ТипыФайлов.os
+++ b/src/Модули/ТипыФайлов.os
@@ -189,7 +189,7 @@
КонецФункции
// ЭтоФайлОписанияПодсистемы
-// Возвращает истину, если файл является описанием подсистемы
+// Возвращает истину, если файл является описанием подсистемы
// Параметры:
// Файл - Файл - Полный путь к файлу
//
@@ -203,7 +203,7 @@
КонецФункции
// ЭтоФайлОписанияПодсистемыEDT
-// Возвращает истину, если файл является описанием подсистемы и в формате EDT
+// Возвращает истину, если файл является описанием подсистемы и в формате EDT
// Параметры:
// Файл - Файл - Полный путь к файлу
//
@@ -217,7 +217,7 @@
КонецФункции
// ЭтоФайлОписанияОпределяемогоТипа
-// Возвращает истину, если файл является описанием определяемого типа
+// Возвращает истину, если файл является описанием определяемого типа
// Параметры:
// Файл - Файл - Полный путь к файлу
//
@@ -231,7 +231,7 @@
КонецФункции
// ЭтоФайлОписанияОпределяемогоТипаEDT
-// Возвращает истину, если файл является описанием определяемого типа в формате EDT
+// Возвращает истину, если файл является описанием определяемого типа в формате EDT
// Параметры:
// Файл - Файл - Полный путь к файлу
//
@@ -244,6 +244,34 @@
КонецФункции
+// ЭтоФайлОписанияФункциональнойОпции
+// Возвращает истину, если файл является описанием функциональной опции
+// Параметры:
+// Файл - Файл - Полный путь к файлу
+//
+// Возвращаемое значение:
+// Булево - Признак
+//
+Функция ЭтоФайлОписанияФункциональнойОпции(Файл) Экспорт
+
+ Возврат ЭтоФайлОписанияМетаданныхКонфигуратора(Файл) И ПутьСодержитКаталог(Файл, "functionaloptions");
+
+КонецФункции
+
+// ЭтоФайлОписанияФункциональнойОпцииEDT
+// Возвращает истину, если файл является описанием функциональной опции в формате EDT
+// Параметры:
+// Файл - Файл - Полный путь к файлу
+//
+// Возвращаемое значение:
+// Булево - Признак
+//
+Функция ЭтоФайлОписанияФункциональнойОпцииEDT(Файл) Экспорт
+
+ Возврат ЭтоФайлОписанияМетаданныхEDT(Файл) И ПутьСодержитКаталог(Файл, "functionaloptions");
+
+КонецФункции
+
// ЭтоМодульМенеджера
// Возвращает истину, если файл является модулем менеджера
// Параметры:
diff --git a/src/СценарииОбработки/СортировкаСоставаФункциональныхОпций.os b/src/СценарииОбработки/СортировкаСоставаФункциональныхОпций.os
new file mode 100644
index 0000000..aa36b2d
--- /dev/null
+++ b/src/СценарииОбработки/СортировкаСоставаФункциональныхОпций.os
@@ -0,0 +1,89 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Служебный модуль с реализацией сценария обработки файла <СортировкаСоставаФункциональныхОпций>
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// ИмяСценария
+// Возвращает имя сценария обработки файлов
+//
+// Возвращаемое значение:
+// Строка - Имя текущего сценария обработки файлов
+//
+Функция ИмяСценария() Экспорт
+
+ Возврат "СортировкаСоставаФункциональныхОпций";
+
+КонецФункции // ИмяСценария()
+
+// ОбработатьФайл
+// Выполняет обработку файла
+//
+// Параметры:
+// АнализируемыйФайл - Файл - Файл из журнала git для анализа
+// КаталогИсходныхФайлов - Строка - Каталог расположения исходных файлов относительно каталог репозитория
+// ДополнительныеПараметры - Структура - Набор дополнительных параметров, которые можно использовать
+// * Лог - Объект - Текущий лог
+// * ИзмененныеКаталоги - Массив - Каталоги, которые необходимо добавить в индекс
+// * КаталогРепозитория - Строка - Адрес каталога репозитория
+// * ФайлыДляПостОбработки - Массив - Файлы, изменившиеся / образовавшиеся в результате работы сценария
+// и которые необходимо дообработать
+//
+// Возвращаемое значение:
+// Булево - Признак выполненной обработки файла
+//
+Функция ОбработатьФайл(АнализируемыйФайл, КаталогИсходныхФайлов, ДополнительныеПараметры) Экспорт
+
+ Лог = ДополнительныеПараметры.Лог;
+ НастройкиСценария = ДополнительныеПараметры.Настройки.Получить(ИмяСценария());
+ УчитываяПрефикс = НастройкиСценария.Получить("УчитываяПрефикс");
+
+ ФайлОбработан = Ложь;
+
+ Если АнализируемыйФайл.Существует() Тогда
+
+ ЭтоФункциональнаяОпцияЕДТ = ТипыФайлов.ЭтоФайлОписанияФункциональнойОпцииEDT(АнализируемыйФайл);
+ Если ЭтоФункциональнаяОпцияЕДТ Или ТипыФайлов.ЭтоФайлОписанияФункциональнойОпции(АнализируемыйФайл) Тогда
+
+ Лог.Информация("Обработка файла '%1' по сценарию '%2'", АнализируемыйФайл.ПолноеИмя, ИмяСценария());
+
+ ФайлОбработан = ОтсортироватьФункциональнуюОпцию(АнализируемыйФайл.ПолноеИмя, ЭтоФункциональнаяОпцияЕДТ, УчитываяПрефикс);
+
+ Если ФайлОбработан Тогда
+ ДополнительныеПараметры.ИзмененныеКаталоги.Добавить(АнализируемыйФайл.ПолноеИмя);
+ КонецЕсли;
+
+ КонецЕсли;
+
+ КонецЕсли;
+
+ Возврат ФайлОбработан;
+
+КонецФункции // ОбработатьФайл()
+
+Функция ОтсортироватьФункциональнуюОпцию(Знач ИмяФайла, ЭтоЕДТ, УчитываяПрефикс)
+
+ СодержимоеФайла = ФайловыеОперации.ПрочитатьТекстФайла(ИмяФайла);
+
+ ВыполненаСортировка = Ложь;
+ Если ЭтоЕДТ Тогда
+ ИмяТега = "content";
+ РазделительЭлементов = Символы.ПС + " ";
+ ВыполненаСортировка = РегулярныеВыражения.СортироватьМассивТеговСостава(
+ СодержимоеФайла, ИмяТега, РазделительЭлементов, УчитываяПрефикс);
+ Иначе
+ ИмяРодительскогоТега = "Content";
+ ОтступРодительскогоТега = " ";
+ РазделительЭлементов = Символы.ПС + " ";
+ ВыполненаСортировка = РегулярныеВыражения.СортироватьВложенныйМассивТеговСостава(
+ СодержимоеФайла, ИмяРодительскогоТега, ОтступРодительскогоТега, РазделительЭлементов, УчитываяПрефикс);
+ КонецЕсли;
+
+ Если ВыполненаСортировка Тогда
+ ФайловыеОперации.ЗаписатьТекстФайла(ИмяФайла, СодержимоеФайла);
+ Возврат Истина;
+ Иначе
+ Возврат Ложь;
+ КонецЕсли;
+
+КонецФункции
diff --git a/tests/fixtures/СортировкаСоставаФункциональныхОпций/До/ИнтегрироватьETL.mdo b/tests/fixtures/СортировкаСоставаФункциональныхОпций/До/ИнтегрироватьETL.mdo
new file mode 100644
index 0000000..71cab33
--- /dev/null
+++ b/tests/fixtures/СортировкаСоставаФункциональныхОпций/До/ИнтегрироватьETL.mdo
@@ -0,0 +1,14 @@
+
+
+ ИнтегрироватьETL
+
+ ru
+ Интегрировать ETL
+
+ Управляет механизмом интеграции с ETL
+ Constant.ИнтегрироватьETL
+ true
+ Subsystem.ПодсистемыБСП.Subsystem.Интеграция.Subsystem.ETL
+ ExchangePlan.ИзмененияETL
+ InformationRegister.ЛогВыгрузкиДанныхETL
+
diff --git a/tests/fixtures/СортировкаСоставаФункциональныхОпций/До/ИспользоватьВалюты.xml b/tests/fixtures/СортировкаСоставаФункциональныхОпций/До/ИспользоватьВалюты.xml
new file mode 100644
index 0000000..4581a89
--- /dev/null
+++ b/tests/fixtures/СортировкаСоставаФункциональныхОпций/До/ИспользоватьВалюты.xml
@@ -0,0 +1,29 @@
+
+
+
+
+ ИспользоватьВалюты
+
+
+ ru
+ Использовать валюты
+
+
+ Управляет использованием валютами
+ Constant.ИспользоватьВалюты
+ true
+
+ InformationRegister.КурсыВалют
+ DataProcessor.ЗагрузкаКурсовВалют
+ Catalog.Валюты
+ Catalog.Валюты.Command.ИсторияКурсов
+ Catalog.Валюты.Command.ОткрытьФормуСписка
+ DataProcessor.ЗагрузкаКурсовВалют.TabularSection.СписокВалют
+ DataProcessor.ЗагрузкаКурсовВалют.Attribute.ОкончаниеПериодаЗагрузки
+ DataProcessor.ЗагрузкаКурсовВалют.Attribute.НачалоПериодаЗагрузки
+ DataProcessor.ЗагрузкаКурсовВалют.Command.ЗагрузкаКурсовВалют
+ Constant.ЗагружатьКурсыВалютССервера1С
+
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/СортировкаСоставаФункциональныхОпций/После/ИнтегрироватьETL.mdo b/tests/fixtures/СортировкаСоставаФункциональныхОпций/После/ИнтегрироватьETL.mdo
new file mode 100644
index 0000000..6a8c587
--- /dev/null
+++ b/tests/fixtures/СортировкаСоставаФункциональныхОпций/После/ИнтегрироватьETL.mdo
@@ -0,0 +1,14 @@
+
+
+ ИнтегрироватьETL
+
+ ru
+ Интегрировать ETL
+
+ Управляет механизмом интеграции с ETL
+ Constant.ИнтегрироватьETL
+ true
+ ExchangePlan.ИзмененияETL
+ InformationRegister.ЛогВыгрузкиДанныхETL
+ Subsystem.ПодсистемыБСП.Subsystem.Интеграция.Subsystem.ETL
+
diff --git a/tests/fixtures/СортировкаСоставаФункциональныхОпций/После/ИспользоватьВалюты.xml b/tests/fixtures/СортировкаСоставаФункциональныхОпций/После/ИспользоватьВалюты.xml
new file mode 100644
index 0000000..026b472
--- /dev/null
+++ b/tests/fixtures/СортировкаСоставаФункциональныхОпций/После/ИспользоватьВалюты.xml
@@ -0,0 +1,29 @@
+
+
+
+
+ ИспользоватьВалюты
+
+
+ ru
+ Использовать валюты
+
+
+ Управляет использованием валютами
+ Constant.ИспользоватьВалюты
+ true
+
+ Catalog.Валюты.Command.ИсторияКурсов
+ Catalog.Валюты.Command.ОткрытьФормуСписка
+ Catalog.Валюты
+ Constant.ЗагружатьКурсыВалютССервера1С
+ DataProcessor.ЗагрузкаКурсовВалют.Attribute.НачалоПериодаЗагрузки
+ DataProcessor.ЗагрузкаКурсовВалют.Attribute.ОкончаниеПериодаЗагрузки
+ DataProcessor.ЗагрузкаКурсовВалют.Command.ЗагрузкаКурсовВалют
+ DataProcessor.ЗагрузкаКурсовВалют.TabularSection.СписокВалют
+ DataProcessor.ЗагрузкаКурсовВалют
+ InformationRegister.КурсыВалют
+
+
+
+
\ No newline at end of file
diff --git a/tests/ТестНастройкиРепозитория.os b/tests/ТестНастройкиРепозитория.os
index 00eda9c..00f4b7b 100644
--- a/tests/ТестНастройкиРепозитория.os
+++ b/tests/ТестНастройкиРепозитория.os
@@ -103,6 +103,7 @@
ОжидаемыеСценарии.Добавить("СортировкаДереваМетаданных.os");
ОжидаемыеСценарии.Добавить("СортировкаСоставаОпределяемыхТипов.os");
ОжидаемыеСценарии.Добавить("СортировкаСоставаПодсистем.os");
+ ОжидаемыеСценарии.Добавить("СортировкаСоставаФункциональныхОпций.os");
ИменаЗагружаемыхСценариев = МенеджерНастроек.ИменаЗагружаемыхСценариев();
diff --git a/tests/ТестПроверкаСценариевОбработки.os b/tests/ТестПроверкаСценариевОбработки.os
index deceb34..4809489 100644
--- a/tests/ТестПроверкаСценариевОбработки.os
+++ b/tests/ТестПроверкаСценариевОбработки.os
@@ -22,6 +22,7 @@
ВсеТесты.Добавить("ТестДолжен_ПроверитьСортировкуСоставаМетаданных");
ВсеТесты.Добавить("ТестДолжен_ПроверитьСценарийСортировкаСоставаПодсистем");
ВсеТесты.Добавить("ТестДолжен_ПроверитьСценарийСортировкаСоставаОпределяемыхТипов");
+ ВсеТесты.Добавить("ТестДолжен_ПроверитьСценарийСортировкаСоставаФункциональныхОпций");
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоСценарийПроверкиДублейПроцедурВызываетИсключение");
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоСценарийПроверкиДублейПроцедурОбработаетФайл");
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоСценарийПроверкиДублейПроцедурНеОбработаетНесуществующийФайл");
@@ -99,6 +100,13 @@
КонецПроцедуры
+Процедура ТестДолжен_ПроверитьСценарийСортировкаСоставаФункциональныхОпций() Экспорт
+
+ ИмяСценария = "СортировкаСоставаФункциональныхОпций";
+ ПроверитьОбработкуФайлов(ИмяСценария, "FunctionalOptions");
+
+КонецПроцедуры
+
Процедура ПроверитьОбработкуФайлов(ИмяСценария, ПодкаталогИсходников)
ОбъектСценария = ПолучитьСценарий(ИмяСценария);
diff --git a/v8config.json b/v8config.json
index f9c74cc..bb877ad 100644
--- a/v8config.json
+++ b/v8config.json
@@ -20,6 +20,7 @@
"СортировкаДереваМетаданных.os",
"СортировкаСоставаОпределяемыхТипов.os",
"СортировкаСоставаПодсистем.os",
+ "СортировкаСоставаФункциональныхОпций.os",
"УдалениеДублейМетаданных.os",
"УдалениеЛишнихКонцевыхПробелов.os",
"УдалениеЛишнихПустыхСтрок.os"