diff --git a/.claude/skills/form-validate/scripts/form-validate.ps1 b/.claude/skills/form-validate/scripts/form-validate.ps1 index 2410e686..1e9ecca6 100644 --- a/.claude/skills/form-validate/scripts/form-validate.ps1 +++ b/.claude/skills/form-validate/scripts/form-validate.ps1 @@ -1,4 +1,4 @@ -# form-validate v1.16 — Validate 1C managed form +# form-validate v1.17 — Validate 1C managed form # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -843,7 +843,10 @@ if (-not $stopped -and $isExtension) { # Check 5 такое пропускает: у заимствованной формы он не проверяет базовые элементы (id < 1000000), # а привязки в вообще вне его списка тегов. Между тем это ровно тот случай, на котором # платформа отвергает загрузку: «Неверный путь к полю - Объект.X». Правило: если основной реквизит - # не объявлен в формы, любой путь с корнем «Объект» не разрешится. + # не объявлен в формы, любой путь с его корнем не разрешится. + # Корень берётся из основного реквизита BaseForm: «Объект» он только у формы объекта, у формы + # списка это «Список», у формы записи регистра «Запись». С зашитым «Объект» проверка на таких + # формах молча не срабатывала — валидатор рапортовал «чисто» на форме, которую платформа не примет. $mainAttrDeclared = $false foreach ($attr in $attrNodes) { $maNode = $attr.SelectSingleNode("f:MainAttribute", $nsMgr) @@ -855,15 +858,17 @@ if (-not $stopped -and $isExtension) { # , а те живут в чужом пространстве имён. $rawForm = [System.IO.File]::ReadAllText($FormPath, [System.Text.Encoding]::UTF8) $mainBase = $baseFormNode.SelectSingleNode("f:Attributes/f:Attribute[f:MainAttribute='true']", $bfNs) + $rootName = if ($mainBase -and $mainBase.GetAttribute("name")) { $mainBase.GetAttribute("name") } else { "Объект" } + $rootPat = [regex]::Escape($rootName) $danglingPaths = @{} - foreach ($m in [regex]::Matches($rawForm, '<(?:\w+:)?\w*DataPath[^>]*>(Объект\.[^<]+)')) { + foreach ($m in [regex]::Matches($rawForm, "<(?:\w+:)?\w*DataPath[^>]*>(${rootPat}\.[^<]+)")) { $danglingPaths[$m.Groups[1].Value] = $true } if ($danglingPaths.Count -gt 0) { $shown = @($danglingPaths.Keys | Sort-Object) $sample = ($shown | Select-Object -First 3) -join ", " $suffix = if ($shown.Count -gt 3) { " (и ещё $($shown.Count - 3))" } else { "" } - Report-Error "Path(s) rooted at 'Объект' but the form declares no MainAttribute: $sample$suffix" + Report-Error "Path(s) rooted at '${rootName}' but the form declares no MainAttribute: $sample$suffix" } elseif ($mainBase) { Report-OK "Object paths: none dangling (MainAttribute not declared)" } diff --git a/.claude/skills/form-validate/scripts/form-validate.py b/.claude/skills/form-validate/scripts/form-validate.py index 4742d265..51ef3fb0 100644 --- a/.claude/skills/form-validate/scripts/form-validate.py +++ b/.claude/skills/form-validate/scripts/form-validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# form-validate v1.16 — Validate 1C managed form +# form-validate v1.17 — Validate 1C managed form # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -825,7 +825,10 @@ def main(): # Check 5 \u0442\u0430\u043a\u043e\u0435 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u0442: \u0443 \u0437\u0430\u0438\u043c\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0444\u043e\u0440\u043c\u044b \u043e\u043d \u043d\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b (id < 1000000), # \u0430 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u0432 \u0432\u043e\u043e\u0431\u0449\u0435 \u0432\u043d\u0435 \u0435\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u0442\u0435\u0433\u043e\u0432. \u041c\u0435\u0436\u0434\u0443 \u0442\u0435\u043c \u044d\u0442\u043e \u0440\u043e\u0432\u043d\u043e \u0442\u043e\u0442 \u0441\u043b\u0443\u0447\u0430\u0439, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c # \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u043e\u0442\u0432\u0435\u0440\u0433\u0430\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443: \u00ab\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u043a \u043f\u043e\u043b\u044e - \u041e\u0431\u044a\u0435\u043a\u0442.X\u00bb. \u041f\u0440\u0430\u0432\u0438\u043b\u043e: \u0435\u0441\u043b\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0440\u0435\u043a\u0432\u0438\u0437\u0438\u0442 - # \u043d\u0435 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d \u0432 \u0444\u043e\u0440\u043c\u044b, \u043b\u044e\u0431\u043e\u0439 \u043f\u0443\u0442\u044c \u0441 \u043a\u043e\u0440\u043d\u0435\u043c \u00ab\u041e\u0431\u044a\u0435\u043a\u0442\u00bb \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u0441\u044f. + # \u043d\u0435 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d \u0432 \u0444\u043e\u0440\u043c\u044b, \u043b\u044e\u0431\u043e\u0439 \u043f\u0443\u0442\u044c \u0441 \u0435\u0433\u043e \u043a\u043e\u0440\u043d\u0435\u043c \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u0441\u044f. + # \u041a\u043e\u0440\u0435\u043d\u044c \u0431\u0435\u0440\u0451\u0442\u0441\u044f \u0438\u0437 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0440\u0435\u043a\u0432\u0438\u0437\u0438\u0442\u0430 BaseForm: \u00ab\u041e\u0431\u044a\u0435\u043a\u0442\u00bb \u043e\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0443 \u0444\u043e\u0440\u043c\u044b \u043e\u0431\u044a\u0435\u043a\u0442\u0430, \u0443 \u0444\u043e\u0440\u043c\u044b + # \u0441\u043f\u0438\u0441\u043a\u0430 \u044d\u0442\u043e \u00ab\u0421\u043f\u0438\u0441\u043e\u043a\u00bb, \u0443 \u0444\u043e\u0440\u043c\u044b \u0437\u0430\u043f\u0438\u0441\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430 \u00ab\u0417\u0430\u043f\u0438\u0441\u044c\u00bb. \u0421 \u0437\u0430\u0448\u0438\u0442\u044b\u043c \u00ab\u041e\u0431\u044a\u0435\u043a\u0442\u00bb \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0442\u0430\u043a\u0438\u0445 + # \u0444\u043e\u0440\u043c\u0430\u0445 \u043c\u043e\u043b\u0447\u0430 \u043d\u0435 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043b\u0430. main_attr_declared = False for attr in attr_nodes: ma_node = attr.find(f"{{{F_NS}}}MainAttribute") @@ -839,12 +842,15 @@ def main(): with open(form_path, "r", encoding="utf-8-sig") as fh: raw_form = fh.read() main_base = base_form_node.find(f"{{{F_NS}}}Attributes/{{{F_NS}}}Attribute[{{{F_NS}}}MainAttribute='true']") - dangling_paths = set(re.findall(r'<(?:\w+:)?\w*DataPath[^>]*>(\u041e\u0431\u044a\u0435\u043a\u0442\.[^<]+)', raw_form)) + root_name = main_base.get("name") if main_base is not None and main_base.get("name") else "\u041e\u0431\u044a\u0435\u043a\u0442" + root_pat = re.escape(root_name) + dangling_paths = set(re.findall( + r'<(?:\w+:)?\w*DataPath[^>]*>(' + root_pat + r'\.[^<]+)', raw_form)) if dangling_paths: shown = sorted(dangling_paths) sample = ", ".join(shown[:3]) suffix = f" (\u0438 \u0435\u0449\u0451 {len(shown) - 3})" if len(shown) > 3 else "" - report_error(f"Path(s) rooted at '\u041e\u0431\u044a\u0435\u043a\u0442' but the form declares no MainAttribute: {sample}{suffix}") + report_error(f"Path(s) rooted at '{root_name}' but the form declares no MainAttribute: {sample}{suffix}") elif main_base is not None: report_ok("Object paths: none dangling (MainAttribute not declared)") diff --git a/tests/skills/cases/form-validate/borrowed-list-path-no-main.json b/tests/skills/cases/form-validate/borrowed-list-path-no-main.json new file mode 100644 index 00000000..4baa5e18 --- /dev/null +++ b/tests/skills/cases/form-validate/borrowed-list-path-no-main.json @@ -0,0 +1,7 @@ +{ + "name": "Ошибка: путь Список.* в заимствованной форме списка без объявленного основного реквизита", + "setup": "fixture:borrowed-list-path-no-main", + "params": { "formPath": "Documents/Заказ/Forms/ФормаСписка" }, + "expectError": true, + "expect": { "stdoutContains": ["Список.Партнер", "rooted at 'Список'"] } +} diff --git a/tests/skills/cases/form-validate/fixtures/borrowed-list-path-no-main/Documents/Заказ/Forms/ФормаСписка/Ext/Form.xml b/tests/skills/cases/form-validate/fixtures/borrowed-list-path-no-main/Documents/Заказ/Forms/ФормаСписка/Ext/Form.xml new file mode 100644 index 00000000..2bd7fa98 --- /dev/null +++ b/tests/skills/cases/form-validate/fixtures/borrowed-list-path-no-main/Documents/Заказ/Forms/ФормаСписка/Ext/Form.xml @@ -0,0 +1,44 @@ + +
+ + false + + + + + + Список.Партнер + + + + + +
+
+ + + + false + + + + + + Список.Партнер + + + + + +
+
+ + + + cfg:DynamicList + + true + + +
+ \ No newline at end of file