diff --git a/.claude/skills/form-add/scripts/form-add.ps1 b/.claude/skills/form-add/scripts/form-add.ps1
index f95b94c8..c05a579c 100644
--- a/.claude/skills/form-add/scripts/form-add.ps1
+++ b/.claude/skills/form-add/scripts/form-add.ps1
@@ -1,4 +1,4 @@
-# form-add v1.10 — Add managed form to 1C config object
+# form-add v1.11 — Add managed form to 1C config object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -484,7 +484,10 @@ if (-not $childObjects) {
exit 1
}
-# Добавить
+# Добавить — идемпотентно (не дублировать уже зарегистрированную)
+$alreadyRegistered = [bool]$childObjects.SelectSingleNode("md:Form[text()='$FormName']", $nsMgr)
+
+if (-not $alreadyRegistered) {
$formElem = $xmlDoc.CreateElement("Form", "http://v8.1c.ru/8.3/MDClasses")
$formElem.InnerText = $FormName
@@ -538,6 +541,7 @@ if ($insertBefore) {
}
}
}
+}
# --- SetDefault ---
@@ -603,7 +607,11 @@ Write-Host " Metadata: $objDirName\$objBaseName\Forms\$FormName.xml"
Write-Host " Form: $objDirName\$objBaseName\Forms\$FormName\Ext\Form.xml"
Write-Host " Module: $objDirName\$objBaseName\Forms\$FormName\Ext\Form\Module.bsl"
Write-Host ""
-Write-Host "Registered: in ChildObjects"
+if ($alreadyRegistered) {
+ Write-Host "Already registered: in ChildObjects (skipped duplicate)"
+} else {
+ Write-Host "Registered: in ChildObjects"
+}
if ($defaultUpdated) {
Write-Host "${defaultPropName}: $defaultValue"
}
diff --git a/.claude/skills/form-add/scripts/form-add.py b/.claude/skills/form-add/scripts/form-add.py
index 80cabd8f..872d52e8 100644
--- a/.claude/skills/form-add/scripts/form-add.py
+++ b/.claude/skills/form-add/scripts/form-add.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# form-add v1.10 — Add managed form to 1C config object
+# form-add v1.11 — Add managed form to 1C config object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -592,47 +592,50 @@ def main():
print(f"Не найден элемент ChildObjects в {object_path}", file=sys.stderr)
sys.exit(1)
- # Add
- form_elem = etree.Element(f"{{{ns}}}Form")
- form_elem.text = form_name
+ # Add — idempotent (do not duplicate already-registered form)
+ already_registered = child_objects.find(f"md:Form[.='{form_name}']", NSMAP) is not None
- # Find first to insert before it
- first_template = child_objects.find("md:Template", NSMAP)
- # Find first to insert before it (if no Template)
- first_tabular = child_objects.find("md:TabularSection", NSMAP)
+ if not already_registered:
+ form_elem = etree.Element(f"{{{ns}}}Form")
+ form_elem.text = form_name
- # Determine insertion point: before Template, before TabularSection, or at end
- insert_before = None
- if first_template is not None:
- insert_before = first_template
- elif first_tabular is not None:
- insert_before = first_tabular
+ # Find first to insert before it
+ first_template = child_objects.find("md:Template", NSMAP)
+ # Find first to insert before it (if no Template)
+ first_tabular = child_objects.find("md:TabularSection", NSMAP)
- if insert_before is not None:
- # Insert before the found element
- idx = list(child_objects).index(insert_before)
- child_objects.insert(idx, form_elem)
- # Whitespace: form_elem gets "\n\t\t\t" as tail (indent before insert_before)
- form_elem.tail = "\n\t\t\t"
- else:
- # Add to end of ChildObjects
- children = list(child_objects)
- if len(children) == 0 and (child_objects.text is None or child_objects.text.strip() == ""):
- # Empty ChildObjects (self-closing)
- child_objects.text = "\n\t\t\t"
- child_objects.append(form_elem)
- form_elem.tail = "\n\t\t"
+ # Determine insertion point: before Template, before TabularSection, or at end
+ insert_before = None
+ if first_template is not None:
+ insert_before = first_template
+ elif first_tabular is not None:
+ insert_before = first_tabular
+
+ if insert_before is not None:
+ # Insert before the found element
+ idx = list(child_objects).index(insert_before)
+ child_objects.insert(idx, form_elem)
+ # Whitespace: form_elem gets "\n\t\t\t" as tail (indent before insert_before)
+ form_elem.tail = "\n\t\t\t"
else:
- if len(children) > 0:
- last_child = children[-1]
- old_tail = last_child.tail
- last_child.tail = "\n\t\t\t"
- child_objects.append(form_elem)
- form_elem.tail = old_tail if old_tail else "\n\t\t"
- else:
- child_objects.text = (child_objects.text or "") + "\n\t\t\t"
+ # Add to end of ChildObjects
+ children = list(child_objects)
+ if len(children) == 0 and (child_objects.text is None or child_objects.text.strip() == ""):
+ # Empty ChildObjects (self-closing)
+ child_objects.text = "\n\t\t\t"
child_objects.append(form_elem)
form_elem.tail = "\n\t\t"
+ else:
+ if len(children) > 0:
+ last_child = children[-1]
+ old_tail = last_child.tail
+ last_child.tail = "\n\t\t\t"
+ child_objects.append(form_elem)
+ form_elem.tail = old_tail if old_tail else "\n\t\t"
+ else:
+ child_objects.text = (child_objects.text or "") + "\n\t\t\t"
+ child_objects.append(form_elem)
+ form_elem.tail = "\n\t\t"
# --- SetDefault ---
@@ -677,7 +680,10 @@ def main():
print(f" Form: {obj_dir_name}\\{obj_base_name}\\Forms\\{form_name}\\Ext\\Form.xml")
print(f" Module: {obj_dir_name}\\{obj_base_name}\\Forms\\{form_name}\\Ext\\Form\\Module.bsl")
print()
- print(f"Registered: in ChildObjects")
+ if already_registered:
+ print(f"Already registered: in ChildObjects (skipped duplicate)")
+ else:
+ print(f"Registered: in ChildObjects")
if default_updated:
print(f"{default_prop_name}: {default_value}")
print()
diff --git a/.claude/skills/template-add/scripts/add-template.ps1 b/.claude/skills/template-add/scripts/add-template.ps1
index a69acff7..acd7a4c7 100644
--- a/.claude/skills/template-add/scripts/add-template.ps1
+++ b/.claude/skills/template-add/scripts/add-template.ps1
@@ -1,4 +1,4 @@
-# template-add v1.9 — Add template to 1C object
+# template-add v1.10 — Add template to 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -329,7 +329,10 @@ if (-not $childObjects) {
exit 1
}
-# Добавить в конец ChildObjects
+# Добавить в конец ChildObjects — идемпотентно (не дублировать уже зарегистрированный)
+$alreadyRegistered = [bool]$childObjects.SelectSingleNode("md:Template[text()='$TemplateName']", $nsMgr)
+
+if (-not $alreadyRegistered) {
$templateElem = $xmlDoc.CreateElement("Template", "http://v8.1c.ru/8.3/MDClasses")
$templateElem.InnerText = $TemplateName
@@ -349,6 +352,7 @@ if ($childObjects.ChildNodes.Count -eq 0) {
$childObjects.AppendChild($xmlDoc.CreateWhitespace("`n`t`t")) | Out-Null
}
}
+}
# --- 4. MainDataCompositionSchema (для ExternalReport / Report) ---
@@ -392,6 +396,9 @@ $writer.Close()
$stream.Close()
Write-Host "[OK] Создан макет: $TemplateName ($TemplateType)"
+if ($alreadyRegistered) {
+ Write-Host " Already registered: $TemplateName in ChildObjects (skipped duplicate)"
+}
Write-Host " Метаданные: $templateMetaPath"
Write-Host " Содержимое: $templateFilePath"
if ($mainDCSUpdated) {
diff --git a/.claude/skills/template-add/scripts/add-template.py b/.claude/skills/template-add/scripts/add-template.py
index 0b07e163..410065bd 100644
--- a/.claude/skills/template-add/scripts/add-template.py
+++ b/.claude/skills/template-add/scripts/add-template.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# add-template v1.9 — Add template to 1C object
+# add-template v1.10 — Add template to 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -446,31 +446,34 @@ def main():
print(f"Не найден элемент ChildObjects в {root_xml_path}", file=sys.stderr)
sys.exit(1)
- # Add to end of ChildObjects
- template_elem = etree.SubElement(child_objects, f"{{{ns}}}Template")
- template_elem.text = template_name
- # Remove auto-appended element to reinsert with proper whitespace
- child_objects.remove(template_elem)
+ # Add to end of ChildObjects — idempotent (do not duplicate already-registered template)
+ already_registered = child_objects.find(f"md:Template[.='{template_name}']", NSMAP) is not None
- children = list(child_objects)
- if len(children) == 0 and (child_objects.text is None or child_objects.text.strip() == ""):
- # Empty ChildObjects (self-closing)
- child_objects.text = "\n\t\t\t"
- child_objects.append(template_elem)
- template_elem.tail = "\n\t\t"
- else:
- if len(children) > 0:
- last_child = children[-1]
- # last_child.tail is the trailing whitespace before
- old_tail = last_child.tail
- last_child.tail = "\n\t\t\t"
- child_objects.append(template_elem)
- template_elem.tail = old_tail if old_tail else "\n\t\t"
- else:
- # Has text content but no element children
- child_objects.text = (child_objects.text or "") + "\n\t\t\t"
+ if not already_registered:
+ template_elem = etree.SubElement(child_objects, f"{{{ns}}}Template")
+ template_elem.text = template_name
+ # Remove auto-appended element to reinsert with proper whitespace
+ child_objects.remove(template_elem)
+
+ children = list(child_objects)
+ if len(children) == 0 and (child_objects.text is None or child_objects.text.strip() == ""):
+ # Empty ChildObjects (self-closing)
+ child_objects.text = "\n\t\t\t"
child_objects.append(template_elem)
template_elem.tail = "\n\t\t"
+ else:
+ if len(children) > 0:
+ last_child = children[-1]
+ # last_child.tail is the trailing whitespace before
+ old_tail = last_child.tail
+ last_child.tail = "\n\t\t\t"
+ child_objects.append(template_elem)
+ template_elem.tail = old_tail if old_tail else "\n\t\t"
+ else:
+ # Has text content but no element children
+ child_objects.text = (child_objects.text or "") + "\n\t\t\t"
+ child_objects.append(template_elem)
+ template_elem.tail = "\n\t\t"
# --- 4. MainDataCompositionSchema (for ExternalReport / Report) ---
@@ -500,6 +503,8 @@ def main():
save_xml_with_bom(tree, root_xml_full)
print(f"[OK] Создан макет: {template_name} ({template_type})")
+ if already_registered:
+ print(f" Already registered: {template_name} in ChildObjects (skipped duplicate)")
print(f" Метаданные: {template_meta_path}")
print(f" Содержимое: {template_file_path}")
if main_dcs_updated:
diff --git a/tests/skills/cases/form-add/epf-compile-then-add.json b/tests/skills/cases/form-add/epf-compile-then-add.json
new file mode 100644
index 00000000..8c4afacd
--- /dev/null
+++ b/tests/skills/cases/form-add/epf-compile-then-add.json
@@ -0,0 +1,21 @@
+{
+ "name": "form-compile затем form-add не дублирует регистрацию формы в ChildObjects",
+ "setup": "none",
+ "preRun": [
+ {
+ "script": "epf-init/scripts/init",
+ "args": { "-Name": "КонсольКода", "-SrcDir": "{workDir}" }
+ },
+ {
+ "script": "form-compile/scripts/form-compile",
+ "input": {
+ "title": "Форма",
+ "attributes": [{ "name": "Объект", "type": "ExternalDataProcessorObject.КонсольКода", "main": true }]
+ },
+ "args": { "-JsonPath": "{inputFile}", "-OutputPath": "{workDir}/КонсольКода/Forms/Форма/Ext/Form.xml" }
+ }
+ ],
+ "params": { "objectPath": "КонсольКода.xml", "formName": "Форма" },
+ "expect": { "stdoutContains": "Already registered" },
+ "validatePath": "КонсольКода/Forms/Форма"
+}
diff --git a/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода.xml b/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода.xml
new file mode 100644
index 00000000..6d5e79c7
--- /dev/null
+++ b/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+ UUID-002
+ UUID-003
+
+
+ UUID-004
+ UUID-005
+
+
+
+ КонсольКода
+
+
+ ru
+ КонсольКода
+
+
+
+ ExternalDataProcessor.КонсольКода.Form.Форма
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Ext/ObjectModule.bsl b/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Ext/ObjectModule.bsl
new file mode 100644
index 00000000..15543d27
--- /dev/null
+++ b/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Ext/ObjectModule.bsl
@@ -0,0 +1,11 @@
+#Область ОписаниеПеременных
+
+#КонецОбласти
+
+#Область ПрограммныйИнтерфейс
+
+#КонецОбласти
+
+#Область СлужебныеПроцедурыИФункции
+
+#КонецОбласти
\ No newline at end of file
diff --git a/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Forms/Форма.xml b/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Forms/Форма.xml
new file mode 100644
index 00000000..dffeea01
--- /dev/null
+++ b/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Forms/Форма.xml
@@ -0,0 +1,22 @@
+
+
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Forms/Форма/Ext/Form.xml
new file mode 100644
index 00000000..3c3d06de
--- /dev/null
+++ b/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Forms/Форма/Ext/Form.xml
@@ -0,0 +1,19 @@
+
+
diff --git a/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Forms/Форма/Ext/Form/Module.bsl b/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Forms/Форма/Ext/Form/Module.bsl
new file mode 100644
index 00000000..8ead4cec
--- /dev/null
+++ b/tests/skills/cases/form-add/snapshots/epf-compile-then-add/КонсольКода/Forms/Форма/Ext/Form/Module.bsl
@@ -0,0 +1,19 @@
+#Область ОбработчикиСобытийФормы
+
+#КонецОбласти
+
+#Область ОбработчикиСобытийЭлементовФормы
+
+#КонецОбласти
+
+#Область ОбработчикиКомандФормы
+
+#КонецОбласти
+
+#Область ОбработчикиОповещений
+
+#КонецОбласти
+
+#Область СлужебныеПроцедурыИФункции
+
+#КонецОбласти
\ No newline at end of file