diff --git a/tests/skills/cases/meta-compile/catalog-characteristics.json b/tests/skills/cases/meta-compile/catalog-characteristics.json
index 289b6d4d..811a86de 100644
--- a/tests/skills/cases/meta-compile/catalog-characteristics.json
+++ b/tests/skills/cases/meta-compile/catalog-characteristics.json
@@ -1,8 +1,37 @@
{
"name": "Характеристики ПВХ (Дополнительные реквизиты и сведения)",
+ "preRun": [
+ {
+ "script": "meta-compile/scripts/meta-compile",
+ "input": { "type": "ChartOfCharacteristicTypes", "name": "ВидыСвойств",
+ "valueTypes": ["String(100)", "Number(15,2)", "Boolean"] },
+ "args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
+ },
+ {
+ "script": "meta-compile/scripts/meta-compile",
+ "input": { "type": "Catalog", "name": "НаборыДопРеквИСвед",
+ "tabularSections": {
+ "ДополнительныеРеквизиты": ["Свойство: ChartOfCharacteristicTypesRef.ВидыСвойств"],
+ "ДополнительныеСведения": ["Свойство: ChartOfCharacteristicTypesRef.ВидыСвойств"]
+ },
+ "predefined": ["Справочник_Организации"] },
+ "args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
+ },
+ {
+ "script": "meta-compile/scripts/meta-compile",
+ "input": { "type": "InformationRegister", "name": "ДополнительныеСведения",
+ "dimensions": ["Объект: CatalogRef.Организации", "Свойство: ChartOfCharacteristicTypesRef.ВидыСвойств"],
+ "resources": [{ "name": "Значение", "type": "Characteristic.ВидыСвойств",
+ "linkByType": { "dataPath": "InformationRegister.ДополнительныеСведения.Dimension.Свойство" } }] },
+ "args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
+ }
+ ],
"input": {
"type": "Catalog",
"name": "Организации",
+ "tabularSections": {
+ "ДополнительныеРеквизиты": ["Свойство: ChartOfCharacteristicTypesRef.ВидыСвойств", "Значение: Characteristic.ВидыСвойств"]
+ },
"characteristics": [
{ "types": { "from": "Справочник.НаборыДопРеквИСвед.ДополнительныеРеквизиты",
"key": "Свойство", "filterField": "Ссылка", "filterValue": "Catalog.НаборыДопРеквИСвед.Справочник_Организации" },
diff --git a/tests/skills/cases/meta-compile/catalog-command.json b/tests/skills/cases/meta-compile/catalog-command.json
index 5fe3abac..503a4d93 100644
--- a/tests/skills/cases/meta-compile/catalog-command.json
+++ b/tests/skills/cases/meta-compile/catalog-command.json
@@ -14,10 +14,12 @@
},
"ЗагрузитьИзФайла": {
"synonym": "Загрузить из файла",
+ "group": "FormCommandBarImportant",
"picture": "CommonPicture.Загрузка",
"loadTransparent": false
},
"ОбновитьСписок": {
+ "group": "FormNavigationPanelGoTo",
"picture": { "src": "StdPicture.Refresh", "transparentPixel": { "x": 0, "y": 0 } }
}
}
diff --git a/tests/skills/cases/meta-compile/catalog-inputbystring-datalock.json b/tests/skills/cases/meta-compile/catalog-inputbystring-datalock.json
index f08b91f5..5eedb7aa 100644
--- a/tests/skills/cases/meta-compile/catalog-inputbystring-datalock.json
+++ b/tests/skills/cases/meta-compile/catalog-inputbystring-datalock.json
@@ -3,14 +3,16 @@
"input": {
"type": "Catalog",
"name": "ДоговорыКонтрагентов",
+ "owners": ["Catalog.Контрагенты"],
"codeLength": 11,
"descriptionLength": 100,
- "inputByString": ["Код", "Наименование", "Контрагент"],
+ "inputByString": ["Код", "Наименование", "НомерДоговора"],
"dataLockFields": ["Организация", "Контрагент", "Владелец"],
"basedOn": ["Catalog.Контрагенты", "Document.ЗаказПоставщику"],
"attributes": [
"Организация: CatalogRef.Организации",
- "Контрагент: CatalogRef.Контрагенты"
+ "Контрагент: CatalogRef.Контрагенты",
+ "НомерДоговора: String(20) | index"
]
},
"validatePath": "Catalogs/ДоговорыКонтрагентов",
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/НаборыДопРеквИСвед.xml b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/НаборыДопРеквИСвед.xml
new file mode 100644
index 00000000..0aa8e899
--- /dev/null
+++ b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/НаборыДопРеквИСвед.xml
@@ -0,0 +1,276 @@
+
+
+
+
+
+ UUID-002
+ UUID-003
+
+
+ UUID-004
+ UUID-005
+
+
+ UUID-006
+ UUID-007
+
+
+ UUID-008
+ UUID-009
+
+
+ UUID-010
+ UUID-011
+
+
+
+ НаборыДопРеквИСвед
+
+
+ ru
+ Наборы доп рекв исвед
+
+
+
+ false
+ HierarchyFoldersAndItems
+ false
+ 2
+ true
+ true
+
+ ToItems
+ 9
+ 25
+ String
+ Variable
+ WholeCatalog
+ false
+ true
+ AsDescription
+
+ Auto
+ InDialog
+ false
+ BothWays
+
+ Catalog.НаборыДопРеквИСвед.StandardAttribute.Description
+ Catalog.НаборыДопРеквИСвед.StandardAttribute.Code
+
+ Begin
+ DontUse
+ Directly
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+ Managed
+ Use
+
+
+
+
+
+ Use
+ Auto
+ DontUse
+ false
+ false
+
+
+
+
+
+ UUID-013
+ UUID-014
+
+
+ UUID-015
+ UUID-016
+
+
+
+ ДополнительныеРеквизиты
+
+
+ ru
+ Дополнительные реквизиты
+
+
+
+
+ DontCheck
+
+
+
+ DontCheck
+ false
+ false
+ Auto
+
+
+ false
+
+
+ Auto
+ Auto
+
+ false
+ Use
+ false
+
+
+
+ Use
+
+
+
+
+
+
+
+
+
+
+
+ Свойство
+
+
+ ru
+ Свойство
+
+
+
+
+ d5p1:ChartOfCharacteristicTypesRef.ВидыСвойств
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+ DontIndex
+ Use
+ Use
+
+
+
+
+
+
+
+ UUID-019
+ UUID-020
+
+
+ UUID-021
+ UUID-022
+
+
+
+ ДополнительныеСведения
+
+
+ ru
+ Дополнительные сведения
+
+
+
+
+ DontCheck
+
+
+
+ DontCheck
+ false
+ false
+ Auto
+
+
+ false
+
+
+ Auto
+ Auto
+
+ false
+ Use
+ false
+
+
+
+ Use
+
+
+
+
+
+
+
+
+
+
+
+ Свойство
+
+
+ ru
+ Свойство
+
+
+
+
+ d5p1:ChartOfCharacteristicTypesRef.ВидыСвойств
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+ DontIndex
+ Use
+ Use
+
+
+
+
+
+
+
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/НаборыДопРеквИСвед/Ext/ObjectModule.bsl b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/НаборыДопРеквИСвед/Ext/ObjectModule.bsl
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/НаборыДопРеквИСвед/Ext/Predefined.xml b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/НаборыДопРеквИСвед/Ext/Predefined.xml
new file mode 100644
index 00000000..6ae0e82a
--- /dev/null
+++ b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/НаборыДопРеквИСвед/Ext/Predefined.xml
@@ -0,0 +1,9 @@
+
+
+ -
+ Справочник_Организации
+
+ Справочник_организации
+ false
+
+
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/Организации.xml b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/Организации.xml
index 8dd0969c..269b3dd8 100644
--- a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/Организации.xml
+++ b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Catalogs/Организации.xml
@@ -119,6 +119,136 @@
false
false
-
+
+
+
+
+ UUID-013
+ UUID-014
+
+
+ UUID-015
+ UUID-016
+
+
+
+ ДополнительныеРеквизиты
+
+
+ ru
+ Дополнительные реквизиты
+
+
+
+
+ DontCheck
+
+
+
+ DontCheck
+ false
+ false
+ Auto
+
+
+ false
+
+
+ Auto
+ Auto
+
+ false
+ Use
+ false
+
+
+
+ Use
+
+
+
+
+
+
+
+
+
+
+
+ Свойство
+
+
+ ru
+ Свойство
+
+
+
+
+ d5p1:ChartOfCharacteristicTypesRef.ВидыСвойств
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+ DontIndex
+ Use
+ Use
+
+
+
+
+ Значение
+
+
+ ru
+ Значение
+
+
+
+
+ cfg:Characteristic.ВидыСвойств
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+ DontIndex
+ Use
+ Use
+
+
+
+
+
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/ChartsOfCharacteristicTypes/ВидыСвойств.xml b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/ChartsOfCharacteristicTypes/ВидыСвойств.xml
new file mode 100644
index 00000000..1c1656b7
--- /dev/null
+++ b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/ChartsOfCharacteristicTypes/ВидыСвойств.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+ UUID-002
+ UUID-003
+
+
+ UUID-004
+ UUID-005
+
+
+ UUID-006
+ UUID-007
+
+
+ UUID-008
+ UUID-009
+
+
+ UUID-010
+ UUID-011
+
+
+ UUID-012
+ UUID-013
+
+
+
+ ВидыСвойств
+
+
+ ru
+ Виды свойств
+
+
+
+ true
+ false
+
+
+ xs:string
+
+ 100
+ Variable
+
+ xs:decimal
+
+ 15
+ 2
+ Any
+
+ xs:boolean
+
+ false
+ true
+ 9
+ Variable
+ 100
+ WholeCharacteristicKind
+ true
+ true
+ AsDescription
+
+ Auto
+ InDialog
+ false
+ BothWays
+
+ ChartOfCharacteristicTypes.ВидыСвойств.StandardAttribute.Description
+ ChartOfCharacteristicTypes.ВидыСвойств.StandardAttribute.Code
+
+ DontUse
+ Begin
+ Directly
+ DontUse
+ Auto
+
+
+
+
+
+
+
+
+
+
+
+
+ Managed
+ Use
+
+
+
+
+
+ DontUse
+ false
+ false
+
+
+
+
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/ChartsOfCharacteristicTypes/ВидыСвойств/Ext/ObjectModule.bsl b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/ChartsOfCharacteristicTypes/ВидыСвойств/Ext/ObjectModule.bsl
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Configuration.xml b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Configuration.xml
index 74ca373f..f8de87e9 100644
--- a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Configuration.xml
+++ b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/Configuration.xml
@@ -246,7 +246,10 @@
Русский
+ ВидыСвойств
+ НаборыДопРеквИСвед
Организации
+ ДополнительныеСведения
\ No newline at end of file
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/InformationRegisters/ДополнительныеСведения.xml b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/InformationRegisters/ДополнительныеСведения.xml
new file mode 100644
index 00000000..46156e10
--- /dev/null
+++ b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/InformationRegisters/ДополнительныеСведения.xml
@@ -0,0 +1,301 @@
+
+
+
+
+
+ UUID-002
+ UUID-003
+
+
+ UUID-004
+ UUID-005
+
+
+ UUID-006
+ UUID-007
+
+
+ UUID-008
+ UUID-009
+
+
+ UUID-010
+ UUID-011
+
+
+ UUID-012
+ UUID-013
+
+
+ UUID-014
+ UUID-015
+
+
+
+ ДополнительныеСведения
+
+
+ ru
+ Дополнительные сведения
+
+
+
+ true
+ InDialog
+
+
+
+
+
+
+
+ DontCheck
+ false
+ false
+ Auto
+
+
+ false
+
+
+ Auto
+ Auto
+
+ false
+ Use
+ false
+
+
+
+ Use
+
+
+
+
+
+
+
+ DontCheck
+ false
+ false
+ Auto
+
+
+ false
+
+
+ Auto
+ Auto
+
+ false
+ Use
+ false
+
+
+
+ Use
+
+
+
+
+
+
+
+ DontCheck
+ false
+ false
+ Auto
+
+
+ false
+
+
+ Auto
+ Auto
+
+ false
+ Use
+ false
+
+
+
+ Use
+
+
+
+
+
+
+
+ DontCheck
+ false
+ false
+ Auto
+
+
+ false
+
+
+ Auto
+ Auto
+
+ false
+ Use
+ false
+
+
+
+ Use
+
+
+
+
+
+
+ Nonperiodical
+ Independent
+ false
+ false
+ Managed
+ Use
+ false
+ false
+
+
+
+
+
+ DontUse
+ false
+ false
+
+
+
+
+ Значение
+
+
+ ru
+ Значение
+
+
+
+
+ cfg:Characteristic.ВидыСвойств
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ InformationRegister.ДополнительныеСведения.Dimension.Свойство
+ 0
+
+ Auto
+ DontIndex
+ Use
+ Use
+
+
+
+
+ Объект
+
+
+ ru
+ Объект
+
+
+
+
+ d5p1:CatalogRef.Организации
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+ false
+ false
+ false
+ DontIndex
+ Use
+ Use
+
+
+
+
+ Свойство
+
+
+ ru
+ Свойство
+
+
+
+
+ d5p1:ChartOfCharacteristicTypesRef.ВидыСвойств
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+ false
+ false
+ false
+ DontIndex
+ Use
+ Use
+
+
+
+
+
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/InformationRegisters/ДополнительныеСведения/Ext/RecordSetModule.bsl b/tests/skills/cases/meta-compile/snapshots/catalog-characteristics/InformationRegisters/ДополнительныеСведения/Ext/RecordSetModule.bsl
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-command/Catalogs/Номенклатура.xml b/tests/skills/cases/meta-compile/snapshots/catalog-command/Catalogs/Номенклатура.xml
index 2267f1f4..b256d631 100644
--- a/tests/skills/cases/meta-compile/snapshots/catalog-command/Catalogs/Номенклатура.xml
+++ b/tests/skills/cases/meta-compile/snapshots/catalog-command/Catalogs/Номенклатура.xml
@@ -132,7 +132,7 @@
-
+ FormCommandBarImportant
Single
false
@@ -156,7 +156,7 @@
-
+ FormNavigationPanelGoTo
Single
false
diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-inputbystring-datalock/Catalogs/ДоговорыКонтрагентов.xml b/tests/skills/cases/meta-compile/snapshots/catalog-inputbystring-datalock/Catalogs/ДоговорыКонтрагентов.xml
index 8180d277..b649a77d 100644
--- a/tests/skills/cases/meta-compile/snapshots/catalog-inputbystring-datalock/Catalogs/ДоговорыКонтрагентов.xml
+++ b/tests/skills/cases/meta-compile/snapshots/catalog-inputbystring-datalock/Catalogs/ДоговорыКонтрагентов.xml
@@ -38,7 +38,9 @@
2
true
true
-
+
+ Catalog.Контрагенты
+
ToItems
11
100
@@ -56,7 +58,7 @@
Catalog.ДоговорыКонтрагентов.StandardAttribute.Code
Catalog.ДоговорыКонтрагентов.StandardAttribute.Description
- Catalog.ДоговорыКонтрагентов.Attribute.Контрагент
+ Catalog.ДоговорыКонтрагентов.Attribute.НомерДоговора
Begin
DontUse
@@ -175,6 +177,50 @@
Use
+
+
+ НомерДоговора
+
+
+ ru
+ Номер договора
+
+
+
+
+ xs:string
+
+ 20
+ Variable
+
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+ Index
+ Use
+ Use
+
+
diff --git a/tests/skills/verify-snapshots.mjs b/tests/skills/verify-snapshots.mjs
index 0bbdf2e1..baedaf16 100644
--- a/tests/skills/verify-snapshots.mjs
+++ b/tests/skills/verify-snapshots.mjs
@@ -111,6 +111,18 @@ function extractTypeRefs(input) {
refs.set(`${m[1]}.${m[2]}`, { type: m[1], name: m[2] });
}
+ // Прямые ссылки Тип.Имя: картинки, макеты, хранилища настроек, определяемые типы
+ const directPattern2 = new RegExp(`(CommonPicture|CommonTemplate|SettingsStorage|DefinedType)\\.(${ID})`, 'g');
+ while ((m = directPattern2.exec(json)) !== null) {
+ refs.set(`${m[1]}.${m[2]}`, { type: m[1], name: m[2] });
+ }
+
+ // Characteristic.X (тип-характеристика в составе типа значения) → ChartOfCharacteristicTypes.X
+ const charPattern = new RegExp(`Characteristic\\.(${ID})`, 'g');
+ while ((m = charPattern.exec(json)) !== null) {
+ refs.set(`ChartOfCharacteristicTypes.${m[1]}`, { type: 'ChartOfCharacteristicTypes', name: m[1] });
+ }
+
const objPattern = new RegExp(`(Document|Catalog|BusinessProcess|Task|ExchangePlan)Object\\.(${ID})`, 'g');
while ((m = objPattern.exec(json)) !== null) {
refs.set(`${m[1]}.${m[2]}`, { type: m[1], name: m[2] });
@@ -223,6 +235,20 @@ function getStructuralDeps(input) {
}
}
break;
+ case 'EventSubscription': {
+ // Обработчик подписки ссылается на экспортный метод общего модуля — 1С требует его существования.
+ // Стабим CommonModule и дописываем экспортную процедуру в его модуль (postWrite).
+ let h = String(inp.handler || '').replace(/^CommonModule\./, '');
+ const hdot = h.indexOf('.');
+ if (hdot > 0) {
+ const modName = h.substring(0, hdot), methodName = h.substring(hdot + 1);
+ deps.push({ type: 'CommonModule', name: modName,
+ dsl: { type: 'CommonModule', name: modName, server: true },
+ postWrite: [{ relPath: `CommonModules/${modName}/Ext/Module.bsl`,
+ content: `Процедура ${methodName}(Источник, Отказ) Экспорт\nКонецПроцедуры\n` }] });
+ }
+ break;
+ }
case 'Sequence':
// Документы последовательности (documents) + реквизиты из documentMap — 1С требует существования
// самого Document.X И реквизита, на который ссылается измерение. Стабим документ с нужными реквизитами
@@ -267,6 +293,10 @@ function makeStubDSL(type, name) {
case 'Task': return { type: 'Task', name };
case 'ExchangePlan': return { type: 'ExchangePlan', name, codeLength: 9, descriptionLength: 100 };
case 'Role': return { type: 'Role', name: name };
+ case 'DefinedType': return { type: 'DefinedType', name, valueType: 'Number(15,2)' };
+ case 'CommonPicture': return { type: 'CommonPicture', name };
+ case 'CommonTemplate': return { type: 'CommonTemplate', name };
+ case 'SettingsStorage': return { type: 'SettingsStorage', name };
case 'Subsystem': return null; // Subsystems need special handling
default: return null;
}
@@ -283,6 +313,7 @@ const TYPE_TO_PREFIX = {
EventSubscription: 'EventSubscription', ScheduledJob: 'ScheduledJob',
DefinedType: 'DefinedType', HTTPService: 'HTTPService', WebService: 'WebService',
Subsystem: 'Subsystem', Role: 'Role',
+ CommonPicture: 'CommonPicture', CommonTemplate: 'CommonTemplate', SettingsStorage: 'SettingsStorage',
};
const TYPE_TO_DIR = {
@@ -296,8 +327,134 @@ const TYPE_TO_DIR = {
EventSubscription: 'EventSubscriptions', ScheduledJob: 'ScheduledJobs',
DefinedType: 'DefinedTypes', HTTPService: 'HTTPServices', WebService: 'WebServices',
Subsystem: 'Subsystems', Role: 'Roles',
+ CommonPicture: 'CommonPictures', CommonTemplate: 'CommonTemplates', SettingsStorage: 'SettingsStorages',
};
+// ─── Кросс-объектные ссылки на поля → богатые стабы ─────────────────────────
+// Разбор MDObjectRef-пути "Тип.Имя[.ТабличнаяЧасть.ТЧ].Реквизит|Измерение|Ресурс.Поле" (+рус.синонимы)
+// в стаб объекта с нужными полями. Плюс сбор предопределённых значений перечислений / элементов справочников
+// из fillValue/choiceParameters. Обобщение ветки Sequence (documentMap) на все типы, ссылающиеся на чужие поля.
+
+const TYPE_SYN = {
+ 'Документ': 'Document', 'Справочник': 'Catalog', 'РегистрНакопления': 'AccumulationRegister',
+ 'РегистрСведений': 'InformationRegister', 'РегистрБухгалтерии': 'AccountingRegister',
+ 'РегистрРасчета': 'CalculationRegister', 'Константа': 'Constant',
+ 'ПланВидовХарактеристик': 'ChartOfCharacteristicTypes', 'ПланСчетов': 'ChartOfAccounts',
+ 'ПланВидовРасчета': 'ChartOfCalculationTypes', 'ПланОбмена': 'ExchangePlan',
+ 'БизнесПроцесс': 'BusinessProcess', 'Задача': 'Task',
+};
+const FIELD_SYN = { 'Реквизит': 'Attribute', 'Измерение': 'Dimension', 'Ресурс': 'Resource', 'ТабличнаяЧасть': 'TabularSection' };
+const asArray = (v) => (v == null ? [] : (Array.isArray(v) ? v : [v]));
+
+function parseFieldRef(rawPath) {
+ const parts = String(rawPath).split(':')[0].trim().split('.');
+ if (parts.length < 2) return null;
+ const type = TYPE_SYN[parts[0]] || parts[0];
+ const name = parts[1];
+ let ts = null, fieldKind = null, fieldName = null, i = 2;
+ while (i < parts.length) {
+ const seg = FIELD_SYN[parts[i]] || parts[i];
+ if (seg === 'TabularSection' && i + 1 < parts.length) { ts = parts[i + 1]; i += 2; continue; }
+ if ((seg === 'Attribute' || seg === 'Dimension' || seg === 'Resource') && i + 1 < parts.length) {
+ fieldKind = seg; fieldName = parts[i + 1]; i += 2; continue;
+ }
+ i++;
+ }
+ return { type, name, ts, fieldKind, fieldName };
+}
+
+function getFieldStubs(input) {
+ const inputs = Array.isArray(input) ? input : [input];
+ const mainKeys = new Set(inputs.filter(i => i && i.type).map(i => `${i.type}.${i.name}`));
+ const acc = new Map(); // key -> { type, name, attrs, dims, res, tsAttrs, enumVals, predef }
+ const ID2 = '[\\wА-Яа-яЁё]+';
+
+ const ent = (type, name) => {
+ const key = `${type}.${name}`;
+ if (!TYPE_TO_DIR[type] || mainKeys.has(key)) return null; // неизвестный тип или сам проверяемый объект — не трогаем
+ let e = acc.get(key);
+ if (!e) { e = { type, name, attrs: new Set(), dims: new Set(), res: new Set(), tsAttrs: new Map(), enumVals: new Set(), predef: new Set() }; acc.set(key, e); }
+ return e;
+ };
+ const addRef = (raw, attrType) => {
+ if (!raw) return;
+ const p = parseFieldRef(raw); if (!p) return;
+ const e = ent(p.type, p.name); if (!e) return;
+ if (p.fieldKind) {
+ const spec = `${p.fieldName}: ${attrType || 'String(10)'}`;
+ if (p.ts) { if (!e.tsAttrs.has(p.ts)) e.tsAttrs.set(p.ts, new Set()); e.tsAttrs.get(p.ts).add(spec); }
+ else if (p.fieldKind === 'Attribute') e.attrs.add(spec);
+ else if (p.fieldKind === 'Dimension') e.dims.add(spec);
+ else if (p.fieldKind === 'Resource') e.res.add(spec);
+ }
+ };
+ const addPredef = (val, ctxType) => {
+ if (typeof val !== 'string' || !val) return;
+ if (val === 'EmptyRef' || val.includes('ПустаяСсылка')) return;
+ let m = val.match(new RegExp(`(?:Enum|Перечисление)\\.(${ID2})\\.(?:EnumValue|ЗначениеПеречисления)\\.(${ID2})`));
+ if (m) { const e = ent('Enum', m[1]); if (e) e.enumVals.add(m[2]); return; }
+ m = val.match(new RegExp(`^(?:Catalog|Справочник)\\.(${ID2})\\.(${ID2})$`));
+ if (m && m[2] !== 'EmptyRef' && m[2] !== 'Predefined') { const e = ent('Catalog', m[1]); if (e) e.predef.add(m[2]); return; }
+ if (ctxType && !val.includes('.')) { // короткая запись — тип несёт реквизит
+ let em = ctxType.match(/^EnumRef\.(.+)$/); if (em) { const e = ent('Enum', em[1]); if (e) e.enumVals.add(val); return; }
+ let cm = ctxType.match(/^CatalogRef\.(.+)$/); if (cm) { const e = ent('Catalog', cm[1]); if (e) e.predef.add(val); return; }
+ }
+ };
+ const scanAttrs = (attrs) => {
+ for (const a of asArray(attrs)) {
+ if (!a || typeof a !== 'object') continue;
+ if (typeof a.fillValue === 'string') addPredef(a.fillValue, a.type);
+ for (const cp of asArray(a.choiceParameters)) {
+ if (cp && typeof cp === 'object') for (const v of asArray(cp.value)) addPredef(v, cp.type);
+ }
+ }
+ };
+
+ for (const inp of inputs) {
+ if (!inp || !inp.type) continue;
+ switch (inp.type) {
+ case 'FilterCriterion': for (const c of asArray(inp.content)) addRef(c, inp.valueType); break;
+ case 'FunctionalOption': addRef(inp.location); for (const c of asArray(inp.content)) addRef(c); break;
+ case 'FunctionalOptionsParameter': for (const u of asArray(inp.use)) addRef(u); break;
+ case 'CommonAttribute':
+ for (const c of asArray(inp.content)) addRef(typeof c === 'string' ? c : (c.metadata || c['Метаданные'] || c['объект'] || ''));
+ break;
+ }
+ for (const b of asArray(inp.basedOn)) addRef(b);
+ for (const o of asArray(inp.owners)) addRef(o);
+ scanAttrs(inp.attributes);
+ if (inp.tabularSections && typeof inp.tabularSections === 'object' && !Array.isArray(inp.tabularSections)) {
+ for (const ts of Object.values(inp.tabularSections)) scanAttrs(Array.isArray(ts) ? ts : (ts && (ts.attributes || ts.columns)));
+ }
+ if (inp.standardAttributes && typeof inp.standardAttributes === 'object') {
+ for (const sa of Object.values(inp.standardAttributes)) if (sa && typeof sa === 'object') scanAttrs([{ ...sa, type: '' }]);
+ }
+ if (inp.type === 'DocumentJournal') {
+ for (const col of asArray(inp.columns)) if (col && typeof col === 'object') for (const r of asArray(col.references)) addRef(r);
+ }
+ }
+
+ const deps = [];
+ for (const e of acc.values()) {
+ const base = makeStubDSL(e.type, e.name) || { type: e.type, name: e.name };
+ if (e.attrs.size) base.attributes = [...(base.attributes || []), ...e.attrs];
+ if (e.dims.size) base.dimensions = [...(base.dimensions || []), ...e.dims];
+ if (e.res.size) base.resources = [...(base.resources || []), ...e.res];
+ if (e.tsAttrs.size) { base.tabularSections = base.tabularSections || {}; for (const [ts, set] of e.tsAttrs) base.tabularSections[ts] = [...set]; }
+ if (e.enumVals.size) base.values = [...e.enumVals];
+ if (e.predef.size) base.predefined = [...e.predef];
+ deps.push({ type: e.type, name: e.name, dsl: base });
+ // Регистру накопления/бухгалтерии/расчёта нужен документ-регистратор, иначе 1С отвергает
+ // конфигурацию («ни один документ не является регистратором для регистра»).
+ if (e.type === 'AccumulationRegister' || e.type === 'AccountingRegister' || e.type === 'CalculationRegister') {
+ const rn = `Регистратор${e.name}`;
+ deps.push({ type: 'Document', name: rn, dsl: { type: 'Document', name: rn },
+ postEdit: [{ op: 'add-registerRecord', val: `${e.type}.${e.name}` }] });
+ }
+ }
+ return deps;
+}
+
// ─── Auto-detect objects in config dir for cf-edit ──────────────────────────
function scanConfigObjects(configDir) {
@@ -544,13 +701,15 @@ async function verifyCase(skillName, caseName, skillConfig, caseData, opts) {
// Structural deps (scanned across both main input and preRun inputs).
// NB: только зависимости (стабы объектов, на которые ССЫЛАЕТСЯ вход). Верификатор НЕ правит сам
// проверяемый объект — иначе в 1С грузится не то, что выдал компилятор (маскировка дефекта DSL).
- const structDeps = getStructuralDeps(allInputs);
+ const structDeps = [...getStructuralDeps(allInputs), ...getFieldStubs(allInputs)];
const structDSLs = new Map();
const structPostEdits = new Map();
+ const structPostWrites = new Map();
for (const dep of structDeps) {
const key = `${dep.type}.${dep.name}`;
if (dep.dsl) structDSLs.set(key, dep.dsl);
if (dep.postEdit) structPostEdits.set(key, dep.postEdit);
+ if (dep.postWrite) structPostWrites.set(key, dep.postWrite);
}
// Type refs from ALL inputs (main + preRun)
@@ -597,6 +756,17 @@ async function verifyCase(skillName, caseName, skillConfig, caseData, opts) {
}
}
}
+
+ // Post-write: перезаписать файл стаба (напр. тело модуля CommonModule с экспортным методом-обработчиком)
+ const writes = structPostWrites.get(key);
+ if (writes) {
+ for (const w of writes) {
+ try {
+ writeFileSync(join(configDir, w.relPath), '' + w.content, 'utf8');
+ log(`postWrite: ${key}`, true, w.relPath);
+ } catch (e) { log(`postWrite: ${key}`, false, e.message); }
+ }
+ }
}
}
@@ -902,6 +1072,65 @@ async function verifyCase(skillName, caseName, skillConfig, caseData, opts) {
return result;
}
+ // ── Step 5.5: досоздать формы/макеты, на которые ссылается вход ──
+ // meta-compile эмитит ссылки defaultForm/mainDataCompositionSchema/… но сами формы/макеты не создаёт
+ // (это территория form-add/template-add). Для загрузки в 1С досоздаём их на уже скомпилированном объекте
+ // (основной — из Step 4, внешние — из стабов Step 2). Решение: верификатор достраивает дочерние формы/макеты.
+ if (Array.isArray(allInputs) && allInputs.length > 0) {
+ // form-add поддерживает не все типы (напр. DocumentJournal — нет); для прочих форму не досоздать.
+ const FORM_ADD_TYPES = new Set(['Document', 'Catalog', 'DataProcessor', 'Report',
+ 'InformationRegister', 'AccumulationRegister', 'ChartOfAccounts', 'ChartOfCharacteristicTypes',
+ 'ExchangePlan', 'BusinessProcess', 'Task', 'DocumentJournal']);
+ const walk = function* (o, key) {
+ if (o == null) return;
+ if (typeof o === 'string') { yield { key, value: o }; return; }
+ if (Array.isArray(o)) { for (const v of o) yield* walk(v, key); return; }
+ if (typeof o === 'object') { for (const [k, v] of Object.entries(o)) yield* walk(v, k); }
+ };
+ const forms = new Map(), tpls = new Map();
+ for (const inp of allInputs) {
+ for (const { key, value } of walk(inp, null)) {
+ const kl = (key || '').toLowerCase();
+ const parts = String(value).split('.');
+ const objType = TYPE_SYN[parts[0]] || parts[0];
+ if (parts.length < 3 || !TYPE_TO_DIR[objType]) continue;
+ const objName = parts[1], leaf = parts[parts.length - 1];
+ // Форма — по ключу *Form (нотация ссылки любая: .Form./.Форма./короткая)
+ if (/form$/.test(kl)) {
+ let purpose = 'Object';
+ if (kl.includes('list')) purpose = 'List';
+ else if (kl.includes('choice')) purpose = 'Choice';
+ else if (kl.includes('record')) purpose = 'Record';
+ if (objType === 'Report' || objType === 'DataProcessor') purpose = 'Object';
+ else if (objType === 'DocumentJournal') purpose = 'List'; // журнал — только списочные формы
+ forms.set(`${objType}.${objName}.${leaf}`, { objType, objName, formName: leaf, purpose });
+ }
+ // Макет — по ключу *schema/*template
+ if (kl.includes('datacompositionschema') || kl.includes('template')) {
+ const isSKD = kl.includes('datacompositionschema');
+ tpls.set(`${objType}.${objName}.${leaf}`, { objName, tplName: leaf, tplType: isSKD ? 'DataCompositionSchema' : 'SpreadsheetDocument', main: isSKD });
+ }
+ }
+ }
+ for (const f of forms.values()) {
+ if (!FORM_ADD_TYPES.has(f.objType)) continue; // form-add не умеет этот тип (напр. DocumentJournal)
+ const objPath = join(configDir, TYPE_TO_DIR[f.objType], `${f.objName}.xml`);
+ const formDir = join(configDir, TYPE_TO_DIR[f.objType], f.objName, 'Forms', f.formName);
+ if (!existsSync(objPath) || existsSync(formDir)) continue;
+ try {
+ execSkill(opts.runtime, 'form-add/scripts/form-add', ['-ObjectPath', objPath, '-FormName', f.formName, '-Purpose', f.purpose]);
+ log(`form-add: ${f.objName}.${f.formName}`, true);
+ } catch (e) { log(`form-add: ${f.objName}.${f.formName}`, false, (e.stderr || e.message || '').substring(0, 200)); }
+ }
+ for (const t of tpls.values()) {
+ try {
+ execSkill(opts.runtime, 'template-add/scripts/add-template', ['-ObjectName', t.objName,
+ '-TemplateName', t.tplName, '-TemplateType', t.tplType, '-SrcDir', configDir, ...(t.main ? ['-SetMainSKD'] : [])]);
+ log(`template-add: ${t.objName}.${t.tplName}`, true);
+ } catch (e) { log(`template-add: ${t.objName}.${t.tplName}`, false, (e.stderr || e.message || '').substring(0, 200)); }
+ }
+ }
+
// ── Step 6: Auto-detect and register objects in ChildObjects ──
// Skip when config came from external/fixture setup — it's already complete.
const allObjects = caseProvidedConfig ? [] : scanConfigObjects(configDir);