diff --git a/.claude/skills/role-compile/SKILL.md b/.claude/skills/role-compile/SKILL.md index 5df23c48..03e473ec 100644 --- a/.claude/skills/role-compile/SKILL.md +++ b/.claude/skills/role-compile/SKILL.md @@ -1,7 +1,7 @@ --- name: role-compile description: Создание роли 1С — метаданные и Rights.xml из описания прав -argument-hint: +argument-hint: allowed-tools: - Bash - Read @@ -9,199 +9,101 @@ allowed-tools: - Glob --- -# /role-compile — создание роли 1С +# /role-compile — генерация роли 1С из JSON DSL -Создаёт файлы роли (метаданные + Rights.xml) по описанию прав. Скрипта нет — агент генерирует XML по шаблонам ниже. +Принимает JSON-определение роли → генерирует `Roles/Имя.xml` (метаданные) и `Roles/Имя/Ext/Rights.xml` (права). UUID автоматически. -## Использование +## Параметры и команда -``` -/role-compile +| Параметр | Описание | +|----------|----------| +| `JsonPath` | Путь к JSON-определению роли | +| `RolesDir` | Каталог `Roles/` в исходниках конфигурации | + +```powershell +powershell.exe -NoProfile -File .claude\skills\role-compile\scripts\role-compile.ps1 -JsonPath "" -OutputDir "" ``` -- **RoleName** — программное имя роли -- **RolesDir** — каталог `Roles/` в исходниках конфигурации +`ИмяРоли` автоматически добавляется в `` файла `Configuration.xml` (ожидается в parent от `RolesDir`). -## Файловая структура и регистрация +## JSON DSL -``` -Roles/ - ИмяРоли.xml ← метаданные (uuid, имя, синоним) - ИмяРоли/ - Ext/ - Rights.xml ← определение прав +### Структура + +```json +{ "name": "ИмяРоли", "synonym": "Отображаемое имя", "objects": [...], "templates": [...] } ``` -В `Configuration.xml` добавить `ИмяРоли` в секцию ``. +Необязательные: `comment` (""), `setForNewObjects` (false), `setForAttributesByDefault` (true), `independentRightsOfChildObjects` (false). -## Шаблон метаданных: Roles/ИмяРоли.xml +### Shorthand-строки и объектная форма -```xml - - - - - ИмяРоли - - - ru - Отображаемое имя роли - - - - - - +```json +"objects": [ + "Catalog.Номенклатура: @view", + "Document.Реализация: @edit", + "DataProcessor.Загрузка: @view", + "InformationRegister.Цены: Read, Update", + { "name": "Document.Продажа", "preset": "view", "rights": {"Delete": false}, "rls": {"Read": "#Шаблон(\"\")"} } +] ``` -**UUID:** `powershell.exe -Command "[guid]::NewGuid().ToString()"` +- Shorthand: `"Тип.Имя: @пресет"` или `"Тип.Имя: Право1, Право2"` +- Объектная форма: `preset` + `rights` (переопределения) + `rls` (ограничения) -## Шаблон прав: Roles/ИмяРоли/Ext/Rights.xml +### Пресеты -```xml - - - false - true - false - - +| Пресет | Действие | +|--------|----------| +| `@view` | Просмотр — Read, View (+InputByString для справочников/документов; Use+View для обработок/отчётов) | +| `@edit` | Полное редактирование — CRUD + Interactive* + Posting (документы) | + +`@` обязателен в shorthand. В объектной форме — `"preset": "view"` без `@`. + +Для сервисов (WebService, HTTPService, IntegrationService) пресеты не определены — используй явные права: `"WebService.Имя: Use"`. + +### Русские синонимы + +Поддерживаются русские типы (`Справочник`→Catalog, `Документ`→Document) и права (`Чтение`→Read, `Просмотр`→View). Смешивание допустимо: `"Справочник.Контрагенты: Чтение, View"`. + +### Шаблоны RLS + +```json +"templates": [{"name": "ДляОбъекта(Мод)", "condition": "ГДЕ Организация = &ТекОрг"}] ``` -NB: namespace `http://v8.1c.ru/8.2/roles` (исторически 8.2, не 8.3). +Ссылка в `rls`: `"#ДляОбъекта(\"\")"`. Символ `&` автоматически экранируется в XML. -## Формат блока прав +## Примеры -```xml - - Catalog.Номенклатура - Readtrue - Viewtrue - +### Простая роль + +```json +{ + "name": "ЧтениеНоменклатуры", "synonym": "Чтение номенклатуры", + "objects": ["Catalog.Номенклатура: @view", "Catalog.Контрагенты: @view", "DataProcessor.Загрузка: @view"] +} ``` -Имя объекта — dot-нотация: `ТипОбъекта.Имя[.ТипВложенного.ИмяВложенного]`. +### Роль с RLS -## Практические наборы прав - -### Catalog / ExchangePlan - -| Набор | Права | -|-------|-------| -| Чтение | Read, View, InputByString | -| Полные | Read, Insert, Update, Delete, View, Edit, InputByString, InteractiveInsert, InteractiveSetDeletionMark, InteractiveClearDeletionMark | - -### Document - -| Набор | Права | -|-------|-------| -| Чтение | Read, View, InputByString | -| Полные | Read, Insert, Update, Delete, View, Edit, InputByString, Posting, UndoPosting, InteractiveInsert, InteractiveSetDeletionMark, InteractiveClearDeletionMark, InteractivePosting, InteractivePostingRegular, InteractiveUndoPosting, InteractiveChangeOfPosted | - -### InformationRegister / AccumulationRegister / AccountingRegister - -| Набор | Права | -|-------|-------| -| Чтение | Read, View | -| Полные | Read, Update, View, Edit | - -TotalsControl — только для управления итогами, обычно не нужно. - -### Простые типы - -| Тип | Права | -|-----|-------| -| `DataProcessor` / `Report` | Use, View | -| `Constant` | Read, Update, View, Edit (чтение: Read, View) | -| `CommonForm` / `CommonCommand` / `Subsystem` / `FilterCriterion` | View | -| `DocumentJournal` | Read, View | -| `Sequence` | Read, Update | -| `SessionParameter` | Get (+ Set если пишет) | -| `CommonAttribute` | View (+ Edit если редактирует) | -| `WebService` / `HTTPService` / `IntegrationService` | Use | -| `CalculationRegister` | Read, View | - -### Редкие ссылочные типы - -| Тип | Особенности (относительно Catalog) | -|-----|-------| -| `ChartOfAccounts`, `ChartOfCharacteristicTypes`, `ChartOfCalculationTypes` | + Predefined-права (InteractiveDeletePredefinedData и др.) | -| `BusinessProcess` | + Start, InteractiveStart, InteractiveActivate | -| `Task` | + Execute, InteractiveExecute, InteractiveActivate | - -### Типы БЕЗ прав в ролях - -Enum, FunctionalOption, DefinedType, CommonModule, CommonPicture, CommonTemplate — не фигурируют в Rights.xml. - -### Вложенные объекты (права: View, Edit) - -``` -Catalog.Контрагенты.Attribute.ИНН -Document.Реализация.StandardAttribute.Posted -Document.Реализация.TabularSection.Товары -InformationRegister.Цены.Dimension.Номенклатура -InformationRegister.Цены.Resource.Цена -Catalog.Контрагенты.Command.ОткрытьКарточку ← только View -Task.Задача.AddressingAttribute.Исполнитель +```json +{ + "name": "ЧтениеДокументовПоОрганизации", + "synonym": "Чтение документов (ограничение по организации)", + "objects": [ + "Catalog.Организации: @view", + {"name": "Document.РеализацияТоваровУслуг", "preset": "view", "rls": {"Read": "#ДляОбъекта(\"\")"}} + ], + "templates": [{"name": "ДляОбъекта(Модификатор)", "condition": "ГДЕ Организация = &ТекущаяОрганизация"}] +} ``` -Используются для точечного запрета: `false` на конкретный реквизит. +Подробные таблицы пресетов, русских синонимов и дополнительные примеры — в `dsl-reference.md`. -### Configuration +## Верификация -Объект: `Configuration.ИмяКонфигурации`. Ключевые права: Administration, DataAdministration, ThinClient, WebClient, ThickClient, MobileClient, ExternalConnection, Output, SaveUserData, InteractiveOpenExtDataProcessors, InteractiveOpenExtReports, MainWindowModeNormal, MainWindowModeWorkplace, MainWindowModeEmbeddedWorkplace, MainWindowModeFullscreenWorkplace, MainWindowModeKiosk, AnalyticsSystemClient. - -> DataHistory-права (ReadDataHistory, UpdateDataHistory и др.) существуют у Catalog, Document, Register, Constant — но используются крайне редко, в типовых ролях практически не встречаются. - -## RLS (ограничения на уровне записей) - -Внутрь ``, после ``. Применяется к Read, Update, Insert, Delete. - -```xml - - Read - true - - #ИмяШаблона("Параметр1", "Параметр2") - - ``` - -Шаблоны — в конце Rights.xml, после всех ``: - -```xml - - ИмяШаблона(Параметр1, Параметр2) - Текст шаблона - +/role-validate [MetadataPath] — проверка корректности XML, прав, RLS +/role-info — визуальная сводка структуры ``` - -`&` в условии → `&`. Типичные шаблоны: ДляОбъекта, ПоЗначениям, ДляРегистра. - -## Пример: роль для регламентного задания - -```xml - - Catalog.Валюты - Readtrue - - - InformationRegister.КурсыВалют - Readtrue - Updatetrue - - - Constant.ОсновнаяВалюта - Readtrue - -``` - -Фоновые задания не требуют Interactive/View/Edit-прав и прав конфигурации (ThinClient, WebClient и др.) — только программные (Read, Insert, Update, Delete, Posting). diff --git a/.claude/skills/role-compile/dsl-reference.md b/.claude/skills/role-compile/dsl-reference.md new file mode 100644 index 00000000..ea352eed --- /dev/null +++ b/.claude/skills/role-compile/dsl-reference.md @@ -0,0 +1,313 @@ +# Role DSL — полная справка + +Подробная справка по JSON DSL для `/role-compile`. Компактное описание — в [SKILL.md](SKILL.md). + +## Структура верхнего уровня + +```json +{ + "name": "ИмяРоли", + "synonym": "Отображаемое имя роли", + "comment": "", + "setForNewObjects": false, + "setForAttributesByDefault": true, + "independentRightsOfChildObjects": false, + "objects": [ ... ], + "templates": [ ... ] +} +``` + +- `name` — программное имя роли (обязательно) +- `synonym` — отображаемое имя (по умолчанию = name) +- `comment` — комментарий (по умолчанию пусто) +- Глобальные флаги — по умолчанию `false`, `true`, `false` + +## Объекты: два формата + +Массив `objects` принимает строки (shorthand) и объекты (полная форма). + +### Строковый shorthand + +``` +"ОбъектМетаданных: @пресет" +"ОбъектМетаданных: Право1, Право2" +``` + +Примеры: +```json +"objects": [ + "Catalog.Номенклатура: @view", + "Document.Реализация: @edit", + "InformationRegister.Цены: Read, Update", + "DataProcessor.Загрузка: @view" +] +``` + +### Объектная форма (для RLS и переопределений) + +```json +{ + "name": "Document.Реализация", + "preset": "view", + "rights": { "Delete": false }, + "rls": { "Read": "#ДляОбъекта(\"\")" } +} +``` + +- `preset` — базовый набор прав (`"view"`, `"edit"`) +- `rights` — переопределения: dict `{"Right": true/false}` или массив `["Right1", "Right2"]` +- `rls` — RLS-ограничения: `{"ИмяПрава": "текст условия"}` + +## Пресеты — подробные таблицы + +Пресеты обозначаются `@` в строковом формате. В объектной форме ключ `preset` без `@`. + +### `@view` — просмотр + +| Тип объекта | Права | +|-------------|-------| +| Catalog, ExchangePlan, Document, ChartOfAccounts, ChartOfCharacteristicTypes, ChartOfCalculationTypes, BusinessProcess, Task | Read, View, InputByString | +| InformationRegister, AccumulationRegister, AccountingRegister, CalculationRegister, Constant, DocumentJournal | Read, View | +| Sequence | Read | +| CommonForm, CommonCommand, Subsystem, FilterCriterion, CommonAttribute | View | +| DataProcessor, Report | Use, View | +| SessionParameter | Get | +| Configuration | ThinClient, WebClient, Output, SaveUserData, MainWindowModeNormal | + +### `@edit` — полное редактирование + +| Тип объекта | Права | +|-------------|-------| +| Catalog, ExchangePlan, ChartOfAccounts, ChartOfCharacteristicTypes, ChartOfCalculationTypes | Read, Insert, Update, Delete, View, Edit, InputByString, InteractiveInsert, InteractiveSetDeletionMark, InteractiveClearDeletionMark | +| Document | Read, Insert, Update, Delete, View, Edit, InputByString, Posting, UndoPosting, InteractiveInsert, InteractiveSetDeletionMark, InteractiveClearDeletionMark, InteractivePosting, InteractivePostingRegular, InteractiveUndoPosting, InteractiveChangeOfPosted | +| BusinessProcess | Read, Insert, Update, Delete, View, Edit, InputByString, Start, InteractiveInsert, InteractiveSetDeletionMark, InteractiveClearDeletionMark, InteractiveActivate, InteractiveStart | +| Task | Read, Insert, Update, Delete, View, Edit, InputByString, Execute, InteractiveInsert, InteractiveSetDeletionMark, InteractiveClearDeletionMark, InteractiveActivate, InteractiveExecute | +| InformationRegister, AccumulationRegister, AccountingRegister, Constant | Read, Update, View, Edit | +| DocumentJournal | Read, View | +| Sequence | Read, Update | +| SessionParameter | Get, Set | +| CommonAttribute | View, Edit | + +Для сервисов (WebService, HTTPService, IntegrationService) пресеты не определены — используй явные права: `"WebService.Имя: Use"`. + +Если пресет не определён для типа объекта — предупреждение с подсказкой доступных. + +## Русские синонимы + +Скрипт автоматически транслирует русские имена в английские. Можно смешивать: `"Справочник.Контрагенты: Чтение, View"` — работает. + +### Типы объектов + +| Русский | English | +|---------|---------| +| `Справочник` | Catalog | +| `Документ` | Document | +| `РегистрСведений` | InformationRegister | +| `РегистрНакопления` | AccumulationRegister | +| `РегистрБухгалтерии` | AccountingRegister | +| `РегистрРасчета` | CalculationRegister | +| `Константа` | Constant | +| `ПланСчетов` | ChartOfAccounts | +| `ПланВидовХарактеристик` | ChartOfCharacteristicTypes | +| `ПланВидовРасчета` | ChartOfCalculationTypes | +| `ПланОбмена` | ExchangePlan | +| `БизнесПроцесс` | BusinessProcess | +| `Задача` | Task | +| `Обработка` | DataProcessor | +| `Отчет` | Report | +| `ОбщаяФорма` | CommonForm | +| `ОбщаяКоманда` | CommonCommand | +| `Подсистема` | Subsystem | +| `КритерийОтбора` | FilterCriterion | +| `ЖурналДокументов` | DocumentJournal | +| `Последовательность` | Sequence | +| `ВебСервис` | WebService | +| `HTTPСервис` | HTTPService | +| `СервисИнтеграции` | IntegrationService | +| `ПараметрСеанса` | SessionParameter | +| `ОбщийРеквизит` | CommonAttribute | +| `Конфигурация` | Configuration | +| `Перечисление` | Enum | + +### Вложенные типы + +| Русский | English | +|---------|---------| +| `Реквизит` | Attribute | +| `СтандартныйРеквизит` | StandardAttribute | +| `ТабличнаяЧасть` | TabularSection | +| `Измерение` | Dimension | +| `Ресурс` | Resource | +| `Команда` | Command | +| `РеквизитАдресации` | AddressingAttribute | + +### Права (основные) + +| Русский | English | +|---------|---------| +| `Чтение` | Read | +| `Добавление` | Insert | +| `Изменение` | Update | +| `Удаление` | Delete | +| `Просмотр` | View | +| `Редактирование` | Edit | +| `ВводПоСтроке` | InputByString | +| `Проведение` | Posting | +| `ОтменаПроведения` | UndoPosting | +| `Использование` | Use | +| `Получение` | Get | +| `Установка` | Set | +| `Старт` | Start | +| `Выполнение` | Execute | +| `УправлениеИтогами` | TotalsControl | + +### Права (интерактивные) + +| Русский | English | +|---------|---------| +| `ИнтерактивноеДобавление` | InteractiveInsert | +| `ИнтерактивнаяПометкаУдаления` | InteractiveSetDeletionMark | +| `ИнтерактивноеСнятиеПометкиУдаления` | InteractiveClearDeletionMark | +| `ИнтерактивноеУдаление` | InteractiveDelete | +| `ИнтерактивноеУдалениеПомеченных` | InteractiveDeleteMarked | +| `ИнтерактивноеПроведение` | InteractivePosting | +| `ИнтерактивноеПроведениеНеоперативное` | InteractivePostingRegular | +| `ИнтерактивнаяОтменаПроведения` | InteractiveUndoPosting | +| `ИнтерактивноеИзменениеПроведенных` | InteractiveChangeOfPosted | +| `ИнтерактивныйСтарт` | InteractiveStart | +| `ИнтерактивнаяАктивация` | InteractiveActivate | +| `ИнтерактивноеВыполнение` | InteractiveExecute | + +### Права (конфигурация) + +| Русский | English | +|---------|---------| +| `Администрирование` | Administration | +| `АдминистрированиеДанных` | DataAdministration | +| `ТонкийКлиент` | ThinClient | +| `ТолстыйКлиент` | ThickClient | +| `ВебКлиент` | WebClient | +| `МобильныйКлиент` | MobileClient | +| `ВнешнееСоединение` | ExternalConnection | +| `Вывод` | Output | +| `СохранениеДанныхПользователя` | SaveUserData | + +## Типы объектов без прав в ролях + +Следующие типы 1С **не могут** иметь права в ролях (не добавляются в `objects`): + +| Тип | Причина | +|-----|---------| +| Enum (Перечисление) | Права наследуются от конфигурации, явное назначение невозможно | +| CommonModule (ОбщийМодуль) | Не имеет собственных прав в роли | +| DefinedType (ОпределяемыйТип) | Тип данных, не объект прав | +| CommonPicture (ОбщаяКартинка) | Ресурс, не объект прав | +| CommonTemplate (ОбщийМакет) | Ресурс, не объект прав | +| Language (Язык) | Конфигурационный элемент | +| FunctionalOption (ФункциональнаяОпция) | Не объект прав | +| FunctionalOptionsParameter | Не объект прав | +| EventSubscription (ПодпискаНаСобытие) | Не объект прав | +| ScheduledJob (РегламентноеЗадание) | Не объект прав | +| StyleItem (ЭлементСтиля) | Ресурс оформления | + +## Шаблоны ограничений (RLS templates) + +```json +"templates": [ + { + "name": "ДляОбъекта(Модификатор)", + "condition": "// текст шаблона\nГДЕ 1=1\n&Модификатор" + } +] +``` + +- `&` в условии автоматически экранируется в `&` в XML +- Ссылка на шаблон в `rls`: `"#ИмяШаблона(\"параметры\")"` — начинается с `#` +- Параметры шаблона можно передавать пустыми: `#ДляОбъекта("")` + +## Примеры + +### 1. Простая роль (только пресеты) + +```json +{ + "name": "ЧтениеНоменклатуры", + "synonym": "Чтение номенклатуры", + "objects": [ + "Catalog.Номенклатура: @view", + "Catalog.Контрагенты: @view", + "DataProcessor.Загрузка: @view" + ] +} +``` + +### 2. Роль для регламентного задания + +```json +{ + "name": "ОбновлениеЦен", + "synonym": "Обновление цен номенклатуры", + "objects": [ + "Catalog.Номенклатура: Read", + "Catalog.Валюты: Read", + "InformationRegister.ЦеныНоменклатуры: Read, Update", + "Constant.ОсновнаяВалюта: Read" + ] +} +``` + +### 3. Роль с RLS + +```json +{ + "name": "ЧтениеДокументовПоОрганизации", + "synonym": "Чтение документов (ограничение по организации)", + "objects": [ + "Catalog.Организации: @view", + { + "name": "Document.РеализацияТоваровУслуг", + "preset": "view", + "rls": { + "Read": "#ДляОбъекта(\"\")" + } + } + ], + "templates": [ + { + "name": "ДляОбъекта(Модификатор)", + "condition": "ГДЕ Организация = &ТекущаяОрганизация" + } + ] +} +``` + +### 4. Роль с русскими синонимами + +```json +{ + "name": "ПросмотрДанных", + "synonym": "Просмотр данных", + "objects": [ + "Справочник.Контрагенты: @view", + "Документ.Реализация: Чтение, Просмотр", + "РегистрСведений.Цены: @edit", + "Обработка.ЗагрузкаДанных: @view" + ] +} +``` + +### 5. Роль с переопределением прав из пресета + +```json +{ + "name": "ОграниченноеРедактирование", + "synonym": "Редактирование без удаления", + "objects": [ + { + "name": "Catalog.Контрагенты", + "preset": "edit", + "rights": { "Delete": false } + } + ] +} +``` diff --git a/.claude/skills/role-compile/scripts/role-compile.ps1 b/.claude/skills/role-compile/scripts/role-compile.ps1 new file mode 100644 index 00000000..83e08de9 --- /dev/null +++ b/.claude/skills/role-compile/scripts/role-compile.ps1 @@ -0,0 +1,713 @@ +param( + [Parameter(Mandatory)] + [string]$JsonPath, + + [Parameter(Mandatory)] + [string]$OutputDir +) + +$ErrorActionPreference = "Stop" +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +# --- 1. Load and validate JSON --- + +if (-not (Test-Path $JsonPath)) { + Write-Error "File not found: $JsonPath" + exit 1 +} + +$json = Get-Content -Raw -Encoding UTF8 $JsonPath +$def = $json | ConvertFrom-Json + +if (-not $def.name) { + Write-Error "JSON must have 'name' field (role programmatic name)" + exit 1 +} + +$roleName = "$($def.name)" +$synonym = if ($def.synonym) { "$($def.synonym)" } else { $roleName } +$comment = if ($def.comment) { "$($def.comment)" } else { "" } + +# --- 2. XML helpers --- + +$script:xmlBuf = $null + +function X { + param([string]$text) + $script:xmlBuf.AppendLine($text) | Out-Null +} + +function Esc-Xml { + param([string]$s) + return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"') +} + +# --- 3. Russian synonyms → canonical English names --- + +$script:typeAliases = @{ + "Справочник" = "Catalog" + "Документ" = "Document" + "РегистрСведений" = "InformationRegister" + "РегистрНакопления" = "AccumulationRegister" + "РегистрБухгалтерии" = "AccountingRegister" + "РегистрРасчета" = "CalculationRegister" + "Константа" = "Constant" + "ПланСчетов" = "ChartOfAccounts" + "ПланВидовХарактеристик" = "ChartOfCharacteristicTypes" + "ПланВидовРасчета" = "ChartOfCalculationTypes" + "ПланОбмена" = "ExchangePlan" + "БизнесПроцесс" = "BusinessProcess" + "Задача" = "Task" + "Обработка" = "DataProcessor" + "Отчет" = "Report" + "ОбщаяФорма" = "CommonForm" + "ОбщаяКоманда" = "CommonCommand" + "Подсистема" = "Subsystem" + "КритерийОтбора" = "FilterCriterion" + "ЖурналДокументов" = "DocumentJournal" + "Последовательность" = "Sequence" + "ВебСервис" = "WebService" + "HTTPСервис" = "HTTPService" + "СервисИнтеграции" = "IntegrationService" + "ПараметрСеанса" = "SessionParameter" + "ОбщийРеквизит" = "CommonAttribute" + "Конфигурация" = "Configuration" + "Перечисление" = "Enum" + # Nested + "Реквизит" = "Attribute" + "СтандартныйРеквизит" = "StandardAttribute" + "ТабличнаяЧасть" = "TabularSection" + "Измерение" = "Dimension" + "Ресурс" = "Resource" + "Команда" = "Command" + "РеквизитАдресации" = "AddressingAttribute" +} + +$script:rightAliases = @{ + "Чтение" = "Read" + "Добавление" = "Insert" + "Изменение" = "Update" + "Удаление" = "Delete" + "Просмотр" = "View" + "Редактирование" = "Edit" + "ВводПоСтроке" = "InputByString" + "Проведение" = "Posting" + "ОтменаПроведения" = "UndoPosting" + "ИнтерактивноеДобавление" = "InteractiveInsert" + "ИнтерактивнаяПометкаУдаления" = "InteractiveSetDeletionMark" + "ИнтерактивноеСнятиеПометкиУдаления" = "InteractiveClearDeletionMark" + "ИнтерактивноеУдаление" = "InteractiveDelete" + "ИнтерактивноеУдалениеПомеченных" = "InteractiveDeleteMarked" + "ИнтерактивноеПроведение" = "InteractivePosting" + "ИнтерактивноеПроведениеНеоперативное" = "InteractivePostingRegular" + "ИнтерактивнаяОтменаПроведения" = "InteractiveUndoPosting" + "ИнтерактивноеИзменениеПроведенных" = "InteractiveChangeOfPosted" + "Использование" = "Use" + "Получение" = "Get" + "Установка" = "Set" + "Старт" = "Start" + "ИнтерактивныйСтарт" = "InteractiveStart" + "ИнтерактивнаяАктивация" = "InteractiveActivate" + "Выполнение" = "Execute" + "ИнтерактивноеВыполнение" = "InteractiveExecute" + "УправлениеИтогами" = "TotalsControl" + "Администрирование" = "Administration" + "АдминистрированиеДанных" = "DataAdministration" + "ТонкийКлиент" = "ThinClient" + "ВебКлиент" = "WebClient" + "ТолстыйКлиент" = "ThickClient" + "ВнешнееСоединение" = "ExternalConnection" + "Вывод" = "Output" + "СохранениеДанныхПользователя" = "SaveUserData" + "МобильныйКлиент" = "MobileClient" +} + +# Translate Russian object name to English (e.g. "Справочник.Контрагенты" → "Catalog.Контрагенты") +function Translate-ObjectName { + param([string]$name) + $parts = $name.Split(".") + $result = @() + foreach ($p in $parts) { + if ($script:typeAliases.ContainsKey($p)) { + $result += $script:typeAliases[$p] + } else { + $result += $p + } + } + return $result -join "." +} + +# Translate Russian right name to English (e.g. "Чтение" → "Read") +function Translate-RightName { + param([string]$name) + if ($script:rightAliases.ContainsKey($name)) { + return $script:rightAliases[$name] + } + return $name +} + +# --- 4. Known rights per object type (source: docs/1c-role-spec.md) --- + +$script:knownRights = @{ + "Configuration" = @( + "Administration","DataAdministration","UpdateDataBaseConfiguration", + "ConfigurationExtensionsAdministration","ActiveUsers","EventLog","ExclusiveMode", + "ThinClient","ThickClient","WebClient","MobileClient","ExternalConnection", + "Automation","Output","SaveUserData","TechnicalSpecialistMode", + "InteractiveOpenExtDataProcessors","InteractiveOpenExtReports", + "AnalyticsSystemClient","CollaborationSystemInfoBaseRegistration", + "MainWindowModeNormal","MainWindowModeWorkplace", + "MainWindowModeEmbeddedWorkplace","MainWindowModeFullscreenWorkplace","MainWindowModeKiosk" + ) + "Catalog" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveDeleteMarked", + "InteractiveDeletePredefinedData","InteractiveSetDeletionMarkPredefinedData", + "InteractiveClearDeletionMarkPredefinedData","InteractiveDeleteMarkedPredefinedData", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "UpdateDataHistoryOfMissingData","ReadDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "Document" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "Posting","UndoPosting", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveDeleteMarked", + "InteractivePosting","InteractivePostingRegular","InteractiveUndoPosting", + "InteractiveChangeOfPosted", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "UpdateDataHistoryOfMissingData","ReadDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "InformationRegister" = @( + "Read","Update","View","Edit","TotalsControl", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "UpdateDataHistoryOfMissingData","ReadDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "AccumulationRegister" = @("Read","Update","View","Edit","TotalsControl") + "AccountingRegister" = @("Read","Update","View","Edit","TotalsControl") + "CalculationRegister" = @("Read","View") + "Constant" = @( + "Read","Update","View","Edit", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "ChartOfAccounts" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete", + "InteractiveDeletePredefinedData","InteractiveSetDeletionMarkPredefinedData", + "InteractiveClearDeletionMarkPredefinedData","InteractiveDeleteMarkedPredefinedData", + "ReadDataHistory","ReadDataHistoryOfMissingData", + "UpdateDataHistory","UpdateDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment" + ) + "ChartOfCharacteristicTypes" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveDeleteMarked", + "InteractiveDeletePredefinedData","InteractiveSetDeletionMarkPredefinedData", + "InteractiveClearDeletionMarkPredefinedData","InteractiveDeleteMarkedPredefinedData", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "ReadDataHistoryOfMissingData","UpdateDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "ChartOfCalculationTypes" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete", + "InteractiveDeletePredefinedData","InteractiveSetDeletionMarkPredefinedData", + "InteractiveClearDeletionMarkPredefinedData","InteractiveDeleteMarkedPredefinedData" + ) + "ExchangePlan" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveDeleteMarked", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "ReadDataHistoryOfMissingData","UpdateDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "BusinessProcess" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "Start","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveActivate","InteractiveStart" + ) + "Task" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "Execute","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveActivate","InteractiveExecute" + ) + "DataProcessor" = @("Use","View") + "Report" = @("Use","View") + "CommonForm" = @("View") + "CommonCommand" = @("View") + "Subsystem" = @("View") + "FilterCriterion" = @("View") + "DocumentJournal" = @("Read","View") + "Sequence" = @("Read","Update") + "WebService" = @("Use") + "HTTPService" = @("Use") + "IntegrationService" = @("Use") + "SessionParameter" = @("Get","Set") + "CommonAttribute" = @("View","Edit") +} + +# Nested objects: Attribute, StandardAttribute, TabularSection, Dimension, Resource, AddressingAttribute +$script:nestedRights = @("View","Edit") +$script:commandRights = @("View") + +# --- 4. Presets (@view, @edit) --- + +$script:presets = @{ + "view" = @{ + "Catalog" = @("Read","View","InputByString") + "ExchangePlan" = @("Read","View","InputByString") + "Document" = @("Read","View","InputByString") + "ChartOfAccounts" = @("Read","View","InputByString") + "ChartOfCharacteristicTypes" = @("Read","View","InputByString") + "ChartOfCalculationTypes" = @("Read","View","InputByString") + "BusinessProcess" = @("Read","View","InputByString") + "Task" = @("Read","View","InputByString") + "InformationRegister" = @("Read","View") + "AccumulationRegister" = @("Read","View") + "AccountingRegister" = @("Read","View") + "CalculationRegister" = @("Read","View") + "Constant" = @("Read","View") + "DocumentJournal" = @("Read","View") + "Sequence" = @("Read") + "CommonForm" = @("View") + "CommonCommand" = @("View") + "Subsystem" = @("View") + "FilterCriterion" = @("View") + "SessionParameter" = @("Get") + "CommonAttribute" = @("View") + "DataProcessor" = @("Use","View") + "Report" = @("Use","View") + "Configuration" = @("ThinClient","WebClient","Output","SaveUserData","MainWindowModeNormal") + } + "edit" = @{ + "Catalog" = @("Read","Insert","Update","Delete","View","Edit","InputByString","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark") + "ExchangePlan" = @("Read","Insert","Update","Delete","View","Edit","InputByString","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark") + "Document" = @("Read","Insert","Update","Delete","View","Edit","InputByString","Posting","UndoPosting","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark","InteractivePosting","InteractivePostingRegular","InteractiveUndoPosting","InteractiveChangeOfPosted") + "ChartOfAccounts" = @("Read","Insert","Update","Delete","View","Edit","InputByString","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark") + "ChartOfCharacteristicTypes" = @("Read","Insert","Update","Delete","View","Edit","InputByString","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark") + "ChartOfCalculationTypes" = @("Read","Insert","Update","Delete","View","Edit","InputByString","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark") + "BusinessProcess" = @("Read","Insert","Update","Delete","View","Edit","InputByString","Start","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark","InteractiveActivate","InteractiveStart") + "Task" = @("Read","Insert","Update","Delete","View","Edit","InputByString","Execute","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark","InteractiveActivate","InteractiveExecute") + "InformationRegister" = @("Read","Update","View","Edit") + "AccumulationRegister" = @("Read","Update","View","Edit") + "AccountingRegister" = @("Read","Update","View","Edit") + "Constant" = @("Read","Update","View","Edit") + "DocumentJournal" = @("Read","View") + "Sequence" = @("Read","Update") + "SessionParameter" = @("Get","Set") + "CommonAttribute" = @("View","Edit") + } +} + +# --- 5. Helpers --- + +function Get-ObjectType { + param([string]$objectName) + $dotIdx = $objectName.IndexOf(".") + if ($dotIdx -lt 0) { return $objectName } + return $objectName.Substring(0, $dotIdx) +} + +function Is-NestedObject { + param([string]$objectName) + return ($objectName.Split(".").Count -ge 3) +} + +function Resolve-Preset { + param([string]$objectType, [string]$presetName) + + $preset = $presetName.TrimStart('@') + + if (-not $script:presets.ContainsKey($preset)) { + Write-Warning "Unknown preset '@$preset'. Known: @view, @edit" + return @() + } + + $typeMap = $script:presets[$preset] + if (-not $typeMap.ContainsKey($objectType)) { + $available = @() + foreach ($k in $script:presets.Keys) { + if ($script:presets[$k].ContainsKey($objectType)) { + $available += "@$k" + } + } + $availStr = if ($available.Count -gt 0) { $available -join ", " } else { "none" } + Write-Warning "Preset '@$preset' not defined for type '$objectType'. Available: $availStr" + return @() + } + + return @($typeMap[$objectType]) +} + +function Validate-RightName { + param([string]$objectName, [string]$rightName) + + $objectType = Get-ObjectType $objectName + + if (Is-NestedObject $objectName) { + if ($objectName -match '\.Command\.') { + if ($rightName -notin $script:commandRights) { + Write-Warning "${objectName}: '$rightName' not valid for commands (only: View)" + return $false + } + } else { + if ($rightName -notin $script:nestedRights) { + Write-Warning "${objectName}: '$rightName' not valid for nested objects (only: View, Edit)" + return $false + } + } + return $true + } + + if (-not $script:knownRights.ContainsKey($objectType)) { + Write-Warning "${objectName}: unknown object type '$objectType'" + return $true + } + + $validRights = $script:knownRights[$objectType] + if ($rightName -notin $validRights) { + $suggestions = @($validRights | Where-Object { + $_ -like "*$rightName*" -or $rightName -like "*$_*" + }) + $sugStr = if ($suggestions.Count -gt 0) { " Did you mean: $($suggestions -join ', ')?" } else { "" } + Write-Warning "${objectName}: unknown right '$rightName'.$sugStr" + return $false + } + + return $true +} + +# --- 6. Parse object entries --- + +function Parse-ObjectEntry { + param($entry) + + # --- String shorthand --- + if ($entry -is [string]) { + $colonIdx = $entry.IndexOf(':') + if ($colonIdx -lt 0) { + Write-Warning "Invalid string '$entry' -- expected 'Object.Name: @preset' or 'Object.Name: Right1, Right2'" + return $null + } + $objName = Translate-ObjectName ($entry.Substring(0, $colonIdx).Trim()) + $rightsStr = $entry.Substring($colonIdx + 1).Trim() + $objectType = Get-ObjectType $objName + + if ($rightsStr.StartsWith('@')) { + $rightNames = @(Resolve-Preset -objectType $objectType -presetName $rightsStr) + } else { + $rightNames = @($rightsStr -split ',\s*' | ForEach-Object { Translate-RightName $_.Trim() } | Where-Object { $_ }) + foreach ($r in $rightNames) { + Validate-RightName -objectName $objName -rightName $r | Out-Null + } + } + + $rights = @() + foreach ($r in $rightNames) { + $rights += ,@{Name=$r; Value="true"; Condition=$null} + } + return @{ Name = $objName; Rights = $rights } + } + + # --- Object form --- + $objName = Translate-ObjectName "$($entry.name)" + if (-not $objName) { + Write-Warning "Object entry missing 'name' field" + return $null + } + + $objectType = Get-ObjectType $objName + $rightsMap = [ordered]@{} + + # 1) Start with preset + if ($entry.preset) { + $presetRights = @(Resolve-Preset -objectType $objectType -presetName "$($entry.preset)") + foreach ($r in $presetRights) { + $rightsMap[$r] = @{Value="true"; Condition=$null} + } + } + + # 2) Apply explicit rights + if ($entry.rights) { + if ($entry.rights -is [array]) { + foreach ($r in $entry.rights) { + $rName = Translate-RightName "$r" + Validate-RightName -objectName $objName -rightName $rName | Out-Null + $rightsMap[$rName] = @{Value="true"; Condition=$null} + } + } else { + foreach ($p in $entry.rights.PSObject.Properties) { + $rName = Translate-RightName $p.Name + Validate-RightName -objectName $objName -rightName $rName | Out-Null + $boolVal = $p.Value + if ($boolVal -eq $true -or "$boolVal" -eq "True") { + $rightsMap[$rName] = @{Value="true"; Condition=$null} + } else { + $rightsMap[$rName] = @{Value="false"; Condition=$null} + } + } + } + } + + # 3) Apply RLS conditions + if ($entry.rls) { + foreach ($p in $entry.rls.PSObject.Properties) { + $rlsRight = Translate-RightName $p.Name + if ($rightsMap.Contains($rlsRight)) { + $rightsMap[$rlsRight].Condition = "$($p.Value)" + } else { + Write-Warning "${objName}: RLS for '$rlsRight' but this right is not in the rights list" + } + } + } + + # Convert to array + $rights = @() + foreach ($k in $rightsMap.Keys) { + $rights += ,@{ + Name = $k + Value = $rightsMap[$k].Value + Condition = $rightsMap[$k].Condition + } + } + + return @{ Name = $objName; Rights = $rights } +} + +# --- 7. Parse all object entries --- + +$parsedObjects = @() +if ($def.objects) { + foreach ($entry in $def.objects) { + $parsed = Parse-ObjectEntry -entry $entry + if ($parsed) { + $parsedObjects += ,$parsed + } + } +} + +# --- 8. Generate UUID --- + +$uuid = [guid]::NewGuid().ToString() + +# --- 9. Emit metadata XML (Roles/Name.xml) --- + +$script:xmlBuf = New-Object System.Text.StringBuilder 4096 + +X '' +X '' +X " " +X ' ' +X " $roleName" +X ' ' +X ' ' +X ' ru' +X " $(Esc-Xml $synonym)" +X ' ' +X ' ' +if ($comment) { + X " $(Esc-Xml $comment)" +} else { + X ' ' +} +X ' ' +X ' ' +X '' + +$metadataXml = $script:xmlBuf.ToString() + +# --- 10. Emit Rights XML (Roles/Name/Ext/Rights.xml) --- + +$script:xmlBuf = New-Object System.Text.StringBuilder 8192 + +X '' +X '' + +# Global flags (defaults match typical 1C roles) +$sfno = if ($null -ne $def.setForNewObjects) { "$($def.setForNewObjects)".ToLower() } else { "false" } +$sfab = if ($null -ne $def.setForAttributesByDefault) { "$($def.setForAttributesByDefault)".ToLower() } else { "true" } +$irco = if ($null -ne $def.independentRightsOfChildObjects) { "$($def.independentRightsOfChildObjects)".ToLower() } else { "false" } + +X " $sfno" +X " $sfab" +X " $irco" + +# Object blocks +$totalRights = 0 +foreach ($obj in $parsedObjects) { + X ' ' + X " $($obj.Name)" + foreach ($right in $obj.Rights) { + X ' ' + X " $($right.Name)" + X " $($right.Value)" + if ($right.Condition) { + X ' ' + X " $(Esc-Xml $right.Condition)" + X ' ' + } + X ' ' + $totalRights++ + } + X ' ' +} + +# RLS restriction templates +$templateCount = 0 +if ($def.templates) { + foreach ($tpl in $def.templates) { + X ' ' + X " $(Esc-Xml "$($tpl.name)")" + X " $(Esc-Xml "$($tpl.condition)")" + X ' ' + $templateCount++ + } +} + +X '' + +$rightsXml = $script:xmlBuf.ToString() + +# --- 11. Write output files --- + +$outDir = if ([System.IO.Path]::IsPathRooted($OutputDir)) { + $OutputDir +} else { + Join-Path (Get-Location) $OutputDir +} + +# Metadata: OutputDir/RoleName.xml +$metadataPath = Join-Path $outDir "$roleName.xml" +if (-not (Test-Path $outDir)) { + New-Item -ItemType Directory -Path $outDir -Force | Out-Null +} + +# Rights: OutputDir/RoleName/Ext/Rights.xml +$roleSubDir = Join-Path $outDir $roleName +$extDir = Join-Path $roleSubDir "Ext" +$rightsPath = Join-Path $extDir "Rights.xml" +if (-not (Test-Path $extDir)) { + New-Item -ItemType Directory -Path $extDir -Force | Out-Null +} + +$enc = New-Object System.Text.UTF8Encoding($true) +[System.IO.File]::WriteAllText($metadataPath, $metadataXml, $enc) +[System.IO.File]::WriteAllText($rightsPath, $rightsXml, $enc) + +# --- 12. Register in Configuration.xml --- + +$configDir = Split-Path $outDir -Parent +$configXmlPath = Join-Path $configDir "Configuration.xml" +$regResult = $null + +if (Test-Path $configXmlPath) { + $configDoc = New-Object System.Xml.XmlDocument + $configDoc.PreserveWhitespace = $true + $configDoc.Load($configXmlPath) + + $nsMgr = New-Object System.Xml.XmlNamespaceManager($configDoc.NameTable) + $nsMgr.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses") + + $childObjects = $configDoc.SelectSingleNode("//md:Configuration/md:ChildObjects", $nsMgr) + if ($childObjects) { + $existing = $childObjects.SelectNodes("md:Role", $nsMgr) + $alreadyExists = $false + foreach ($r in $existing) { + if ($r.InnerText -eq $roleName) { + $alreadyExists = $true + break + } + } + + if ($alreadyExists) { + $regResult = "already" + } else { + $roleElem = $configDoc.CreateElement("Role", "http://v8.1c.ru/8.3/MDClasses") + $roleElem.InnerText = $roleName + + if ($existing.Count -gt 0) { + # Insert after last existing + $lastRole = $existing[$existing.Count - 1] + $newWs = $configDoc.CreateWhitespace("`n`t`t`t") + $childObjects.InsertAfter($newWs, $lastRole) | Out-Null + $childObjects.InsertAfter($roleElem, $newWs) | Out-Null + } else { + # No existing roles — insert before closing whitespace + $lastChild = $childObjects.LastChild + if ($lastChild.NodeType -eq [System.Xml.XmlNodeType]::Whitespace) { + $newWs = $configDoc.CreateWhitespace("`n`t`t`t") + $childObjects.InsertBefore($newWs, $lastChild) | Out-Null + $childObjects.InsertBefore($roleElem, $lastChild) | Out-Null + } else { + $childObjects.AppendChild($configDoc.CreateWhitespace("`n`t`t`t")) | Out-Null + $childObjects.AppendChild($roleElem) | Out-Null + $childObjects.AppendChild($configDoc.CreateWhitespace("`n`t`t")) | Out-Null + } + } + + # Save + $cfgSettings = New-Object System.Xml.XmlWriterSettings + $cfgSettings.Encoding = New-Object System.Text.UTF8Encoding($true) + $cfgSettings.Indent = $false + $stream = New-Object System.IO.FileStream($configXmlPath, [System.IO.FileMode]::Create) + $writer = [System.Xml.XmlWriter]::Create($stream, $cfgSettings) + $configDoc.Save($writer) + $writer.Close() + $stream.Close() + + $regResult = "added" + } + } else { + $regResult = "no-childobj" + } +} else { + $regResult = "no-config" +} + +# --- 13. Summary --- + +Write-Host "[OK] Role '$roleName' compiled" +Write-Host " UUID: $uuid" +Write-Host " Metadata: $metadataPath" +Write-Host " Rights: $rightsPath" +Write-Host " Objects: $($parsedObjects.Count), Rights: $totalRights, Templates: $templateCount" +switch ($regResult) { + "added" { Write-Host " Configuration.xml: $roleName added to ChildObjects" } + "already" { Write-Host " Configuration.xml: $roleName already registered" } + "no-childobj" { Write-Warning "Configuration.xml found but not found" } + "no-config" { Write-Warning "Configuration.xml not found at $configXmlPath — register manually" } +} diff --git a/.claude/skills/role-info/scripts/role-info.ps1 b/.claude/skills/role-info/scripts/role-info.ps1 index 4c93908c..9de9c9f1 100644 --- a/.claude/skills/role-info/scripts/role-info.ps1 +++ b/.claude/skills/role-info/scripts/role-info.ps1 @@ -7,6 +7,7 @@ ) $ErrorActionPreference = 'Stop' +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 # --- Output helper (always collect, paginate at the end) --- $script:lines = @() diff --git a/.claude/skills/role-validate/SKILL.md b/.claude/skills/role-validate/SKILL.md new file mode 100644 index 00000000..03c21583 --- /dev/null +++ b/.claude/skills/role-validate/SKILL.md @@ -0,0 +1,105 @@ +--- +name: role-validate +description: Валидация структурной корректности роли 1С (Rights.xml) — формат, права, RLS, шаблоны +argument-hint: +allowed-tools: + - Bash + - Read +--- + +# /role-validate — валидация роли 1С + +Проверяет корректность `Rights.xml` роли: формат XML, namespace, глобальные флаги, типы объектов, имена прав, RLS-ограничения, шаблоны. Опционально проверяет метаданные роли (UUID, имя, синоним). + +## Использование + +``` +/role-validate [MetadataPath] +``` + +## Запуск скрипта + +```powershell +powershell.exe -NoProfile -File .claude\skills\role-validate\scripts\role-validate.ps1 -RightsPath [-MetadataPath ] [-OutFile ] +``` + +### Параметры + +| Параметр | Обязательный | Описание | +|----------|:------------:|----------| +| `-RightsPath` | да | Путь к `Rights.xml` роли | +| `-MetadataPath` | нет | Путь к метаданным роли (`Roles/ИмяРоли.xml`) | +| `-OutFile` | нет | Записать результат в файл (UTF-8 BOM). Без этого — вывод в консоль | + +**Важно:** Для кириллических путей используй `-OutFile` и читай результат через Read tool. + +## Проверки + +### Rights.xml +1. XML well-formed — парсинг без ошибок +2. Корневой элемент `` с namespace `http://v8.1c.ru/8.2/roles` +3. Три глобальных флага: `setForNewObjects`, `setForAttributesByDefault`, `independentRightsOfChildObjects` +4. Для каждого ``: + - `` не пуст + - Тип объекта распознан (Catalog, Document, InformationRegister и т.д.) + - Каждое `` имеет `` и `` (`true`/`false`) + - Имя права валидно для данного типа объекта (с подсказкой при опечатке) +5. Вложенные объекты (3+ сегмента через `.`): допустимы только View, Edit (или Use для IntegrationServiceChannel) +6. RLS ``: `` не пуст +7. Шаблоны ``: `` и `` не пусты + +### Метаданные (опционально) +- Элемент `` найден +- UUID в корректном формате +- `` не пуст +- `` присутствует + +## Формат вывода + +``` +Validating: Roles/МояРоль/Ext/Rights.xml + OK XML well-formed + OK Root element: with correct namespace + OK 3 global flags present + WARN Document.Реализация: unknown right 'Rea'. Did you mean: Read? + OK 12 objects, 45 rights + OK 2 RLS restrictions + OK 1 templates: ДляОбъекта + + OK Metadata: UUID valid (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) + OK Metadata: Name = МояРоль + OK Metadata: Synonym present +--- +Result: 0 error(s), 1 warning(s) +``` + +### Уровни сообщений + +| Маркер | Значение | +|--------|----------| +| `OK` | Проверка пройдена | +| `WARN` | Предупреждение (неизвестный тип объекта, подозрительное имя права) | +| `ERR` | Ошибка (невалидный XML, отсутствие обязательных элементов) | + +Код возврата: `0` — без ошибок, `1` — есть ошибки. + +## Примеры + +### Только Rights.xml + +``` +/role-validate upload/acc_8.3.20/Roles/БазовыеПраваБП/Ext/Rights.xml +``` + +### С проверкой метаданных + +``` +/role-validate Roles/МояРоль/Ext/Rights.xml Roles/МояРоль.xml +``` + +### Верификация после /role-compile + +``` +/role-compile role.json Roles/ +/role-validate Roles/МояРоль/Ext/Rights.xml Roles/МояРоль.xml +``` diff --git a/.claude/skills/role-validate/scripts/role-validate.ps1 b/.claude/skills/role-validate/scripts/role-validate.ps1 new file mode 100644 index 00000000..ccad6b59 --- /dev/null +++ b/.claude/skills/role-validate/scripts/role-validate.ps1 @@ -0,0 +1,472 @@ +param( + [Parameter(Mandatory)] + [string]$RightsPath, + + [string]$MetadataPath, + + [string]$OutFile +) + +$ErrorActionPreference = "Stop" +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +# --- 1. Known rights per object type --- + +$script:knownRights = @{ + "Configuration" = @( + "Administration","DataAdministration","UpdateDataBaseConfiguration", + "ConfigurationExtensionsAdministration","ActiveUsers","EventLog","ExclusiveMode", + "ThinClient","ThickClient","WebClient","MobileClient","ExternalConnection", + "Automation","Output","SaveUserData","TechnicalSpecialistMode", + "InteractiveOpenExtDataProcessors","InteractiveOpenExtReports", + "AnalyticsSystemClient","CollaborationSystemInfoBaseRegistration", + "MainWindowModeNormal","MainWindowModeWorkplace", + "MainWindowModeEmbeddedWorkplace","MainWindowModeFullscreenWorkplace","MainWindowModeKiosk" + ) + "Catalog" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveDeleteMarked", + "InteractiveDeletePredefinedData","InteractiveSetDeletionMarkPredefinedData", + "InteractiveClearDeletionMarkPredefinedData","InteractiveDeleteMarkedPredefinedData", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "UpdateDataHistoryOfMissingData","ReadDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "Document" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "Posting","UndoPosting", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveDeleteMarked", + "InteractivePosting","InteractivePostingRegular","InteractiveUndoPosting", + "InteractiveChangeOfPosted", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "UpdateDataHistoryOfMissingData","ReadDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "InformationRegister" = @( + "Read","Update","View","Edit","TotalsControl", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "UpdateDataHistoryOfMissingData","ReadDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "AccumulationRegister" = @("Read","Update","View","Edit","TotalsControl") + "AccountingRegister" = @("Read","Update","View","Edit","TotalsControl") + "CalculationRegister" = @("Read","View") + "Constant" = @( + "Read","Update","View","Edit", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "ChartOfAccounts" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete", + "InteractiveDeletePredefinedData","InteractiveSetDeletionMarkPredefinedData", + "InteractiveClearDeletionMarkPredefinedData","InteractiveDeleteMarkedPredefinedData", + "ReadDataHistory","ReadDataHistoryOfMissingData", + "UpdateDataHistory","UpdateDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment" + ) + "ChartOfCharacteristicTypes" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveDeleteMarked", + "InteractiveDeletePredefinedData","InteractiveSetDeletionMarkPredefinedData", + "InteractiveClearDeletionMarkPredefinedData","InteractiveDeleteMarkedPredefinedData", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "ReadDataHistoryOfMissingData","UpdateDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "ChartOfCalculationTypes" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete", + "InteractiveDeletePredefinedData","InteractiveSetDeletionMarkPredefinedData", + "InteractiveClearDeletionMarkPredefinedData","InteractiveDeleteMarkedPredefinedData" + ) + "ExchangePlan" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveDeleteMarked", + "ReadDataHistory","ViewDataHistory","UpdateDataHistory", + "ReadDataHistoryOfMissingData","UpdateDataHistoryOfMissingData", + "UpdateDataHistorySettings","UpdateDataHistoryVersionComment", + "EditDataHistoryVersionComment","SwitchToDataHistoryVersion" + ) + "BusinessProcess" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "Start","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveActivate","InteractiveStart" + ) + "Task" = @( + "Read","Insert","Update","Delete","View","Edit","InputByString", + "Execute","InteractiveInsert","InteractiveSetDeletionMark","InteractiveClearDeletionMark", + "InteractiveDelete","InteractiveActivate","InteractiveExecute" + ) + "DataProcessor" = @("Use","View") + "Report" = @("Use","View") + "CommonForm" = @("View") + "CommonCommand" = @("View") + "Subsystem" = @("View") + "FilterCriterion" = @("View") + "DocumentJournal" = @("Read","View") + "Sequence" = @("Read","Update") + "WebService" = @("Use") + "HTTPService" = @("Use") + "IntegrationService" = @("Use") + "SessionParameter" = @("Get","Set") + "CommonAttribute" = @("View","Edit") +} + +$script:nestedRights = @("View","Edit") +$script:channelRights = @("Use") +$script:commandRights = @("View") + +# --- 2. Output helpers --- + +$script:lines = @() +$script:errors = 0 +$script:warnings = 0 + +function Out-OK { + param([string]$msg) + $script:lines += " OK $msg" +} + +function Out-WARN { + param([string]$msg) + $script:warnings++ + $script:lines += " WARN $msg" +} + +function Out-ERR { + param([string]$msg) + $script:errors++ + $script:lines += " ERR $msg" +} + +function Get-ObjectType { + param([string]$name) + $dotIdx = $name.IndexOf(".") + if ($dotIdx -lt 0) { return $name } + return $name.Substring(0, $dotIdx) +} + +function Is-NestedObject { + param([string]$name) + return ($name.Split(".").Count -ge 3) +} + +function Find-Similar { + param([string]$needle, [string[]]$haystack) + $result = @($haystack | Where-Object { + $_ -like "*$needle*" -or $needle -like "*$_*" + }) + if ($result.Count -gt 3) { $result = $result[0..2] } + return $result +} + +# --- 3. Validate Rights.xml --- + +$script:lines += "Validating: $RightsPath" + +if (-not (Test-Path $RightsPath)) { + Out-ERR "File not found: $RightsPath" + $script:lines += "---" + $script:lines += "Result: $($script:errors) error(s), $($script:warnings) warning(s)" + $output = $script:lines -join "`n" + if ($OutFile) { + $enc = New-Object System.Text.UTF8Encoding($true) + [System.IO.File]::WriteAllText($OutFile, $output, $enc) + } else { + Write-Host $output + } + exit 1 +} + +# 3a. Parse XML +try { + [xml]$xml = Get-Content -Path $RightsPath -Encoding UTF8 + Out-OK "XML well-formed" +} catch { + Out-ERR "XML parse error: $($_.Exception.Message)" + $script:lines += "---" + $script:lines += "Result: $($script:errors) error(s), $($script:warnings) warning(s)" + $output = $script:lines -join "`n" + if ($OutFile) { + $enc = New-Object System.Text.UTF8Encoding($true) + [System.IO.File]::WriteAllText($OutFile, $output, $enc) + } else { + Write-Host $output + } + exit 1 +} + +$root = $xml.DocumentElement +$rightsNs = "http://v8.1c.ru/8.2/roles" + +# 3b. Check root element +if ($root.LocalName -ne "Rights") { + Out-ERR "Root element is '$($root.LocalName)', expected 'Rights'" +} elseif ($root.NamespaceURI -ne $rightsNs) { + Out-WARN "Namespace is '$($root.NamespaceURI)', expected '$rightsNs'" +} else { + Out-OK "Root element: with correct namespace" +} + +# 3c. Global flags +$flagNames = @("setForNewObjects","setForAttributesByDefault","independentRightsOfChildObjects") +$flagsFound = 0 +foreach ($fn in $flagNames) { + $node = $root.GetElementsByTagName($fn, $rightsNs) + if ($node.Count -gt 0) { + $val = $node[0].InnerText + if ($val -ne "true" -and $val -ne "false") { + Out-WARN "$fn = '$val' (expected 'true' or 'false')" + } + $flagsFound++ + } else { + Out-WARN "Missing global flag: $fn" + } +} +if ($flagsFound -eq 3) { + Out-OK "3 global flags present" +} + +# 3d. Objects +$objects = $root.GetElementsByTagName("object", $rightsNs) +$objCount = $objects.Count +$rightCount = 0 +$rlsCount = 0 + +foreach ($obj in $objects) { + $objName = "" + foreach ($child in $obj.ChildNodes) { + if ($child.LocalName -eq "name") { + $objName = $child.InnerText + break + } + } + + if (-not $objName) { + Out-ERR "Object without " + continue + } + + $objectType = Get-ObjectType $objName + $isNested = Is-NestedObject $objName + + # Check object type is known + if (-not $isNested -and -not $script:knownRights.ContainsKey($objectType)) { + Out-WARN "${objName}: unknown object type '$objectType'" + } + + # Check rights + foreach ($child in $obj.ChildNodes) { + if ($child.LocalName -ne "right") { continue } + + $rName = "" + $rValue = "" + $hasRLS = $false + + foreach ($rc in $child.ChildNodes) { + if ($rc.LocalName -eq "name") { $rName = $rc.InnerText } + if ($rc.LocalName -eq "value") { $rValue = $rc.InnerText } + if ($rc.LocalName -eq "restrictionByCondition") { + $hasRLS = $true + $rlsCount++ + # Check condition not empty + $condNode = $null + foreach ($rcc in $rc.ChildNodes) { + if ($rcc.LocalName -eq "condition") { $condNode = $rcc } + } + if (-not $condNode -or -not $condNode.InnerText) { + Out-WARN "${objName}: RLS condition for '$rName' is empty" + } + } + } + + if (-not $rName) { + Out-ERR "${objName}: without " + continue + } + + if ($rValue -ne "true" -and $rValue -ne "false") { + Out-ERR "${objName}: right '$rName' has invalid value '$rValue'" + continue + } + + $rightCount++ + + # Validate right name + if ($isNested) { + if ($objName -match '\.Command\.') { + if ($rName -notin $script:commandRights) { + Out-WARN "${objName}: '$rName' not valid for commands (only: View)" + } + } elseif ($objName -match '\.IntegrationServiceChannel\.') { + if ($rName -notin $script:channelRights) { + Out-WARN "${objName}: '$rName' not valid for channels (only: Use)" + } + } else { + if ($rName -notin $script:nestedRights) { + Out-WARN "${objName}: '$rName' not valid for nested objects (only: View, Edit)" + } + } + } elseif ($script:knownRights.ContainsKey($objectType)) { + $validRights = $script:knownRights[$objectType] + if ($rName -notin $validRights) { + $similar = Find-Similar -needle $rName -haystack $validRights + $sugStr = if ($similar.Count -gt 0) { " Did you mean: $($similar -join ', ')?" } else { "" } + Out-WARN "${objName}: unknown right '$rName'.$sugStr" + } + } + } +} + +Out-OK "$objCount objects, $rightCount rights" +if ($rlsCount -gt 0) { + Out-OK "$rlsCount RLS restrictions" +} + +# 3e. Templates +$templates = $root.GetElementsByTagName("restrictionTemplate", $rightsNs) +if ($templates.Count -gt 0) { + $tplNames = @() + foreach ($tpl in $templates) { + $tName = "" + $tCond = "" + foreach ($child in $tpl.ChildNodes) { + if ($child.LocalName -eq "name") { $tName = $child.InnerText } + if ($child.LocalName -eq "condition") { $tCond = $child.InnerText } + } + if (-not $tName) { + Out-WARN "Restriction template without " + } else { + $parenIdx = $tName.IndexOf("(") + $shortName = if ($parenIdx -gt 0) { $tName.Substring(0, $parenIdx) } else { $tName } + $tplNames += $shortName + } + if (-not $tCond) { + Out-WARN "Template '$tName': empty " + } + } + Out-OK "$($templates.Count) templates: $($tplNames -join ', ')" +} + +# --- 4. Validate metadata (optional) --- + +if ($MetadataPath) { + $script:lines += "" + + if (-not (Test-Path $MetadataPath)) { + Out-ERR "Metadata file not found: $MetadataPath" + } else { + try { + [xml]$metaXml = Get-Content -Path $MetadataPath -Encoding UTF8 + $roleNode = $metaXml.DocumentElement.SelectSingleNode("//*[local-name()='Role']") + if (-not $roleNode) { + Out-ERR "Metadata: element not found" + } else { + $uuid = $roleNode.GetAttribute("uuid") + if ($uuid -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') { + Out-OK "Metadata: UUID valid ($uuid)" + } else { + Out-ERR "Metadata: invalid UUID format '$uuid'" + } + + $nameNode = $roleNode.SelectSingleNode(".//*[local-name()='Name']") + if ($nameNode -and $nameNode.InnerText) { + Out-OK "Metadata: Name = $($nameNode.InnerText)" + } else { + Out-ERR "Metadata: is empty or missing" + } + + $synNode = $roleNode.SelectSingleNode(".//*[local-name()='Synonym']") + if ($synNode -and $synNode.InnerXml) { + Out-OK "Metadata: Synonym present" + } else { + Out-WARN "Metadata: is empty" + } + } + } catch { + Out-ERR "Metadata XML parse error: $($_.Exception.Message)" + } + } +} + +# --- 5. Check registration in Configuration.xml --- + +# Infer paths: RightsPath = .../Roles/Name/Ext/Rights.xml +$extDir2 = Split-Path (Resolve-Path $RightsPath).Path -Parent +$roleDir2 = Split-Path $extDir2 -Parent +$rolesDir2 = Split-Path $roleDir2 -Parent +$configDir2 = Split-Path $rolesDir2 -Parent +$configXmlPath2 = Join-Path $configDir2 "Configuration.xml" +$inferredRoleName = Split-Path $roleDir2 -Leaf + +# Use metadata name if available +if ($MetadataPath -and (Test-Path $MetadataPath)) { + try { + [xml]$metaXml2 = Get-Content -Path $MetadataPath -Encoding UTF8 + $nameNode2 = $metaXml2.DocumentElement.SelectSingleNode("//*[local-name()='Role']//*[local-name()='Name']") + if ($nameNode2 -and $nameNode2.InnerText) { + $inferredRoleName = $nameNode2.InnerText + } + } catch { } +} + +if (Test-Path $configXmlPath2) { + $script:lines += "" + try { + [xml]$cfgXml = Get-Content -Path $configXmlPath2 -Encoding UTF8 + $cfgNs = New-Object System.Xml.XmlNamespaceManager($cfgXml.NameTable) + $cfgNs.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses") + $childObj = $cfgXml.SelectSingleNode("//md:Configuration/md:ChildObjects", $cfgNs) + if ($childObj) { + $roleNodes = $childObj.SelectNodes("md:Role", $cfgNs) + $found = $false + foreach ($rn in $roleNodes) { + if ($rn.InnerText -eq $inferredRoleName) { + $found = $true + break + } + } + if ($found) { + Out-OK "Configuration.xml: $inferredRoleName registered" + } else { + Out-WARN "Configuration.xml: $inferredRoleName NOT found in ChildObjects" + } + } + } catch { + Out-WARN "Configuration.xml: parse error — $($_.Exception.Message)" + } +} + +# --- 6. Summary --- + +$script:lines += "---" +$script:lines += "Result: $($script:errors) error(s), $($script:warnings) warning(s)" + +$output = $script:lines -join "`n" + +if ($OutFile) { + $outPath = if ([System.IO.Path]::IsPathRooted($OutFile)) { $OutFile } else { Join-Path (Get-Location) $OutFile } + $outDir = [System.IO.Path]::GetDirectoryName($outPath) + if (-not (Test-Path $outDir)) { + New-Item -ItemType Directory -Path $outDir -Force | Out-Null + } + $enc = New-Object System.Text.UTF8Encoding($true) + [System.IO.File]::WriteAllText($outPath, $output, $enc) + Write-Host "[OK] Validation result written to: $outPath" +} else { + Write-Host $output +} + +if ($script:errors -gt 0) { exit 1 } else { exit 0 } diff --git a/README.md b/README.md index ef4e042d..a1adf052 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ | Внешние обработки (EPF) | 10 навыков `/epf-*` | Создание, модификация, сборка обработок из XML-исходников | [Подробнее](docs/epf-guide.md) | | Табличный документ (MXL) | 4 навыка `/mxl-*` | Анализ, создание, компиляция макетов печатных форм | [Подробнее](docs/mxl-guide.md) | | Управляемые формы (Form) | 6 навыков `/form-*` | Создание, анализ, генерация, модификация, валидация управляемых форм | [Подробнее](docs/form-guide.md) | -| Роли (Role) | 2 навыка `/role-*` | Анализ прав роли, создание роли из описания | [Подробнее](docs/role-guide.md) | +| Роли (Role) | 3 навыка `/role-*` | Анализ прав роли, создание из JSON DSL, валидация | [Подробнее](docs/role-guide.md) | | Утилиты | `/img-grid` | Наложение сетки на изображение для определения пропорций колонок | — | ## Требования @@ -40,6 +40,7 @@ - [MXL DSL](docs/mxl-dsl-spec.md) — JSON-формат описания макета для `/mxl-compile` и `/mxl-decompile` - [Form DSL](docs/form-dsl-spec.md) — JSON-формат описания формы для `/form-compile` - [Роли (Rights.xml)](docs/1c-role-spec.md) — XML-формат прав роли, типы объектов, RLS +- [Role DSL](docs/role-dsl-spec.md) — JSON-формат описания ролей для `/role-compile` ## Структура репозитория @@ -66,7 +67,8 @@ ├── form-edit/ # Добавление элементов в форму ├── form-patterns/ # Справочник паттернов компоновки форм ├── role-info/ # Анализ прав роли -├── role-compile/ # Создание роли из описания +├── role-compile/ # Создание роли из JSON DSL +├── role-validate/ # Валидация роли └── img-grid/ # Сетка для анализа изображений docs/ ├── epf-guide.md # Гайд: внешние обработки diff --git a/docs/1c-dcs-spec.md b/docs/1c-dcs-spec.md new file mode 100644 index 00000000..6c07c0a3 --- /dev/null +++ b/docs/1c-dcs-spec.md @@ -0,0 +1,957 @@ +# Спецификация XML-формата схемы компоновки данных 1С (DCS) + +Спецификация формата `DataCompositionSchema` — макетов типа «Схема компоновки данных» в конфигурации 1С:Предприятие 8.3. +Составлена на основе анализа 930 схем конфигурации «Бухгалтерия предприятия 3.0.180» (платформа 8.3.24). + +--- + +## 0. Файловая структура + +### Два файла на каждую схему + +``` +<Объект>/Templates/ + ИмяМакета.xml ← метаданные (UUID, имя, TemplateType) + ИмяМакета/ + Ext/ + Template.xml ← тело схемы (DataCompositionSchema) +``` + +Типичные имена макетов: `ОсновнаяСхемаКомпоновкиДанных`, `СхемаКомпоновкиДанных`, произвольные. + +### Метаданные макета — шаблон + +```xml + + + + +``` + +Значение `TemplateType` для DCS всегда: **`DataCompositionSchema`**. + +### Где встречаются DCS-макеты + +| Тип объекта метаданных | Частота | Примечание | +|---|---|---| +| Reports (Отчёты) | ~420 | Основное место — каждый отчёт СКД | +| DataProcessors (Обработки) | ~11 | Обработки с отчётными функциями | +| Enums (Перечисления) | ~20 | Дополнительные ссылки | +| Catalogs (Справочники) | ~5 | Запросы к справочным данным | +| DocumentJournals | ~4 | Журналы документов | +| CommonTemplates | ~3 | Общие макеты | +| InformationRegisters | ~2 | Регистры сведений | +| Documents (Документы) | ~1 | Редко | + +--- + +## 1. Пространства имён + +Корневой элемент — ``. + +```xml + +``` + +| Префикс | URI | Назначение | +|---|---|---| +| *(default)* | `.../data-composition-system/schema` | Элементы схемы (dataSource, dataSet, field, parameter, ...) | +| `dcscom` | `.../data-composition-system/common` | Общие типы СКД (dimension, account, role, ...) | +| `dcscor` | `.../data-composition-system/core` | Ядро СКД (Field, SettingsParameterValue, ChoiceParameterLinks, ...) | +| `dcsset` | `.../data-composition-system/settings` | Настройки варианта (selection, filter, order, group, ...) | +| `v8` | `.../data/core` | Типы данных ядра (LocalStringType, Type, StandardPeriod, ...) | +| `v8ui` | `.../data/ui` | UI-типы (HorizontalAlign, ...) | +| `xs` | `.../XMLSchema` | Стандартные XSD-типы (string, dateTime, boolean, decimal, ...) | +| `xsi` | `.../XMLSchema-instance` | Атрибуты экземпляра (xsi:type, xsi:nil) | + +Дополнительные пространства имён (появляются в `settingsVariant`): + +| Префикс | URI | Где | +|---|---|---| +| `style` | `http://v8.1c.ru/8.1/data/ui/style` | В settings — стили оформления | +| `sys` | `http://v8.1c.ru/8.1/data/ui/fonts/system` | В settings — системные шрифты | +| `web` | `http://v8.1c.ru/8.1/data/ui/colors/web` | В settings — веб-цвета | +| `win` | `http://v8.1c.ru/8.1/data/ui/colors/windows` | В settings — цвета Windows | + +--- + +## 2. Общая структура DataCompositionSchema + +Элементы верхнего уровня (порядок фиксирован): + +``` +DataCompositionSchema +├── dataSource* — источники данных (раздел 3) +├── dataSet* — наборы данных (раздел 4) +├── dataSetLink* — связи между наборами (раздел 5) +├── calculatedField* — вычисляемые поля (раздел 6) +├── totalField* — итоговые поля (раздел 7) +├── parameter* — параметры схемы (раздел 8) +├── template* — макеты областей (раздел 9) +├── groupTemplate* — привязки макетов группировок (раздел 10) +├── settingsVariant* — варианты настроек (раздел 11) +``` + +`*` — 0..N элементов. + +Минимальная DCS содержит: 1 dataSource + 1 dataSet + 1 settingsVariant. + +--- + +## 3. Источники данных (dataSource) + +```xml + + ИсточникДанных1 + Local + +``` + +| Элемент | Обязат. | Описание | +|---|---|---| +| `name` | да | Уникальное имя, на которое ссылаются наборы данных | +| `dataSourceType` | да | Тип: `Local` (текущая информационная база) или `External` (внешний) | + +В подавляющем большинстве случаев — один источник `Local`. Имя произвольное: `ИсточникДанных1`, `ИнформационнаяБаза` и т.п. + +--- + +## 4. Наборы данных (dataSet) + +Тип набора определяется атрибутом `xsi:type`. Три типа: + +### 4.1. DataSetQuery — запрос + +Самый распространённый тип. Содержит SQL-подобный запрос на языке 1С. + +```xml + + НаборДанных1 + ... + ИсточникДанных1 + ВЫБРАТЬ ... ИЗ ... + false + +``` + +| Элемент | Обязат. | Описание | +|---|---|---| +| `name` | да | Уникальное имя набора | +| `field` | нет | Описания полей (раздел 4.4) | +| `dataSource` | да | Ссылка на имя dataSource | +| `query` | да | Текст запроса на языке 1С (XML-экранирование: `&` для `&`, `>` для `>`) | +| `autoFillFields` | нет | `false` — отключить автозаполнение полей из запроса (по умолчанию `true`) | + +#### Особенности запросов в DCS + +- Параметры: `&ИмяПараметра` (в XML: `&ИмяПараметра`) +- Авторазметка полей в фигурных скобках: `{ВЫБРАТЬ ...}`, `{ГДЕ ...}`, `{ЛЕВОЕ СОЕДИНЕНИЕ ...}` — позволяют СКД автоматически модифицировать запрос +- Пакетные запросы: несколько запросов через `; ////////////////` +- Временные таблицы: `ПОМЕСТИТЬ ИмяВТ`, `ИНДЕКСИРОВАТЬ ПО` + +### 4.2. DataSetObject — объект + +Данные берутся из программно заполненной таблицы значений. + +```xml + + НаборДанных1 + ... + ИсточникДанных1 + ТаблицаПроверки + +``` + +| Элемент | Обязат. | Описание | +|---|---|---| +| `objectName` | да | Имя объекта (таблицы значений), передаваемого программно | + +### 4.3. DataSetUnion — объединение + +Объединяет поля из нескольких наборов. Сам не содержит запросов — объединяет подчинённые наборы. + +```xml + + РасчетНалога + ... + + ДанныеПоСтоимости + ... + + + ДанныеПоКадастру + ... + + +``` + +| Элемент | Обязат. | Описание | +|---|---|---| +| `field` | нет | Поля объединения (описывают результирующие колонки) | +| `item` | да | Вложенные наборы (DataSetQuery или другие) | + +### 4.4. Поля набора данных (field) + +Каждое поле — элемент ``: + +```xml + + ОстаточнаяСтоимость + ОстаточнаяСтоимость + + <v8:item> + <v8:lang>ru</v8:lang> + <v8:content>Остаточная стоимость</v8:content> + </v8:item> + + + true + + + true + + + xs:string + + 11 + Variable + + + + + Формат + ЧДЦ=2 + + + ... + ... + +``` + +#### Элементы поля + +| Элемент | Обязат. | Описание | +|---|---|---| +| `dataPath` | да | Путь к данным (имя поля в результате СКД). Через точку — реквизиты: `Номенклатура.Артикул` | +| `field` | да | Имя поля в запросе (может отличаться от dataPath) | +| `title` | нет | Локализованный заголовок (`v8:LocalStringType`) | +| `useRestriction` | нет | Ограничения использования поля (раздел 4.5) | +| `attributeUseRestriction` | нет | Ограничения использования реквизитов поля (раздел 4.5) | +| `role` | нет | Роль поля в СКД (раздел 4.6) | +| `valueType` | нет | Тип значения поля (раздел 4.7) | +| `appearance` | нет | Оформление — список параметров `dcscor:item` (раздел 4.8) | +| `inputParameters` | нет | Параметры ввода / связи параметров выбора (раздел 4.9) | +| `presentationExpression` | нет | Выражение для формирования представления (на языке 1С) | + +### 4.5. Ограничения использования поля (useRestriction / attributeUseRestriction) + +```xml + + true + true + true + true + +``` + +Каждый подэлемент — `true`/`false` (по умолчанию `false` = разрешено). Можно указывать подмножество. + +`attributeUseRestriction` — аналогичная структура, применяется к реквизитам (дочерним полям) поля. + +### 4.6. Роли полей (role) + +```xml + + true + true + Счет.Вид + +``` + +| Подэлемент | Описание | +|---|---| +| `dcscom:dimension` | Поле является измерением (`true`/`false`) | +| `dcscom:account` | Поле является счётом | +| `dcscom:accountTypeExpression` | Выражение для определения типа счёта | +| `dcscom:balance` | Поле является остатком | +| `dcscom:balanceGroup` | Группа остатка | +| `dcscom:period` | Поле — период | + +### 4.7. Тип значения (valueType) + +```xml + + xs:string + + 11 + Variable + + +``` + +Типы: `xs:string`, `xs:dateTime`, `xs:decimal`, `xs:boolean`, ссылочные (`d4p1:CatalogRef.Номенклатура`). + +Квалификаторы: +- `v8:StringQualifiers` → `v8:Length`, `v8:AllowedLength` (Fixed/Variable) +- `v8:DateQualifiers` → `v8:DateFractions` (Date/Time/DateTime) +- `v8:NumberQualifiers` → `v8:Digits`, `v8:FractionDigits`, `v8:AllowedSign` (Any/Nonnegative) + +### 4.8. Оформление полей (appearance) + +Список параметров оформления: + +```xml + + + Формат + ЧДЦ=2 + + + ГоризонтальноеПоложение + Center + + +``` + +Типичные параметры оформления: + +| Параметр | Тип значения | Пример | +|---|---|---| +| `Формат` | `xs:string` | `ЧДЦ=2`, `ЧГ=0`, `ЧН=0`, `ДФ=dd.MM.yyyy`, `Л=ru; ДФ=ММММ` | +| `ГоризонтальноеПоложение` | `v8ui:HorizontalAlign` | `Left`, `Center`, `Right` | + +### 4.9. Параметры ввода (inputParameters) + +Связи параметров выбора для интерактивных полей: + +```xml + + + СвязиПараметровВыбора + + + Отбор.Владелец + Организация + Clear + + + + +``` + +Используется для каскадных зависимостей в пользовательских настройках (например, подразделение зависит от организации). + +--- + +## 5. Связи между наборами данных (dataSetLink) + +Позволяют передавать параметры из одного набора в другой: + +```xml + + Периоды + ДанныеТ13 + НачалоМесяца + Месяц + НачалоМесяца + false + +``` + +| Элемент | Обязат. | Описание | +|---|---|---| +| `sourceDataSet` | да | Имя набора-источника | +| `destinationDataSet` | да | Имя целевого набора | +| `sourceExpression` | да | Выражение из источника (поле или формула) | +| `destinationExpression` | да | Выражение для сопоставления в целевом наборе | +| `parameter` | нет | Имя параметра для передачи значения | +| `parameterListAllowed` | нет | Допустим ли список значений (`true`/`false`) | + +--- + +## 6. Вычисляемые поля (calculatedField) + +Поля, вычисляемые выражением на языке 1С (не из запроса): + +```xml + + УИД + БухгалтерскиеОтчеты.ПолучитьУИДСсылкиСтрокой(Номенклатура) + + <v8:item> + <v8:lang>ru</v8:lang> + <v8:content>Уникальный идентификатор</v8:content> + </v8:item> + + + true + true + true + + +``` + +| Элемент | Обязат. | Описание | +|---|---|---| +| `dataPath` | да | Путь к полю в результате | +| `expression` | да | Выражение на языке 1С (может вызывать методы общих модулей) | +| `title` | нет | Локализованный заголовок | +| `useRestriction` | нет | Ограничения использования (аналогично полям) | +| `valueType` | нет | Тип значения | +| `appearance` | нет | Оформление | + +--- + +## 7. Итоговые поля (totalField) + +Агрегатные функции для подведения итогов: + +```xml + + Количество + Сумма(Количество) + + + Цена + Максимум(Цена) + +``` + +| Элемент | Обязат. | Описание | +|---|---|---| +| `dataPath` | да | Путь к полю | +| `expression` | да | Агрегатная функция: `Сумма(...)`, `Количество(...)`, `Максимум(...)`, `Минимум(...)`, `Среднее(...)` | +| `group` | нет | Для какой группировки считать итоги | + +--- + +## 8. Параметры схемы (parameter) + +Параметры, доступные для задания пользователем или программно: + +```xml + + Период + + <v8:item> + <v8:lang>ru</v8:lang> + <v8:content>Период</v8:content> + </v8:item> + + + v8:StandardPeriod + + + LastMonth + + false + &Период.ДатаНачала + false + Always + +``` + +| Элемент | Обязат. | Описание | +|---|---|---| +| `name` | да | Имя параметра (используется в запросах как `&ИмяПараметра`) | +| `title` | нет | Локализованный заголовок | +| `valueType` | нет | Тип значения (раздел 4.7) | +| `value` | нет | Значение по умолчанию | +| `useRestriction` | нет | `true` — параметр скрыт от пользователя, `false` — доступен | +| `expression` | нет | Выражение для автоматического вычисления (например, `&Период.ДатаНачала`) | +| `availableAsField` | нет | `false` — параметр недоступен как поле в отчёте | +| `use` | нет | Режим: `Always` (всегда), `Auto` (автоматически) | + +### Типы значений параметров + +| Тип | XML-тип | Пример value | +|---|---|---| +| Дата | `xs:dateTime` | `0001-01-01T00:00:00` | +| Строка | `xs:string` | `Т13` | +| Стандартный период | `v8:StandardPeriod` | `LastMonth` | +| Ссылка | `d4p1:CatalogRef.ИмяСправочника` | `xsi:nil="true"` | +| null | — | `xsi:nil="true"` | + +Стандартные варианты периодов (`v8:StandardPeriodVariant`): `Custom`, `Today`, `ThisWeek`, `ThisMonth`, `ThisQuarter`, `ThisYear`, `LastMonth`, `LastQuarter`, `LastYear` и др. + +--- + +## 9. Макеты областей (template) + +Пользовательские шаблоны вывода (макеты ячеек): + +```xml + +``` + +Пространство имён `dcsat`: `http://v8.1c.ru/8.1/data-composition-system/area-template`. + +| Элемент | Описание | +|---|---| +| `name` | Имя макета (ссылаются groupTemplate) | +| `template` (вложенный) | Описание строк/ячеек (`dcsat:AreaTemplate`) | +| `parameter` | Параметры макета (`dcsat:ExpressionAreaTemplateParameter`) — выражения для подстановки | + +--- + +## 10. Привязки макетов группировок (groupTemplate) + +Связывают группировку с пользовательским макетом: + +```xml + + ТипЦен + Header + + +``` + +| Элемент | Описание | +|---|---| +| `groupField` | Имя поля группировки | +| `templateType` | Тип: `Header` (заголовок), `Footer` (подвал), `Overall` (общий) | +| `template` | Ссылка на имя template из раздела 9 | + +--- + +## 11. Варианты настроек (settingsVariant) + +Каждый вариант — именованная конфигурация отчёта. Отчёт может иметь несколько вариантов. + +```xml + + Основной + + + ru + Основной вариант отчёта + + + + + + +``` + +### 11.1. Структура settings + +``` +dcsset:settings +├── dcsset:selection — выбранные поля (раздел 11.2) +├── dcsset:filter — отборы (раздел 11.3) +├── dcsset:order — сортировка (раздел 11.4) +├── dcsset:conditionalAppearance — условное оформление (раздел 11.5) +├── dcsset:outputParameters — параметры вывода (раздел 11.6) +├── dcsset:dataParameters — значения параметров данных (раздел 11.7) +├── dcsset:item* — элементы структуры (раздел 11.8) +``` + +### 11.2. Выборка полей (selection) + +```xml + + + ТипОбъекта + + + ru + Наименование + + + + + +``` + +Типы элементов выборки: +- `dcsset:SelectedItemField` — конкретное поле (элемент `dcsset:field`) +- `dcsset:SelectedItemAuto` — автоматический подбор полей + +### 11.3. Отборы (filter) + +```xml + + + false + Организация + Equal + false + + + ru + Описание фильтра + + + Normal + GUID + + +``` + +Типы элементов фильтра: +- `dcsset:FilterItemComparison` — сравнение поля с значением +- `dcsset:FilterItemGroup` — группа условий (И/ИЛИ) + +Типы сравнения (`comparisonType`): + +| Значение | Описание | +|---|---| +| `Equal` | Равно | +| `NotEqual` | Не равно | +| `Greater` | Больше | +| `GreaterOrEqual` | Больше или равно | +| `Less` | Меньше | +| `LessOrEqual` | Меньше или равно | +| `InList` | В списке | +| `NotInList` | Не в списке | +| `InHierarchy` | В иерархии | +| `InListByHierarchy` | В списке по иерархии | +| `Contains` | Содержит | +| `NotContains` | Не содержит | +| `BeginsWith` | Начинается с | +| `NotBeginsWith` | Не начинается с | +| `Filled` | Заполнено | +| `NotFilled` | Не заполнено | + +Значение правой части (`right`) — может содержать списки: +```xml + + + -1 + +``` + +### 11.4. Сортировка (order) + +```xml + + + РазмерДанных + Desc + + + +``` + +Типы элементов сортировки: +- `dcsset:OrderItemField` — по полю (`dcsset:field` + `dcsset:orderType`: `Asc`/`Desc`) +- `dcsset:OrderItemAuto` — автоматическая сортировка + +### 11.5. Условное оформление (conditionalAppearance) + +```xml + + + + + ИмяПоля + + + + + ИмяПоля + Equal + 0 + + + + + Текст + + + + + +``` + +### 11.6. Параметры вывода (outputParameters) + +```xml + + + false + Заголовок + + + ru + Текст заголовка + + + + +``` + +Типичные параметры вывода: + +| Параметр | Тип значения | Описание | +|---|---|---| +| `Заголовок` | `v8:LocalStringType` | Заголовок отчёта | +| `МакетОформления` | `xs:string` | Имя макета оформления: `ОформлениеОтчетовЧерноБелый`, `Зеленый` и др. | +| `РасположениеПолейГруппировки` | `dcsset:DataCompositionGroupFieldsPlacement` | `Together`, `Separately`, `SeparatelyAndInGroups` | +| `РасположениеРеквизитов` | `dcsset:DataCompositionAttributesPlacement` | `Together`, `Separately`, `SeparatelyAndInGroups` | +| `ГоризонтальноеРасположениеОбщихИтогов` | `dcscor:DataCompositionTotalPlacement` | `None`, `Begin`, `End`, `Auto` | +| `ВертикальноеРасположениеОбщихИтогов` | `dcscor:DataCompositionTotalPlacement` | `None`, `Begin`, `End`, `Auto` | +| `ВыводитьЗаголовок` | `dcsset:DataCompositionTextOutputType` | `Auto`, `DontOutput`, `Output` | +| `ВыводитьПараметрыДанных` | `dcsset:DataCompositionTextOutputType` | То же | +| `ВыводитьОтбор` | `dcsset:DataCompositionTextOutputType` | То же | + +### 11.7. Параметры данных (dataParameters) + +Значения параметров схемы в конкретном варианте: + +```xml + + + false + Период + + LastMonth + + Normal + GUID + + +``` + +| Элемент | Описание | +|---|---| +| `dcscor:use` | `true`/`false` — использовать значение или нет | +| `dcscor:parameter` | Имя параметра из раздела 8 | +| `dcscor:value` | Значение параметра | +| `dcsset:viewMode` | Режим отображения: `Normal`, `QuickAccess`, `Inaccessible` | +| `dcsset:userSettingID` | GUID пользовательской настройки | + +### 11.8. Элементы структуры (structure items) + +Структура отчёта — иерархия группировок, таблиц, диаграмм. + +#### StructureItemGroup — группировка + +```xml + + Группировка + + + Организация + Items + None + 0001-01-01T00:00:00 + 0001-01-01T00:00:00 + + + + + + + + + ... + + ... + + +``` + +Типы группировки (`groupType`): `Items`, `Hierarchy`, `HierarchyOnly`. + +Типы дополнения периодом (`periodAdditionType`): `None`, `Year`, `HalfYear`, `Quarter`, `Month`, `TenDays`, `Week`, `Day`. + +Пустая группировка (без `groupItems`) = детальные записи. + +#### StructureItemTable — таблица (кросс-таблица) + +```xml + + Таблица + + ... + ... + ... + + + Группировка + ... + ... + ... + + +``` + +#### StructureItemChart — диаграмма + +```xml + + + ... + ... + ... + + + ... + ... + + + + РазмерДанных + + + ... + +``` + +--- + +## 12. Типы данных — сводка + +### v8:LocalStringType — локализованная строка + +```xml + + <v8:item> + <v8:lang>ru</v8:lang> + <v8:content>Текст на русском</v8:content> + </v8:item> + +``` + +Также можно задать как простую строку: `xsi:type="xs:string"`. + +### dcscor:SettingsParameterValue — параметр настройки + +```xml + + true + ИмяПараметра + Значение + +``` + +### dcscor:Field — ссылка на поле + +```xml +ИмяПоля +``` + +--- + +## 13. Полный минимальный пример + +Простая DCS: один запрос, два поля, один итог, один вариант: + +```xml + + + + ИсточникДанных1 + Local + + + НаборДанных1 + + Наименование + Наименование + + <v8:item> + <v8:lang>ru</v8:lang> + <v8:content>Наименование</v8:content> + </v8:item> + + + + Количество + Количество + + ИсточникДанных1 + ВЫБРАТЬ + Номенклатура.Наименование КАК Наименование, + КОЛИЧЕСТВО(1) КАК Количество +ИЗ + Справочник.Номенклатура КАК Номенклатура +СГРУППИРОВАТЬ ПО + Номенклатура.Наименование + + + Количество + Сумма(Количество) + + + Основной + + + ru + Основной + + + + + + Наименование + + + Количество + + + + + + + + + + + + + +``` diff --git a/docs/role-dsl-spec.md b/docs/role-dsl-spec.md new file mode 100644 index 00000000..300fa000 --- /dev/null +++ b/docs/role-dsl-spec.md @@ -0,0 +1,112 @@ +# Спецификация Role JSON DSL + +Формат JSON для описания ролей 1С, используемый навыком `/role-compile`. + +## Обзор + +Role JSON DSL — компактный JSON-формат, транслируемый скриптом в XML-файлы роли 1С:Предприятия (метаданные + Rights.xml). Поддерживает пресеты прав, русские синонимы типов и прав, шаблоны ограничений (RLS). + +## Корневой объект + +| Поле | Тип | Обяз. | По умолчанию | Описание | +|------|-----|:-----:|:------------:|----------| +| `name` | string | да | — | Программное имя роли | +| `synonym` | string | нет | = name | Отображаемое имя | +| `comment` | string | нет | `""` | Комментарий | +| `setForNewObjects` | bool | нет | `false` | Устанавливать для новых объектов | +| `setForAttributesByDefault` | bool | нет | `true` | Устанавливать для реквизитов по умолчанию | +| `independentRightsOfChildObjects` | bool | нет | `false` | Независимые права подчинённых объектов | +| `objects` | array | нет | `[]` | Массив объектов метаданных с правами | +| `templates` | array | нет | `[]` | Шаблоны ограничений (RLS) | + +## Элементы `objects` + +Массив принимает два формата, которые можно смешивать. + +### Строковый shorthand + +``` +"Тип.Имя: @пресет" +"Тип.Имя: Право1, Право2" +``` + +Тип — английский (Catalog) или русский (Справочник). Права — английские (Read) или русские (Чтение). + +### Объектная форма + +| Поле | Тип | Обяз. | Описание | +|------|-----|:-----:|----------| +| `name` | string | да | Полное имя объекта: `Тип.Имя` | +| `preset` | string | нет | Пресет: `"view"`, `"edit"` (без `@`) | +| `rights` | object\|array | нет | Переопределения: `{"Right": bool}` или `["Right1", "Right2"]` | +| `rls` | object | нет | RLS: `{"ИмяПрава": "текст условия или #шаблон"}` | + +При наличии `preset` + `rights` — сначала применяется пресет, затем `rights` переопределяют отдельные права. + +## Пресеты + +Три встроенных пресета определяют набор прав в зависимости от типа объекта: + +| Пресет | Назначение | +|--------|------------| +| `view` | Просмотр: Read, View (+InputByString для справочников/документов; Use+View для обработок/отчётов) | +| `edit` | Полное редактирование: CRUD + Interactive* + Posting (документы) | + +Подробные таблицы прав для каждого типа объекта — в `.claude/skills/role-compile/dsl-reference.md`. + +## Шаблоны ограничений (RLS) + +Элементы массива `templates`: + +| Поле | Тип | Описание | +|------|-----|----------| +| `name` | string | Имя шаблона с параметрами: `"ДляОбъекта(Модификатор)"` | +| `condition` | string | Текст условия. `&` экранируется в `&` автоматически | + +Ссылка на шаблон в `rls`: `"#ИмяШаблона(\"параметры\")"` — начинается с `#`. + +## Русские синонимы + +DSL принимает русские имена типов и прав, транслируя их в английские. Полные таблицы соответствий — в `.claude/skills/role-compile/dsl-reference.md`. + +Примеры: `Справочник` → Catalog, `Документ` → Document, `Чтение` → Read, `Просмотр` → View. + +## Пример + +```json +{ + "name": "МенеджерПродаж", + "synonym": "Менеджер продаж", + "objects": [ + "Document.РеализацияТоваровУслуг: @edit", + "Catalog.Контрагенты: @view", + "Catalog.Номенклатура: @view", + { + "name": "Document.ЗаказКлиента", + "preset": "view", + "rls": { "Read": "#ДляОбъекта(\"\")" } + } + ], + "templates": [ + { + "name": "ДляОбъекта(Модификатор)", + "condition": "ГДЕ Организация = &ТекущаяОрганизация" + } + ] +} +``` + +## Генерируемые файлы + +``` +Roles/ + ИмяРоли.xml # Метаданные (UUID, синоним, флаги) + ИмяРоли/ + └── Ext/ + └── Rights.xml # Права доступа (объекты, права, RLS) +``` + +## См. также + +- [Роли (Rights.xml)](1c-role-spec.md) — XML-формат прав роли, типы объектов, RLS +- [Гайд по ролям](role-guide.md) — сценарии использования, рабочий цикл diff --git a/docs/role-guide.md b/docs/role-guide.md index ff40e964..44dd08b7 100644 --- a/docs/role-guide.md +++ b/docs/role-guide.md @@ -1,13 +1,72 @@ # Роли (Role) -Навыки группы `/role-*` позволяют анализировать и создавать роли 1С — XML-файлы прав доступа (Rights.xml) и метаданных. +Навыки группы `/role-*` позволяют анализировать, создавать и проверять роли 1С — XML-файлы прав доступа (Rights.xml) и метаданных. ## Навыки | Навык | Параметры | Описание | |-------|-----------|----------| | `/role-info` | `` | Компактная сводка прав: объекты по типам, только разрешённые, RLS, шаблоны | -| `/role-compile` | ` ` | Создание роли: метаданные + Rights.xml по описанию прав | +| `/role-compile` | ` ` | Генерация роли из JSON DSL: метаданные + Rights.xml, UUID автоматически | +| `/role-validate` | ` [MetadataPath]` | Валидация структурной корректности: XML, namespace, права, RLS, шаблоны | + +## Рабочий цикл + +``` +Описание прав (текст) → JSON DSL → /role-compile → XML-исходники → /role-validate + → /role-info +``` + +1. Claude формирует JSON-определение роли (с пресетами или явными правами) +2. `/role-compile` генерирует `Roles/ИмяРоли.xml` + `Roles/ИмяРоли/Ext/Rights.xml` +3. `/role-validate` проверяет корректность сгенерированного XML +4. `/role-info` выводит компактную сводку для визуальной проверки + +## JSON DSL — компактный формат + +Роли описываются в JSON с двумя уровнями детализации: + +### Строковый shorthand (простые роли) + +```json +{ + "name": "ЧтениеНоменклатуры", + "synonym": "Чтение номенклатуры", + "objects": [ + "Catalog.Номенклатура: @view", + "Catalog.Контрагенты: @view", + "DataProcessor.Загрузка: @view" + ] +} +``` + +Формат строки: `Тип.Имя: @пресет` или `Тип.Имя: Право1, Право2`. + +### Объектная форма (RLS, переопределения) + +```json +{ + "name": "Document.Реализация", + "preset": "view", + "rights": { "Delete": false }, + "rls": { "Read": "#ДляОбъекта(\"\")" } +} +``` + +Форматы можно смешивать в одном массиве `objects`. + +### Пресеты + +| Пресет | Действие | +|--------|----------| +| `@view` | Просмотр: Read, View (+InputByString для справочников/документов; Use+View для обработок/отчётов) | +| `@edit` | Полное редактирование: CRUD + Interactive* + Posting (для документов) | + +`@` обязателен в строковом shorthand. В объектной форме — ключ `preset` без `@`. + +### Русские синонимы + +Скрипт принимает русские имена типов и прав: `Справочник` → Catalog, `Чтение` → Read, `Проведение` → Posting и т.д. Полный список — в [спецификации Role DSL](role-dsl-spec.md). ## Сценарии использования @@ -22,15 +81,6 @@ Claude вызовет `/role-info`, получит компактную свод - где есть ограничения RLS - какие шаблоны ограничений используются -### Создание роли для регламентного задания - -``` -> Проанализируй модуль регламентного задания ОбновлениеКурсовВалют -> и создай роль с минимальными правами для его выполнения -``` - -Claude проанализирует код, определит используемые объекты метаданных, и вызовет `/role-compile` для создания роли с нужными правами (Read, Update, Posting и т.д.). - ### Создание роли по описанию ``` @@ -41,11 +91,46 @@ Claude проанализирует код, определит использу > - Регистр ЦеныНоменклатуры: чтение ``` -Рабочий цикл: -1. Claude генерирует `Roles/МенеджерПродаж.xml` (метаданные с UUID) -2. Claude генерирует `Roles/МенеджерПродаж/Ext/Rights.xml` (права) -3. Регистрирует роль в `Configuration.xml` (``) -4. Проверяет результат через `/role-info` +Claude сформирует JSON с пресетами: +```json +{ + "name": "МенеджерПродаж", + "synonym": "Менеджер продаж", + "objects": [ + "Document.РеализацияТоваровУслуг: @edit", + "Catalog.Контрагенты: @view", + "Catalog.Номенклатура: @view", + "InformationRegister.ЦеныНоменклатуры: @view" + ] +} +``` + +И вызовет `/role-compile` → `/role-validate` → `/role-info`. + +### Создание роли для регламентного задания + +``` +> Проанализируй модуль регламентного задания ОбновлениеКурсовВалют +> и создай роль с минимальными правами для его выполнения +``` + +Claude проанализирует код, определит используемые объекты, создаст JSON с точечными правами (без пресетов — только нужные права), и скомпилирует роль. + +### Создание роли с RLS + +``` +> Создай роль для чтения документов с ограничением по организации +``` + +Claude использует объектную форму JSON с шаблонами ограничений. + +### Проверка существующей роли + +``` +> Проверь корректность роли Roles/МояРоль/Ext/Rights.xml +``` + +Claude вызовет `/role-validate` и покажет результат: ошибки (невалидный XML, отсутствующие элементы) и предупреждения (неизвестные типы объектов, подозрительные имена прав с подсказками). ## Структура файлов роли @@ -66,4 +151,5 @@ Roles/ ## Спецификация -Полная спецификация формата: [1c-role-spec.md](1c-role-spec.md) — типы объектов, права, RLS, шаблоны ограничений, версии формата. +- [1c-role-spec.md](1c-role-spec.md) — XML-формат прав роли, типы объектов, RLS, шаблоны ограничений +- [role-dsl-spec.md](role-dsl-spec.md) — JSON DSL для описания ролей (формат входных данных `/role-compile`)