mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-14 23:39:02 +03:00
fix(form-validate): корень висячих путей — из основного реквизита формы
Проверка 11d искала висячие пути регуляркой, зашитой на корень «Объект». Он такой только у формы объекта: у формы списка корень «Список», у формы записи регистра «Запись». На таких формах регулярка не совпадала, и проверка молча не срабатывала — валидатор рапортовал «чисто» на форме, которую платформа отвергает с «Неверный путь к полю». Узел основного реквизита BaseForm проверка уже доставала строкой выше, но использовала только как булев признак. Теперь из него берётся имя — и для регулярки, и для текста сообщения. Если основного реквизита нет и в BaseForm, корень неизвестен и поведение остаётся прежним. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a424e9cd19
commit
c63b626dd4
@@ -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),
|
||||
# а привязки в <xr:Link> вообще вне его списка тегов. Между тем это ровно тот случай, на котором
|
||||
# платформа отвергает загрузку: «Неверный путь к полю - Объект.X». Правило: если основной реквизит
|
||||
# не объявлен в <Attributes> формы, любой путь с корнем «Объект» не разрешится.
|
||||
# не объявлен в <Attributes> формы, любой путь с его корнем не разрешится.
|
||||
# Корень берётся из основного реквизита BaseForm: «Объект» он только у формы объекта, у формы
|
||||
# списка это «Список», у формы записи регистра «Запись». С зашитым «Объект» проверка на таких
|
||||
# формах молча не срабатывала — валидатор рапортовал «чисто» на форме, которую платформа не примет.
|
||||
$mainAttrDeclared = $false
|
||||
foreach ($attr in $attrNodes) {
|
||||
$maNode = $attr.SelectSingleNode("f:MainAttribute", $nsMgr)
|
||||
@@ -855,15 +858,17 @@ if (-not $stopped -and $isExtension) {
|
||||
# <ChoiceParameterLinks>, а те живут в чужом пространстве имён.
|
||||
$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[^>]*>(Объект\.[^<]+)</(?:\w+:)?\w*DataPath>')) {
|
||||
foreach ($m in [regex]::Matches($rawForm, "<(?:\w+:)?\w*DataPath[^>]*>(${rootPat}\.[^<]+)</(?:\w+:)?\w*DataPath>")) {
|
||||
$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)"
|
||||
}
|
||||
|
||||
@@ -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 <xr:Link> \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 <Attributes> \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 <Attributes> \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\.[^<]+)</(?:\w+:)?\w*DataPath>', 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'\.[^<]+)</(?:\w+:)?\w*DataPath>', 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)")
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Ошибка: путь Список.* в заимствованной форме списка без объявленного основного реквизита",
|
||||
"setup": "fixture:borrowed-list-path-no-main",
|
||||
"params": { "formPath": "Documents/Заказ/Forms/ФормаСписка" },
|
||||
"expectError": true,
|
||||
"expect": { "stdoutContains": ["Список.Партнер", "rooted at 'Список'"] }
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Form xmlns="http://v8.1c.ru/8.3/xcf/logform" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.20">
|
||||
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">
|
||||
<Autofill>false</Autofill>
|
||||
</AutoCommandBar>
|
||||
<ChildItems>
|
||||
<Table name="Список" id="1">
|
||||
<ChildItems>
|
||||
<InputField name="СписокПартнер" id="2">
|
||||
<DataPath>Список.Партнер</DataPath>
|
||||
<ContextMenu name="СписокПартнерКонтекстноеМеню" id="3"/>
|
||||
<ExtendedTooltip name="СписокПартнерРасширеннаяПодсказка" id="4"/>
|
||||
</InputField>
|
||||
</ChildItems>
|
||||
<ExtendedTooltip name="СписокРасширеннаяПодсказка" id="5"/>
|
||||
</Table>
|
||||
</ChildItems>
|
||||
<Attributes/>
|
||||
<BaseForm version="2.20">
|
||||
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">
|
||||
<Autofill>false</Autofill>
|
||||
</AutoCommandBar>
|
||||
<ChildItems>
|
||||
<Table name="Список" id="1">
|
||||
<ChildItems>
|
||||
<InputField name="СписокПартнер" id="2">
|
||||
<DataPath>Список.Партнер</DataPath>
|
||||
<ContextMenu name="СписокПартнерКонтекстноеМеню" id="3"/>
|
||||
<ExtendedTooltip name="СписокПартнерРасширеннаяПодсказка" id="4"/>
|
||||
</InputField>
|
||||
</ChildItems>
|
||||
<ExtendedTooltip name="СписокРасширеннаяПодсказка" id="5"/>
|
||||
</Table>
|
||||
</ChildItems>
|
||||
<Attributes>
|
||||
<Attribute name="Список" id="1">
|
||||
<Type>
|
||||
<v8:Type>cfg:DynamicList</v8:Type>
|
||||
</Type>
|
||||
<MainAttribute>true</MainAttribute>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</BaseForm>
|
||||
</Form>
|
||||
Reference in New Issue
Block a user