diff --git a/.claude/skills/form-validate/scripts/form-validate.ps1 b/.claude/skills/form-validate/scripts/form-validate.ps1 index b8d8efa4..5ad34e62 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.13 — Validate 1C managed form +# form-validate v1.14 — Validate 1C managed form # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -56,6 +56,7 @@ try { $nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable) $nsMgr.AddNamespace("f", "http://v8.1c.ru/8.3/xcf/logform") $nsMgr.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core") +$nsMgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance") $root = $xmlDoc.DocumentElement @@ -68,6 +69,7 @@ for ($i = 0; $i -lt 15; $i++) { if (-not $walkDir -or $walkDir -eq (Split-Path $walkDir)) { break } if (Test-Path (Join-Path $walkDir "Configuration.xml")) { $script:isConfigContext = $true + $script:configXmlPath = Join-Path $walkDir "Configuration.xml" break } $walkDir = Split-Path $walkDir @@ -879,6 +881,75 @@ if (-not $stopped) { } } +# --- Check 13: префиксы в значениях объявлены в самом файле --- +# `cfg:DataProcessorObject.X` в при незадекларированном xmlns:cfg — валидный XML, который +# платформа не читает вовсе: «Исключение XDTO произошло при чтении файла». Ошибка типична для +# рукописного XML: префикс скопирован из чужой формы, а объявление в корне забыто. Область видимости +# считаем по узлу (GetNamespaceOfPrefix), а не по корню: локальная xmlns на элементе законна. + +if (-not $stopped) { + $prefixErrors = 0 + $prefixChecked = 0 + + $prefixPattern = '^([A-Za-z_][A-Za-z0-9_.-]*):.+$' + # Значения, где префикс обязан резолвиться: тип реквизита/колонки и xsi:type + # Только листовые узлы: под local-name()='Type' подходит и обёртка , и вложенный , + # а InnerText обёртки — то же значение, иначе одна ошибка сообщалась бы дважды. + foreach ($node in $xmlDoc.SelectNodes("//*[local-name()='Type' or local-name()='TypeSet']", $nsMgr)) { + if ($node.SelectSingleNode("*")) { continue } + $val = $node.InnerText.Trim() + if (-not $val) { continue } + $m = [regex]::Match($val, $prefixPattern) + if (-not $m.Success) { continue } + $prefixChecked++ + $pfx = $m.Groups[1].Value + if (-not $node.GetNamespaceOfPrefix($pfx)) { + Report-Error "13. Type '$val': namespace prefix '${pfx}:' is not declared — the platform cannot read the file (XDTO)" + $prefixErrors++ + } + } + foreach ($node in $xmlDoc.SelectNodes("//*[@xsi:type]", $nsMgr)) { + $val = $node.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance") + $m = [regex]::Match($val, $prefixPattern) + if (-not $m.Success) { continue } + $prefixChecked++ + $pfx = $m.Groups[1].Value + if (-not $node.GetNamespaceOfPrefix($pfx)) { + Report-Error "13. xsi:type='$val': namespace prefix '${pfx}:' is not declared — the platform cannot read the file (XDTO)" + $prefixErrors++ + } + } + + if ($prefixChecked -eq 0) { + Report-OK "13. Namespace prefixes: nothing to check" + } elseif ($prefixErrors -eq 0) { + Report-OK "13. Namespace prefixes: $prefixChecked values, all declared" + } +} + +# --- Check 14: версия формата формы совпадает с версией конфигурации --- +# Версию задаёт платформа, которой выгружали, и в пределах одной выгрузки она едина. Форма из +# другой версии — «Неизвестная версия формата N загружаемого файла»: платформа не читает файл, +# который новее её самой. Типичный след ручной сборки: форму скопировали из свежей конфигурации. + +if (-not $stopped -and $script:configXmlPath) { + $formVer = $root.GetAttribute("version") + $cfgVer = "" + try { + $cfgHead = [System.IO.File]::ReadAllText($script:configXmlPath, [System.Text.Encoding]::UTF8) + $vm = [regex]::Match($cfgHead.Substring(0, [Math]::Min(4000, $cfgHead.Length)), ']*\bversion="([^"]+)"') + if ($vm.Success) { $cfgVer = $vm.Groups[1].Value } + } catch { } + + if (-not $cfgVer -or -not $formVer) { + Report-OK "14. Format version: not comparable" + } elseif ($formVer -ne $cfgVer) { + Report-Error "14. Format version $formVer differs from configuration ($cfgVer) — a dump carries one version, the platform refuses a file it cannot read" + } else { + Report-OK "14. Format version: $formVer, matches configuration" + } +} + # --- Summary --- $checks = $script:okCount + $errors + $warnings diff --git a/.claude/skills/form-validate/scripts/form-validate.py b/.claude/skills/form-validate/scripts/form-validate.py index 44ff8c60..8b6f809f 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.13 — Validate 1C managed form +# form-validate v1.14 — Validate 1C managed form # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -128,6 +128,7 @@ def main(): # Detect context: config vs EPF/ERF is_config_context = False + config_xml_path = '' walk_dir = os.path.dirname(os.path.abspath(form_path)) for _ in range(15): parent = os.path.dirname(walk_dir) @@ -135,6 +136,7 @@ def main(): break if os.path.isfile(os.path.join(walk_dir, 'Configuration.xml')): is_config_context = True + config_xml_path = os.path.join(walk_dir, 'Configuration.xml') break walk_dir = parent @@ -808,6 +810,69 @@ def main(): else: report_ok('12. Types: no type values to check') + # --- Check 13: префиксы в значениях объявлены в самом файле --- + # `cfg:DataProcessorObject.X` в при незадекларированном xmlns:cfg — валидный XML, который + # платформа не читает вовсе: «Исключение XDTO произошло при чтении файла». Ошибка типична для + # рукописного XML: префикс скопирован из чужой формы, а объявление в корне забыто. Область видимости + # считаем по узлу (nsmap элемента), а не по корню: локальная xmlns на элементе законна. + if not stopped: + prefix_errors = 0 + prefix_checked = 0 + prefix_re = re.compile(r'^([A-Za-z_][A-Za-z0-9_.-]*):.+$') + + for node in root.iter(): + if not isinstance(node.tag, str): + continue + ln = localname(node) + values = [] + if ln in ('Type', 'TypeSet'): + values.append((node.text or '').strip()) + xsi_type = node.get(f'{{{"http://www.w3.org/2001/XMLSchema-instance"}}}type') + if xsi_type: + values.append(xsi_type.strip()) + for val in values: + if not val: + continue + m = prefix_re.match(val) + if not m: + continue + prefix_checked += 1 + pfx = m.group(1) + if pfx not in node.nsmap: + kind = "xsi:type" if val == xsi_type else "Type" + report_error(f"13. {kind} '{val}': namespace prefix '{pfx}:' is not declared " + "— the platform cannot read the file (XDTO)") + prefix_errors += 1 + + if prefix_checked == 0: + report_ok('13. Namespace prefixes: nothing to check') + elif prefix_errors == 0: + report_ok(f'13. Namespace prefixes: {prefix_checked} values, all declared') + + # --- Check 14: версия формата формы совпадает с версией конфигурации --- + # Версию задаёт платформа, которой выгружали, и в пределах одной выгрузки она едина. Форма из + # другой версии — «Неизвестная версия формата N загружаемого файла»: платформа не читает файл, + # который новее её самой. Типичный след ручной сборки: форму скопировали из свежей конфигурации. + if not stopped and config_xml_path: + form_ver = root.get('version', '') + cfg_ver = '' + try: + with open(config_xml_path, 'r', encoding='utf-8-sig', errors='ignore') as fh: + head = fh.read(4000) + vm = re.search(r']*[ \t]version="([^"]+)"', head) + if vm: + cfg_ver = vm.group(1) + except OSError: + pass + + if not cfg_ver or not form_ver: + report_ok('14. Format version: not comparable') + elif form_ver != cfg_ver: + report_error(f'14. Format version {form_ver} differs from configuration ({cfg_ver}) ' + '— a dump carries one version, the platform refuses a file it cannot read') + else: + report_ok(f'14. Format version: {form_ver}, matches configuration') + # --- Finalize --- checks = ok_count + errors + warnings if errors == 0 and warnings == 0 and not detailed: diff --git a/tests/skills/cases/form-validate/fixtures/undeclared-prefix/Configuration.xml b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/Configuration.xml new file mode 100644 index 00000000..cdd7f810 --- /dev/null +++ b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/Configuration.xml @@ -0,0 +1,252 @@ + + + + + + 9cd510cd-abfc-11d4-9434-004095e12fc7 + 6590ae26-c618-47e9-88ff-0287ccddbb95 + + + 9fcd25a0-4822-11d4-9414-008048da11f9 + 68758358-e384-48ad-8726-b5c4df78a527 + + + e3687481-0a87-462c-a166-9f34594f9bba + 3598430a-1ac9-4c5a-9ef7-2c89ffda034f + + + 9de14907-ec23-4a07-96f0-85521cb6b53b + b51935d6-9b8e-48e7-be6e-893225c8cb17 + + + 51f2d5d8-ea4d-4064-8892-82951750031e + 7ffb0fba-e5fa-4351-8df3-36d2bda9f0d9 + + + e68182ea-4237-4383-967f-90c1e3370bc7 + 1d4ef11d-4faf-44b1-a2fd-bf8fca4471f6 + + + fb282519-d103-4dd3-bc12-cb271d631dfc + 39689cbd-2bc7-4095-b7ab-c2b78a8d80f6 + + + + ТестоваяКонфигурация + + + ru + ТестоваяКонфигурация + + + + + Version8_3_24 + ManagedApplication + + PlatformApplication + + Russian + + + + + false + false + false + + + + + + + + + + + + + + + + + + + + + + Biometrics + true + + + Location + false + + + BackgroundLocation + false + + + BluetoothPrinters + false + + + WiFiPrinters + false + + + Contacts + false + + + Calendars + false + + + PushNotifications + false + + + LocalNotifications + false + + + InAppPurchases + false + + + PersonalComputerFileExchange + false + + + Ads + false + + + NumberDialing + false + + + CallProcessing + false + + + CallLog + false + + + AutoSendSMS + false + + + ReceiveSMS + false + + + SMSLog + false + + + Camera + false + + + Microphone + false + + + MusicLibrary + false + + + PictureAndVideoLibraries + false + + + AudioPlaybackAndVibration + false + + + BackgroundAudioPlaybackAndVibration + false + + + InstallPackages + false + + + OSBackup + true + + + ApplicationUsageStatistics + false + + + BarcodeScanning + false + + + BackgroundAudioRecording + false + + + AllFilesAccess + false + + + Videoconferences + false + + + NFC + false + + + DocumentScanning + false + + + SpeechToText + false + + + Geofences + false + + + IncomingShareRequests + false + + + AllIncomingShareRequestsTypesProcessing + false + + + + + + Normal + + + Language.Русский + + + + + + Managed + NotAutoFree + DontUse + DontUse + TaxiEnableVersion8_2 + DontUse + Version8_3_24 + + + + Русский + Opaque + + + \ No newline at end of file diff --git a/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque.xml b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque.xml new file mode 100644 index 00000000..ed423503 --- /dev/null +++ b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque.xml @@ -0,0 +1,72 @@ + + + + + + 3ec72b27-689a-48d0-b000-cbf45321b7a1 + 14083b95-8787-4eac-aa27-bc3f06a00a84 + + + ae1c02de-9b63-47e8-a2e6-2e09ec4e7cfa + 43629958-f181-436f-ad8b-1f4d05c70cf2 + + + + Opaque + + + ru + Opaque + + + + true + DataProcessor.Opaque.Form.Форма + + false + + + + + + + Наименование + + + ru + Наименование + + + + + xs:string + + 150 + Variable + + + false + + + + false + + false + false + + + DontCheck + Items + + + Auto + Auto + + + Auto + + +
Форма
+
+
+
\ No newline at end of file diff --git a/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Ext/ManagerModule.bsl b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Ext/ManagerModule.bsl new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Ext/ManagerModule.bsl @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Ext/ObjectModule.bsl b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Ext/ObjectModule.bsl new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Ext/ObjectModule.bsl @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Forms/Форма.xml b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Forms/Форма.xml new file mode 100644 index 00000000..e8e8ef90 --- /dev/null +++ b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Forms/Форма.xml @@ -0,0 +1,22 @@ + + +
+ + Форма + + + ru + Форма + + + + Managed + false + + PlatformApplication + MobilePlatformApplication + + + +
+
\ No newline at end of file diff --git a/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Forms/Форма/Ext/Form.xml new file mode 100644 index 00000000..69835b80 --- /dev/null +++ b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Forms/Форма/Ext/Form.xml @@ -0,0 +1,35 @@ + +
+ + <v8:item> + <v8:lang>ru</v8:lang> + <v8:content>Тест</v8:content> + </v8:item> + + + + + 1/0:a917a122-f663-4c45-8de0-fd5104007de3 + + + + + 10 + + + + + Объект.Наименование + + + + + + + + cfg:DataProcessorObject.Opaque + + true + + + diff --git a/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Forms/Форма/Ext/Form/Module.bsl b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Forms/Форма/Ext/Form/Module.bsl new file mode 100644 index 00000000..4cf92998 --- /dev/null +++ b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/DataProcessors/Opaque/Forms/Форма/Ext/Form/Module.bsl @@ -0,0 +1,19 @@ +#Область ОбработчикиСобытийФормы + +#КонецОбласти + +#Область ОбработчикиСобытийЭлементовФормы + +#КонецОбласти + +#Область ОбработчикиКомандФормы + +#КонецОбласти + +#Область ОбработчикиОповещений + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +#КонецОбласти \ No newline at end of file diff --git a/tests/skills/cases/form-validate/fixtures/undeclared-prefix/Ext/ClientApplicationInterface.xml b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/Ext/ClientApplicationInterface.xml new file mode 100644 index 00000000..20af10dc --- /dev/null +++ b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/Ext/ClientApplicationInterface.xml @@ -0,0 +1,18 @@ + + + + + cbab57f2-a0f3-4f0a-89ea-4cb19570ab75 + + + + + b553047f-c9aa-4157-978d-448ecad24248 + + + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/form-validate/fixtures/undeclared-prefix/Languages/Русский.xml b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/Languages/Русский.xml new file mode 100644 index 00000000..4ff64aea --- /dev/null +++ b/tests/skills/cases/form-validate/fixtures/undeclared-prefix/Languages/Русский.xml @@ -0,0 +1,16 @@ + + + + + Русский + + + ru + Русский + + + + ru + + + \ No newline at end of file diff --git a/tests/skills/cases/form-validate/undeclared-prefix.json b/tests/skills/cases/form-validate/undeclared-prefix.json new file mode 100644 index 00000000..b1aae19a --- /dev/null +++ b/tests/skills/cases/form-validate/undeclared-prefix.json @@ -0,0 +1,8 @@ +{ + "name": "Ошибка: префикс в значении типа не объявлен — платформа не прочитает файл (XDTO)", + "setup": "fixture:undeclared-prefix", + "params": { "formPath": "DataProcessors/Opaque/Forms/Форма" }, + "expectError": true, + "expect": { "stdoutContains": "prefix 'cfg:' is not declared" }, + "skipPlatformVerify": "фикстура намеренно нечитаема платформой — в этом и смысл кейса" +}