diff --git a/.claude/skills/role-compile/SKILL.md b/.claude/skills/role-compile/SKILL.md new file mode 100644 index 00000000..41a069db --- /dev/null +++ b/.claude/skills/role-compile/SKILL.md @@ -0,0 +1,209 @@ +--- +name: role-compile +description: Создание роли 1С — метаданные и Rights.xml из описания прав +argument-hint: +allowed-tools: + - Bash + - Read + - Write + - Glob +--- + +# /role-compile — создание роли 1С + +Создаёт файлы роли (метаданные + Rights.xml) по описанию прав. Скрипта нет — агент генерирует XML по шаблонам ниже. + +## Использование + +``` +/role-compile +``` + +- **RoleName** — программное имя роли (например, `ВыполнениеРегламентныхЗаданий`) +- **RolesDir** — каталог `Roles/` в исходниках конфигурации или обработки + +## Что создать + +### 1. Файл метаданных: `Roles/.xml` + +```xml + + + + + ИмяРоли + + + ru + Отображаемое имя роли + + + + + + +``` + +**UUID:** Сгенерируй через PowerShell: `[guid]::NewGuid().ToString()` + +**Namespace:** Минимальный набор — достаточно `xmlns`, `v8`, `xr`, `xs`, `xsi`. Полный набор из спецификации тоже корректен. + +### 2. Файл прав: `Roles//Ext/Rights.xml` + +```xml + + + false + true + false + + +``` + +### 3. Регистрация в Configuration.xml + +Добавь `ИмяРоли` в секцию `` файла `Configuration.xml`. + +## Формат блока прав + +Каждый объект — отдельный блок ``: + +```xml + + ТипОбъекта.ИмяОбъекта + + ИмяПрава + true + + +``` + +Несколько прав — несколько `` внутри одного ``. + +## Права по типам объектов (краткая справка) + +### Ссылочные объекты данных + +| Тип | Типичные права | +|-----|---------------| +| `Catalog` | Read, Insert, Update, Delete, View, Edit, InputByString, InteractiveInsert, InteractiveSetDeletionMark, InteractiveClearDeletionMark, InteractiveDelete | +| `Document` | (все Catalog) + Posting, UndoPosting, InteractivePosting, InteractivePostingRegular, InteractiveUndoPosting, InteractiveChangeOfPosted | +| `ChartOfAccounts` | (как Catalog) + предопределённые: InteractiveDeletePredefinedData и др. | +| `ChartOfCharacteristicTypes` | (как ChartOfAccounts) | +| `ChartOfCalculationTypes` | (как ChartOfAccounts) | +| `ExchangePlan` | (как Catalog) | +| `BusinessProcess` | (как Catalog) + Start, InteractiveStart, InteractiveActivate | +| `Task` | (как Catalog) + Execute, InteractiveExecute, InteractiveActivate | + +### Регистры + +| Тип | Права | +|-----|-------| +| `InformationRegister` | Read, Update, View, Edit, TotalsControl | +| `AccumulationRegister` | Read, Update, View, Edit, TotalsControl | +| `AccountingRegister` | Read, Update, View, Edit, TotalsControl | +| `CalculationRegister` | Read, View | + +### Простые типы + +| Тип | Права | +|-----|-------| +| `DataProcessor` | Use, View | +| `Report` | Use, View | +| `Constant` | Read, Update, View, Edit | +| `CommonForm` | View | +| `CommonCommand` | View | +| `Subsystem` | View | +| `DocumentJournal` | Read, View | +| `Sequence` | Read, Update | +| `SessionParameter` | Get, Set | +| `CommonAttribute` | View, Edit | +| `WebService` / `HTTPService` / `IntegrationService` | Use | + +### Вложенные объекты + +| Вложенный тип | Права | Пример | +|--------------|-------|--------| +| `*.StandardAttribute.*` | View, Edit | `Document.Реализация.StandardAttribute.Posted` | +| `*.Attribute.*` | View, Edit | `Catalog.Контрагенты.Attribute.ИНН` | +| `*.TabularSection.*` | View, Edit | `Document.Реализация.TabularSection.Товары` | +| `*Register.*.Dimension.*` | View, Edit | `InformationRegister.Цены.Dimension.Номенклатура` | +| `*Register.*.Resource.*` | View, Edit | `InformationRegister.Цены.Resource.Цена` | +| `*.Command.*` | View | `Catalog.Контрагенты.Command.Открыть` | + +### Configuration + +Права на конфигурацию в целом: `Configuration.ИмяКонфигурации` + +Ключевые: Administration, DataAdministration, ThinClient, WebClient, ThickClient, ExternalConnection, Output, SaveUserData, InteractiveOpenExtDataProcessors, InteractiveOpenExtReports + +## Типичные наборы прав + +### Чтение справочника + +```xml + + Catalog.Номенклатура + Readtrue + Viewtrue + InputByStringtrue + +``` + +### Полные права на документ + +```xml + + Document.РеализацияТоваровУслуг + Readtrue + Inserttrue + Updatetrue + Deletetrue + Postingtrue + UndoPostingtrue + Viewtrue + InteractiveInserttrue + Edittrue + InteractiveSetDeletionMarktrue + InteractiveClearDeletionMarktrue + InteractivePostingtrue + InteractivePostingRegulartrue + InteractiveUndoPostingtrue + InteractiveChangeOfPostedtrue + InputByStringtrue + +``` + +### Использование обработки + +```xml + + DataProcessor.ОбновлениеЦен + Usetrue + Viewtrue + +``` + +### Чтение/запись регистра + +```xml + + InformationRegister.ЦеныНоменклатуры + Readtrue + Updatetrue + Viewtrue + Edittrue + +``` + +## Полная спецификация + +См. [1c-role-spec.md](../../docs/1c-role-spec.md) — полный каталог прав, RLS, шаблоны ограничений, версии формата. diff --git a/.claude/skills/role-info/SKILL.md b/.claude/skills/role-info/SKILL.md new file mode 100644 index 00000000..9e3acfcd --- /dev/null +++ b/.claude/skills/role-info/SKILL.md @@ -0,0 +1,74 @@ +--- +name: role-info +description: Компактная сводка прав роли 1С из Rights.xml — объекты, права, RLS, шаблоны ограничений +argument-hint: +allowed-tools: + - Bash + - Read +--- + +# /role-info — анализ роли 1С + +Парсит `Rights.xml` роли и выдаёт компактную сводку: объекты сгруппированы по типу, показаны только разрешённые права. Сжатие: тысячи строк XML → 50–150 строк текста. + +## Использование + +``` +/role-info +``` + +**RightsPath** — путь к файлу `Rights.xml` роли (обычно `Roles/ИмяРоли/Ext/Rights.xml`). + +## Запуск скрипта + +```powershell +powershell.exe -File .claude\skills\role-info\scripts\role-info.ps1 -RightsPath -OutFile +``` + +### Параметры + +| Параметр | Обязательный | Описание | +|----------|:------------:|----------| +| `-RightsPath` | да | Путь к Rights.xml | +| `-ShowDenied` | нет | Показать запрещённые права (по умолчанию скрыты) | +| `-MaxPerGroup` | нет | Макс. объектов на группу (по умолчанию 20). `0` = без ограничений | +| `-OutFile` | нет | Записать результат в файл (UTF-8 BOM). Без этого — вывод в консоль | + +**Важно:** Всегда используй `-OutFile` и читай результат через Read tool. Прямой вывод в консоль через bash ломает кириллицу. + +## Формат вывода + +``` +=== Role: БазовыеПраваБП --- "Базовые права: Бухгалтерия предприятия" === + +Properties: setForNewObjects=false, setForAttributesByDefault=true, independentRightsOfChildObjects=false + +Allowed rights: + + Catalog (8): + Контрагенты: Read, View, InputByString + Банки: Read, View, InputByString + ... + + Document (12): + РеализацияТоваровУслуг: Read, View, Posting, InteractivePosting + ... + + InformationRegister (6): + ЦеныНоменклатуры: Read [RLS], Update + ... + +Denied: 18 rights (use -ShowDenied to list) + +RLS: 4 restrictions +Templates: ДляРегистра, ПоЗначениям + +--- +Total: 138 allowed, 18 denied +``` + +### Обозначения + +- `[RLS]` — право с ограничением на уровне записей (restrictionByCondition) +- `-View`, `-Edit` — запрещённые права (в секции Denied, при `-ShowDenied`) +- Вложенные объекты показываются с суффиксом: `Контрагенты.StandardAttribute.PredefinedDataName` diff --git a/.claude/skills/role-info/scripts/role-info.ps1 b/.claude/skills/role-info/scripts/role-info.ps1 new file mode 100644 index 00000000..86490178 --- /dev/null +++ b/.claude/skills/role-info/scripts/role-info.ps1 @@ -0,0 +1,231 @@ +param( + [Parameter(Mandatory=$true)][string]$RightsPath, + [switch]$ShowDenied, + [int]$MaxPerGroup = 20, + [string]$OutFile +) + +$ErrorActionPreference = 'Stop' + +# --- Output helper --- +$script:lines = @() +function Out([string]$text) { + if ($OutFile) { $script:lines += $text } + else { Write-Host $text } +} + +# --- Resolve paths --- +if (-not [System.IO.Path]::IsPathRooted($RightsPath)) { + $RightsPath = Join-Path (Get-Location).Path $RightsPath +} + +if (-not (Test-Path $RightsPath)) { + Out "[ERROR] File not found: $RightsPath" + exit 1 +} + +# --- Try to find metadata file for role name/synonym --- +$roleName = "" +$roleSynonym = "" +$extDir = Split-Path $RightsPath # .../Ext +$roleDir = Split-Path $extDir # .../RoleName +$rolesDir = Split-Path $roleDir # .../Roles +$roleFolderName = Split-Path $roleDir -Leaf +$metaPath = Join-Path $rolesDir "$roleFolderName.xml" + +if (Test-Path $metaPath) { + try { + [xml]$metaXml = Get-Content -Path $metaPath -Encoding UTF8 + $ns = New-Object System.Xml.XmlNamespaceManager($metaXml.NameTable) + $ns.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses") + $ns.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core") + $nameNode = $metaXml.SelectSingleNode("//md:Role/md:Properties/md:Name", $ns) + if ($nameNode) { $roleName = $nameNode.InnerText } + $synNode = $metaXml.SelectSingleNode("//md:Role/md:Properties/md:Synonym/v8:item[v8:lang='ru']/v8:content", $ns) + if ($synNode) { $roleSynonym = $synNode.InnerText } + } catch { + # Ignore metadata parsing errors + } +} + +if (-not $roleName) { $roleName = $roleFolderName } + +# --- Parse Rights.xml --- +[xml]$xml = Get-Content -Path $RightsPath -Encoding UTF8 +$root = $xml.DocumentElement +$rightsNs = "http://v8.1c.ru/8.2/roles" + +# Global flags +$setForNew = $root.setForNewObjects +$setForAttrs = $root.setForAttributesByDefault +$independentChild = $root.independentRightsOfChildObjects + +# --- Collect objects --- +# Structure: grouped by type prefix, then by object short name +$allowed = [ordered]@{} # type -> [ordered]@{ shortName -> [list of rights] } +$denied = [ordered]@{} # type -> [ordered]@{ shortName -> [list of rights] } +$rlsObjects = @() +$totalAllowed = 0 +$totalDenied = 0 + +$objects = $root.GetElementsByTagName("object", $rightsNs) +foreach ($obj in $objects) { + $objName = "" + $rights = @() + + foreach ($child in $obj.ChildNodes) { + if ($child.LocalName -eq "name" -and $child.NamespaceURI -eq $rightsNs) { + $objName = $child.InnerText + } + if ($child.LocalName -eq "right" -and $child.NamespaceURI -eq $rightsNs) { + $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 } + } + if ($rName -and $rValue) { + $rights += @{ name = $rName; value = $rValue; rls = $hasRLS } + } + } + } + + if (-not $objName -or $rights.Count -eq 0) { continue } + + # Split into type prefix and short name + $dotIdx = $objName.IndexOf(".") + if ($dotIdx -lt 0) { continue } + $typePrefix = $objName.Substring(0, $dotIdx) + $shortName = $objName.Substring($dotIdx + 1) + + foreach ($r in $rights) { + if ($r.value -eq "true") { + $totalAllowed++ + if (-not $allowed.Contains($typePrefix)) { + $allowed[$typePrefix] = [ordered]@{} + } + if (-not $allowed[$typePrefix].Contains($shortName)) { + $allowed[$typePrefix][$shortName] = @() + } + $suffix = $r.name + if ($r.rls) { + $suffix += " [RLS]" + $rlsObjects += "$typePrefix.$shortName ($($r.name))" + } + $allowed[$typePrefix][$shortName] += $suffix + } + else { + $totalDenied++ + if (-not $denied.Contains($typePrefix)) { + $denied[$typePrefix] = [ordered]@{} + } + if (-not $denied[$typePrefix].Contains($shortName)) { + $denied[$typePrefix][$shortName] = @() + } + $denied[$typePrefix][$shortName] += $r.name + } + } +} + +# --- Restriction templates --- +$templates = @() +$tplNodes = $root.GetElementsByTagName("restrictionTemplate", $rightsNs) +foreach ($tpl in $tplNodes) { + foreach ($child in $tpl.ChildNodes) { + if ($child.LocalName -eq "name") { + $tName = $child.InnerText + # Extract just the name part before parentheses + $parenIdx = $tName.IndexOf("(") + if ($parenIdx -gt 0) { $tName = $tName.Substring(0, $parenIdx) } + $templates += $tName + } + } +} + +# --- Output --- +$header = "=== Role: $roleName" +if ($roleSynonym) { $header += " --- `"$roleSynonym`"" } +$header += " ===" +Out $header +Out "" + +Out "Properties: setForNewObjects=$setForNew, setForAttributesByDefault=$setForAttrs, independentRightsOfChildObjects=$independentChild" +Out "" + +# Helper: output group with truncation +function OutGroup($objMap, [string]$prefix, [switch]$isDenied) { + $keys = @($objMap.Keys) + $shown = 0 + foreach ($shortName in $keys) { + if ($MaxPerGroup -gt 0 -and $shown -ge $MaxPerGroup) { + $remaining = $keys.Count - $shown + Out " ... ещё $remaining (используй -MaxPerGroup 0 для полного списка)" + break + } + if ($isDenied) { + $rightsList = ($objMap[$shortName] | ForEach-Object { "-$_" }) -join ", " + } else { + $rightsList = $objMap[$shortName] -join ", " + } + Out " ${shortName}: $rightsList" + $shown++ + } +} + +# Allowed rights grouped by type +if ($allowed.Count -gt 0) { + Out "Allowed rights:" + Out "" + foreach ($typePrefix in $allowed.Keys) { + $objMap = $allowed[$typePrefix] + Out " $typePrefix ($($objMap.Count)):" + OutGroup $objMap $typePrefix + Out "" + } +} +else { + Out "(no allowed rights)" + Out "" +} + +# Denied rights +if ($ShowDenied -and $denied.Count -gt 0) { + Out "Denied rights:" + Out "" + foreach ($typePrefix in $denied.Keys) { + $objMap = $denied[$typePrefix] + Out " $typePrefix ($($objMap.Count)):" + OutGroup $objMap $typePrefix -isDenied + Out "" + } +} +elseif ($totalDenied -gt 0) { + Out "Denied: $totalDenied rights (use -ShowDenied to list)" + Out "" +} + +# RLS summary +if ($rlsObjects.Count -gt 0) { + Out "RLS: $($rlsObjects.Count) restrictions" +} + +# Templates +if ($templates.Count -gt 0) { + Out "Templates: $($templates -join ', ')" +} + +Out "" +Out "---" +Out "Total: $totalAllowed allowed, $totalDenied denied" + +# --- Write to file if requested --- +if ($OutFile) { + if (-not [System.IO.Path]::IsPathRooted($OutFile)) { + $OutFile = Join-Path (Get-Location).Path $OutFile + } + $utf8 = New-Object System.Text.UTF8Encoding($true) + [System.IO.File]::WriteAllLines($OutFile, $script:lines, $utf8) + Write-Host "Output written to $OutFile" +}