answer42 0.2.22__tar.gz → 0.2.23__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {answer42-0.2.22 → answer42-0.2.23}/PKG-INFO +1 -1
- {answer42-0.2.22 → answer42-0.2.23}/pyproject.toml +1 -1
- {answer42-0.2.22 → answer42-0.2.23}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form/Module.bsl +129 -78
- {answer42-0.2.22 → answer42-0.2.23}/src/client_cf/Catalogs//320/237/320/241_/320/222/320/273/320/260/320/264/320/265/320/273/320/265/321/206.xml +1 -1
- {answer42-0.2.22 → answer42-0.2.23}/src/client_cf/Catalogs//320/237/320/241_/320/241/320/277/321/200/320/260/320/262/320/276/321/207/320/275/320/270/320/2721.xml +2 -2
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/server.py +54 -5
- {answer42-0.2.22 → answer42-0.2.23}/.gitignore +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/LICENSE +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/README.md +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/credentials.example.json +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/docs/agent-installation.md +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/docs/architecture.md +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/docs/assets/answer42-logo.png +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/docs/assets/platform42-logo.svg +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/docs/installation.md +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/scripts/build_cf.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/scripts/build_pages.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/scripts/e2e_stable.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/scripts/openclaw_mcp_autoreload.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/scripts/rag_cli.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/cf/ConfigDumpInfo.xml +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/cf/Configuration.xml +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form.xml" +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260.xml" +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/cf/DataProcessors/MCPTestManager.xml +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/cf/Ext/ManagedApplicationModule.bsl +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/client_cf/ConfigDumpInfo.xml +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/client_cf/Configuration.xml +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/client_cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Ext/ManagerModule.bsl" +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Ext/ObjectModule.bsl" +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Templates//320/236/321/201/320/275/320/276/320/262/320/275/320/260/321/217/320/241/321/205/320/265/320/274/320/260/320/232/320/276/320/274/320/277/320/276/320/275/320/276/320/262/320/272/320/270/320/224/320/260/320/275/320/275/321/213/321/205/Ext/Template.xml" +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Templates//320/236/321/201/320/275/320/276/320/262/320/275/320/260/321/217/320/241/321/205/320/265/320/274/320/260/320/232/320/276/320/274/320/277/320/276/320/275/320/276/320/262/320/272/320/270/320/224/320/260/320/275/320/275/321/213/321/205.xml" +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262.xml" +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/__init__.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/assets/MCPTestClient.cf +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/assets/MCPTestManager.cf +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/assets/__init__.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/assets/skills/answer42/SKILL.md +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/assets/skills/answer42-rag/SKILL.md +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/bridge.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/credentials.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/os_support.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/platform.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/protocol.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/rag/__init__.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/rag/detect.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/rag/model.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/rag/parsers.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/rag/service.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/rag/store.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/recorder.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/release_helper.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/runtime.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/skill_installer.py +0 -0
- {answer42-0.2.22 → answer42-0.2.23}/src/mcp_1c/window_control.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: answer42
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.23
|
|
4
4
|
Summary: Answer42 — The Answer to Life, Universe, and 1C — UI Driver. MCP-powered 1C:Enterprise UI automation: click, fill, navigate, test, and introspect managed forms through the test-client API
|
|
5
5
|
Author: Marvin (AI Assistant), 42Clouds, and contributors
|
|
6
6
|
Author-email: "Kosolapov Stanislav (proDOOMman)" <prodoomman@gmail.com>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "answer42"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.23"
|
|
4
4
|
description = "Answer42 — The Answer to Life, Universe, and 1C — UI Driver. MCP-powered 1C:Enterprise UI automation: click, fill, navigate, test, and introspect managed forms through the test-client API"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -298,6 +298,8 @@
|
|
|
298
298
|
Возврат ТекстТабличногоДокумента(Параметры);
|
|
299
299
|
ИначеЕсли Метод = "tabular_document_save" Тогда
|
|
300
300
|
Возврат СохранитьТабличныйДокументMCP(Параметры);
|
|
301
|
+
ИначеЕсли Метод = "tabular_document_state_display" Тогда
|
|
302
|
+
Возврат ОтображениеСостоянияТабличногоДокументаMCP(Параметры);
|
|
301
303
|
ИначеЕсли Метод = "set_field_value" Тогда
|
|
302
304
|
Возврат УстановитьЗначениеПоля(Параметры);
|
|
303
305
|
ИначеЕсли Метод = "activate_field" Тогда
|
|
@@ -2938,19 +2940,22 @@
|
|
|
2938
2940
|
Попытка
|
|
2939
2941
|
Подчиненные = БезопасныеПодчиненные(Объект);
|
|
2940
2942
|
Для Каждого Подчиненный Из Подчиненные Цикл
|
|
2941
|
-
|
|
2942
|
-
Если ТипТек = "тестируемоеполетабличногодокументаформы" Тогда
|
|
2943
|
+
Если ЭтоИскомоеПолеТабличногоДокумента(Подчиненный, "", "") Тогда
|
|
2943
2944
|
Элемент = Новый Структура;
|
|
2944
2945
|
Элемент.Вставить("name", БезопасноеСвойство(Подчиненный, "Имя", ""));
|
|
2945
2946
|
Элемент.Вставить("title", БезопасныйЗаголовок(Подчиненный));
|
|
2947
|
+
Элемент.Вставить("type", Строка(ТипЗнч(Подчиненный)));
|
|
2948
|
+
Элемент.Вставить("data", БезопасноеПредставлениеДанныхПоля(Подчиненный));
|
|
2946
2949
|
Если ВключатьТекст Тогда
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
Элемент.Вставить("
|
|
2953
|
-
|
|
2950
|
+
ТекстДокумента = ТекстТабличногоДокументаИзПоля(Подчиненный);
|
|
2951
|
+
Если ТекстДокумента.Свойство("error") Тогда
|
|
2952
|
+
Элемент.Вставить("text_error", ТекстДокумента.error);
|
|
2953
|
+
Иначе
|
|
2954
|
+
Элемент.Вставить("rows", ТекстДокумента.rows);
|
|
2955
|
+
Элемент.Вставить("columns", ТекстДокумента.columns);
|
|
2956
|
+
Элемент.Вставить("address", ТекстДокумента.address);
|
|
2957
|
+
Элемент.Вставить("text", ТекстДокумента.text);
|
|
2958
|
+
КонецЕсли;
|
|
2954
2959
|
КонецЕсли;
|
|
2955
2960
|
Результат.Добавить(Элемент);
|
|
2956
2961
|
КонецЕсли;
|
|
@@ -2987,35 +2992,101 @@
|
|
|
2987
2992
|
Если Поле = Неопределено Тогда
|
|
2988
2993
|
Возврат Новый Структура("found", Ложь);
|
|
2989
2994
|
КонецЕсли;
|
|
2995
|
+
ДанныеТекста = ТекстТабличногоДокументаИзПоля(Поле);
|
|
2996
|
+
Если ДанныеТекста.Свойство("error") Тогда
|
|
2997
|
+
Возврат Новый Структура("found,error", Истина, ДанныеТекста.error);
|
|
2998
|
+
КонецЕсли;
|
|
2999
|
+
Возврат Новый Структура("found,rows,columns,address,text,cells", Истина, ДанныеТекста.rows, ДанныеТекста.columns, ДанныеТекста.address, ДанныеТекста.text, ДанныеТекста.cells);
|
|
3000
|
+
КонецФункции
|
|
3001
|
+
|
|
3002
|
+
&НаКлиенте
|
|
3003
|
+
Функция ТекстТабличногоДокументаИзПоля(Поле)
|
|
2990
3004
|
Попытка
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
КоличествоКолонок = БезопасноеСвойство(Поле, "КоличествоКолонок", 0);
|
|
3005
|
+
КоличествоСтрок = Поле.ПолучитьРазмерОбластиДанныхДокументаПоВертикали();
|
|
3006
|
+
КоличествоКолонок = Поле.ПолучитьРазмерОбластиДанныхДокументаПоГоризонтали();
|
|
2994
3007
|
Если КоличествоСтрок = 0 Или КоличествоКолонок = 0 Тогда
|
|
2995
|
-
Возврат Новый Структура("
|
|
3008
|
+
Возврат Новый Структура("rows,columns,address,text,cells", 0, 0, "", "", Новый Массив);
|
|
2996
3009
|
КонецЕсли;
|
|
3010
|
+
АдресВсейОбласти = АдресОбластиТабличногоДокументаMCP(1, 1, КоличествоСтрок, КоличествоКолонок);
|
|
3011
|
+
ТекстВсейОбласти = Поле.ПолучитьТекстОбласти(АдресВсейОбласти);
|
|
2997
3012
|
МаксСтрок = Мин(КоличествоСтрок, 50);
|
|
2998
3013
|
МаксКолонок = Мин(КоличествоКолонок, 20);
|
|
2999
3014
|
Ячейки = Новый Массив;
|
|
3000
3015
|
Для НомерСтроки = 1 По МаксСтрок Цикл
|
|
3001
|
-
|
|
3016
|
+
СтрокаТекстов = Новый Массив;
|
|
3002
3017
|
Для НомерКолонки = 1 По МаксКолонок Цикл
|
|
3003
3018
|
Попытка
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
Строка.Добавить(?(ТипЗнч(Текст) = Тип("Строка"), Текст, Строка(Текст)));
|
|
3019
|
+
АдресЯчейки = АдресОбластиТабличногоДокументаMCP(НомерСтроки, НомерКолонки, НомерСтроки, НомерКолонки);
|
|
3020
|
+
СтрокаТекстов.Добавить(Поле.ПолучитьТекстОбласти(АдресЯчейки));
|
|
3007
3021
|
Исключение
|
|
3008
|
-
|
|
3022
|
+
СтрокаТекстов.Добавить("");
|
|
3009
3023
|
КонецПопытки;
|
|
3010
3024
|
КонецЦикла;
|
|
3011
|
-
Ячейки.Добавить(
|
|
3025
|
+
Ячейки.Добавить(СтрокаТекстов);
|
|
3012
3026
|
КонецЦикла;
|
|
3013
|
-
|
|
3027
|
+
Возврат Новый Структура("rows,columns,address,text,cells", КоличествоСтрок, КоличествоКолонок, АдресВсейОбласти, ТекстВсейОбласти, Ячейки);
|
|
3028
|
+
Исключение
|
|
3029
|
+
Возврат Новый Структура("error", ОписаниеОшибки());
|
|
3030
|
+
КонецПопытки;
|
|
3031
|
+
КонецФункции
|
|
3032
|
+
|
|
3033
|
+
&НаКлиенте
|
|
3034
|
+
Функция АдресОбластиТабличногоДокументаMCP(НачальнаяСтрока, НачальнаяКолонка, КонечнаяСтрока, КонечнаяКолонка)
|
|
3035
|
+
Начало = "R" + Строка(НачальнаяСтрока) + "C" + Строка(НачальнаяКолонка);
|
|
3036
|
+
Конец = "R" + Строка(КонечнаяСтрока) + "C" + Строка(КонечнаяКолонка);
|
|
3037
|
+
Возврат Начало + ":" + Конец;
|
|
3038
|
+
КонецФункции
|
|
3039
|
+
|
|
3040
|
+
&НаКлиенте
|
|
3041
|
+
Функция ОтображениеСостоянияТабличногоДокументаMCP(Параметры)
|
|
3042
|
+
Если ТестируемоеПриложение = Неопределено Тогда
|
|
3043
|
+
ВызватьИсключение "Тестируемое приложение не подключено.";
|
|
3044
|
+
КонецЕсли;
|
|
3045
|
+
АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
|
|
3046
|
+
Имя = СвойствоСтруктуры(Параметры, "name", "");
|
|
3047
|
+
Заголовок = СвойствоСтруктуры(Параметры, "title", "");
|
|
3048
|
+
Глубина = СвойствоСтруктуры(Параметры, "max_depth", 8);
|
|
3049
|
+
Поле = НайтиТабличныйДокументПоПараметрам(АктивноеОкно, Имя, Заголовок, 0, Глубина);
|
|
3050
|
+
Если Поле = Неопределено Тогда
|
|
3051
|
+
Возврат Новый Структура("found", Ложь);
|
|
3052
|
+
КонецЕсли;
|
|
3053
|
+
Попытка
|
|
3054
|
+
Отображение = Поле.ПолучитьОтображениеСостояния();
|
|
3055
|
+
Если Отображение = Неопределено Тогда
|
|
3056
|
+
Возврат Новый Структура("found,state_display", Истина, Неопределено);
|
|
3057
|
+
КонецЕсли;
|
|
3058
|
+
Состояние = Новый Структура;
|
|
3059
|
+
Состояние.Вставить("display_variant", Строка(БезопасноеСвойство(Отображение, "ВариантОтображения", "")));
|
|
3060
|
+
Состояние.Вставить("visible", БезопасноеСвойство(Отображение, "Видимость", Ложь));
|
|
3061
|
+
Состояние.Вставить("additional_show_mode", Строка(БезопасноеСвойство(Отображение, "ДополнительныйРежимОтображения", "")));
|
|
3062
|
+
Состояние.Вставить("command_name", Строка(БезопасноеСвойство(Отображение, "ИмяКоманды", "")));
|
|
3063
|
+
Состояние.Вставить("picture", Строка(БезопасноеСвойство(Отображение, "Картинка", "")));
|
|
3064
|
+
Состояние.Вставить("text", Строка(БезопасноеСвойство(Отображение, "Текст", "")));
|
|
3065
|
+
Возврат Новый Структура("found,state_display", Истина, Состояние);
|
|
3014
3066
|
Исключение
|
|
3015
3067
|
Возврат Новый Структура("found,error", Истина, ОписаниеОшибки());
|
|
3016
3068
|
КонецПопытки;
|
|
3017
3069
|
КонецФункции
|
|
3018
3070
|
|
|
3071
|
+
&НаКлиенте
|
|
3072
|
+
Функция ФайлСуществуетMCP(Путь)
|
|
3073
|
+
Попытка
|
|
3074
|
+
Файл = Новый Файл(Путь);
|
|
3075
|
+
Возврат Файл.Существует();
|
|
3076
|
+
Исключение
|
|
3077
|
+
Возврат Ложь;
|
|
3078
|
+
КонецПопытки;
|
|
3079
|
+
КонецФункции
|
|
3080
|
+
|
|
3081
|
+
&НаКлиенте
|
|
3082
|
+
Функция БезопасноеПредставлениеДанныхПоля(Поле)
|
|
3083
|
+
Попытка
|
|
3084
|
+
Возврат Строка(Поле.ПолучитьПредставлениеДанных());
|
|
3085
|
+
Исключение
|
|
3086
|
+
Возврат "";
|
|
3087
|
+
КонецПопытки;
|
|
3088
|
+
КонецФункции
|
|
3089
|
+
|
|
3019
3090
|
&НаКлиенте
|
|
3020
3091
|
Функция СохранитьТабличныйДокументMCP(Параметры)
|
|
3021
3092
|
Если ТестируемоеПриложение = Неопределено Тогда
|
|
@@ -3036,62 +3107,34 @@
|
|
|
3036
3107
|
КонецЕсли;
|
|
3037
3108
|
|
|
3038
3109
|
// Для tested form field extension for spreadsheet document direct Записать()
|
|
3039
|
-
// недоступен/ненадежен;
|
|
3040
|
-
//
|
|
3041
|
-
//
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
КонецЦикла;
|
|
3110
|
+
// недоступен/ненадежен; сначала для любого формата перебираем фильтры
|
|
3111
|
+
// штатного файлового диалога, а Python-сторона проверяет сигнатуру/расширение
|
|
3112
|
+
// реально созданного файла нужного формата.
|
|
3113
|
+
ФайлыФильтров = Новый Массив;
|
|
3114
|
+
ОшибкиФильтров = Новый Массив;
|
|
3115
|
+
ВариантыСохранитьКак = Новый Массив;
|
|
3116
|
+
ВариантыСохранитьКак.Добавить(Истина);
|
|
3117
|
+
ВариантыСохранитьКак.Добавить(Ложь);
|
|
3118
|
+
Для Каждого СохранитьКак Из ВариантыСохранитьКак Цикл
|
|
3119
|
+
Для ИндексФильтра = 1 По 30 Цикл
|
|
3120
|
+
ПутьПопытки = ДобавитьСуффиксКПутиMCP(Путь, "_saveas_" + ?(СохранитьКак, "true", "false") + "_filter" + Строка(ИндексФильтра));
|
|
3121
|
+
Попытка
|
|
3122
|
+
ТестируемоеПриложение.УстановитьРезультатДиалогаВыбораФайла(Истина, ПутьПопытки, ИндексФильтра);
|
|
3123
|
+
Поле.ЗаписатьСодержимоеВФайл(СохранитьКак);
|
|
3124
|
+
// Не проверяем существование файла на стороне 1С синхронно: web/ws
|
|
3125
|
+
// клиент может материализовать файл чуть позже. Возвращаем все
|
|
3126
|
+
// успешные попытки, а Python-сторона проверяет факт, сигнатуру и формат.
|
|
3127
|
+
ФайлыФильтров.Добавить(Новый Структура("path,filter_index,save_as", ПутьПопытки, ИндексФильтра, СохранитьКак));
|
|
3128
|
+
Исключение
|
|
3129
|
+
ОшибкиФильтров.Добавить(Новый Структура("filter_index,save_as,error", ИндексФильтра, СохранитьКак, ОписаниеОшибки()));
|
|
3130
|
+
КонецПопытки;
|
|
3061
3131
|
КонецЦикла;
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
Возврат Новый Структура("saved,path,format,method,files,errors", Ложь, Путь, ФорматФайла, "write_content_to_file_dialog_filters", Файлы, Ошибки);
|
|
3132
|
+
КонецЦикла;
|
|
3133
|
+
Если ФайлыФильтров.Количество() > 0 Тогда
|
|
3134
|
+
Возврат Новый Структура("saved,path,format,method,files,errors", Истина, Путь, ФорматФайла, "write_content_to_file_dialog_filters", ФайлыФильтров, ОшибкиФильтров);
|
|
3066
3135
|
КонецЕсли;
|
|
3067
3136
|
|
|
3068
|
-
|
|
3069
|
-
Попытка
|
|
3070
|
-
ТестируемоеПриложение.УстановитьРезультатДиалогаВыбораФайла(Истина, Путь, ИндексФильтраДиалогаТабличногоДокументаMCP(ФорматФайла));
|
|
3071
|
-
Поле.ЗаписатьСодержимоеВФайл(Истина);
|
|
3072
|
-
Возврат Новый Структура("saved,path,format,method,save_as", Истина, Путь, ФорматФайла, "write_content_to_file_dialog", Истина);
|
|
3073
|
-
Исключение
|
|
3074
|
-
ОшибкаЗаписатьСодержимое = ОписаниеОшибки();
|
|
3075
|
-
КонецПопытки;
|
|
3076
|
-
Попытка
|
|
3077
|
-
ТестируемоеПриложение.УстановитьРезультатДиалогаВыбораФайла(Истина, Путь, ИндексФильтраДиалогаТабличногоДокументаMCP(ФорматФайла));
|
|
3078
|
-
Поле.ЗаписатьСодержимоеВФайл(Ложь);
|
|
3079
|
-
Возврат Новый Структура("saved,path,format,method,save_as", Истина, Путь, ФорматФайла, "write_content_to_file_dialog", Ложь);
|
|
3080
|
-
Исключение
|
|
3081
|
-
Если Не ПустаяСтрока(ОшибкаЗаписатьСодержимое) Тогда
|
|
3082
|
-
ОшибкаЗаписатьСодержимое = ОшибкаЗаписатьСодержимое + Символы.ПС + ОписаниеОшибки();
|
|
3083
|
-
Иначе
|
|
3084
|
-
ОшибкаЗаписатьСодержимое = ОписаниеОшибки();
|
|
3085
|
-
КонецЕсли;
|
|
3086
|
-
КонецПопытки;
|
|
3087
|
-
|
|
3088
|
-
Попытка
|
|
3089
|
-
ТипФайла = ТипФайлаТабличногоДокументаMCP(ФорматФайла);
|
|
3090
|
-
Поле.Записать(Путь, ТипФайла);
|
|
3091
|
-
Возврат Новый Структура("saved,path,format,method", Истина, Путь, ФорматФайла, "write_direct");
|
|
3092
|
-
Исключение
|
|
3093
|
-
Возврат Новый Структура("saved,path,format,error,content_error", Ложь, Путь, ФорматФайла, ОписаниеОшибки(), ОшибкаЗаписатьСодержимое);
|
|
3094
|
-
КонецПопытки;
|
|
3137
|
+
Возврат Новый Структура("saved,path,format,method,files,errors,error", Ложь, Путь, ФорматФайла, "write_content_to_file_dialog_filters", ФайлыФильтров, ОшибкиФильтров, "Ни один фильтр файлового диалога не выполнил сохранение табличного документа");
|
|
3095
3138
|
КонецФункции
|
|
3096
3139
|
|
|
3097
3140
|
&НаКлиенте
|
|
@@ -3118,8 +3161,6 @@
|
|
|
3118
3161
|
Возврат ТипФайлаТабличногоДокумента.XLSX;
|
|
3119
3162
|
ИначеЕсли Формат = "xls" Тогда
|
|
3120
3163
|
Возврат ТипФайлаТабличногоДокумента.XLS;
|
|
3121
|
-
ИначеЕсли Формат = "mxl" Тогда
|
|
3122
|
-
Возврат ТипФайлаТабличногоДокумента.MXL;
|
|
3123
3164
|
ИначеЕсли Формат = "html" Или Формат = "htm" Тогда
|
|
3124
3165
|
Возврат ТипФайлаТабличногоДокумента.HTML;
|
|
3125
3166
|
ИначеЕсли Формат = "txt" Тогда
|
|
@@ -3138,8 +3179,6 @@
|
|
|
3138
3179
|
Возврат 2;
|
|
3139
3180
|
ИначеЕсли Формат = "xls" Тогда
|
|
3140
3181
|
Возврат 3;
|
|
3141
|
-
ИначеЕсли Формат = "mxl" Тогда
|
|
3142
|
-
Возврат 4;
|
|
3143
3182
|
ИначеЕсли Формат = "html" Или Формат = "htm" Тогда
|
|
3144
3183
|
Возврат 5;
|
|
3145
3184
|
ИначеЕсли Формат = "txt" Тогда
|
|
@@ -3179,7 +3218,10 @@
|
|
|
3179
3218
|
ТипТек = НормализованноеИмяТипа(Строка(ТипЗнч(Объект)));
|
|
3180
3219
|
ЭтоПолеТабличногоДокумента = ТипТек = "тестируемоеполетабличногодокументаформы";
|
|
3181
3220
|
Если Не ЭтоПолеТабличногоДокумента И ТипТек = "тестируемоеполеформы" Тогда
|
|
3182
|
-
|
|
3221
|
+
// На некоторых отчетах ПолучитьПредставлениеДанных() у поля табличного
|
|
3222
|
+
// документа падает, пока поле недоступно для интерактивных действий.
|
|
3223
|
+
// Поэтому сначала проверяем Вид поля, затем представление данных.
|
|
3224
|
+
ЭтоПолеТабличногоДокумента = ВидПоляТабличныйДокумент(Объект) Или ПредставлениеДанныхПоляТабличныйДокумент(Объект);
|
|
3183
3225
|
КонецЕсли;
|
|
3184
3226
|
Если Не ЭтоПолеТабличногоДокумента Тогда
|
|
3185
3227
|
Возврат Ложь;
|
|
@@ -3189,6 +3231,15 @@
|
|
|
3189
3231
|
Возврат (ПустаяСтрока(Имя) Или ИмяТек = Имя) И (ПустаяСтрока(Заголовок) Или ЗаголовокТек = Заголовок);
|
|
3190
3232
|
КонецФункции
|
|
3191
3233
|
|
|
3234
|
+
&НаКлиенте
|
|
3235
|
+
Функция ВидПоляТабличныйДокумент(Поле)
|
|
3236
|
+
Попытка
|
|
3237
|
+
Возврат НРег(Строка(Поле.Вид)) = "полетабличногодокумента";
|
|
3238
|
+
Исключение
|
|
3239
|
+
Возврат Ложь;
|
|
3240
|
+
КонецПопытки;
|
|
3241
|
+
КонецФункции
|
|
3242
|
+
|
|
3192
3243
|
&НаКлиенте
|
|
3193
3244
|
Функция ПредставлениеДанныхПоляТабличныйДокумент(Поле)
|
|
3194
3245
|
Попытка
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
<Owners/>
|
|
37
37
|
<SubordinationUse>ToItems</SubordinationUse>
|
|
38
38
|
<CodeLength>9</CodeLength>
|
|
39
|
-
<DescriptionLength>
|
|
39
|
+
<DescriptionLength>80</DescriptionLength>
|
|
40
40
|
<CodeType>String</CodeType>
|
|
41
41
|
<CodeAllowedLength>Variable</CodeAllowedLength>
|
|
42
42
|
<CodeSeries>WholeCatalog</CodeSeries>
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
<Owners/>
|
|
37
37
|
<SubordinationUse>ToItems</SubordinationUse>
|
|
38
38
|
<CodeLength>9</CodeLength>
|
|
39
|
-
<DescriptionLength>
|
|
39
|
+
<DescriptionLength>80</DescriptionLength>
|
|
40
40
|
<CodeType>String</CodeType>
|
|
41
41
|
<CodeAllowedLength>Variable</CodeAllowedLength>
|
|
42
42
|
<CodeSeries>WholeCatalog</CodeSeries>
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
<Type>
|
|
91
91
|
<v8:Type>xs:string</v8:Type>
|
|
92
92
|
<v8:StringQualifiers>
|
|
93
|
-
<v8:Length>
|
|
93
|
+
<v8:Length>80</v8:Length>
|
|
94
94
|
<v8:AllowedLength>Variable</v8:AllowedLength>
|
|
95
95
|
</v8:StringQualifiers>
|
|
96
96
|
</Type>
|
|
@@ -3288,7 +3288,7 @@ async def tabular_document_save(
|
|
|
3288
3288
|
name: Annotated[str, "Tabular document field technical name"] = "",
|
|
3289
3289
|
title: Annotated[str, "Tabular document field visible title"] = "",
|
|
3290
3290
|
path: Annotated[str, "Output file path; empty = auto-generated temp path"] = "",
|
|
3291
|
-
format: Annotated[str, "Output format: pdf | xlsx | xls |
|
|
3291
|
+
format: Annotated[str, "Output format: pdf | xlsx | xls | html | txt"] = "pdf",
|
|
3292
3292
|
max_depth: Annotated[int, "Maximum search depth"] = 8,
|
|
3293
3293
|
*,
|
|
3294
3294
|
session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
|
|
@@ -3298,15 +3298,32 @@ async def tabular_document_save(
|
|
|
3298
3298
|
return _select_saved_tabular_document_file(result, format)
|
|
3299
3299
|
|
|
3300
3300
|
|
|
3301
|
+
@mcp.tool()
|
|
3302
|
+
async def tabular_document_state_display(
|
|
3303
|
+
name: Annotated[str, "Tabular document field technical name"] = "",
|
|
3304
|
+
title: Annotated[str, "Tabular document field visible title"] = "",
|
|
3305
|
+
max_depth: Annotated[int, "Maximum search depth"] = 8,
|
|
3306
|
+
*,
|
|
3307
|
+
session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
|
|
3308
|
+
) -> Any:
|
|
3309
|
+
"""Return the state display of a tabular document field, when exposed by 1C."""
|
|
3310
|
+
return await _bridge_call_recorded(session_id, "tabular_document_state_display", {"name": name, "title": title, "max_depth": max_depth})
|
|
3311
|
+
|
|
3312
|
+
|
|
3301
3313
|
def _select_saved_tabular_document_file(result: Any, format: str) -> Any:
|
|
3302
3314
|
if not isinstance(result, dict):
|
|
3303
3315
|
return result
|
|
3304
3316
|
files = result.get("files")
|
|
3305
3317
|
if not isinstance(files, list) or not files:
|
|
3318
|
+
if result.get("found", True) is not False:
|
|
3319
|
+
result["saved"] = False
|
|
3320
|
+
result.setdefault("error", f"No files were produced for requested tabular document format: {format.lower()}")
|
|
3306
3321
|
return result
|
|
3307
3322
|
selected = _find_saved_tabular_document_file(files, format)
|
|
3308
3323
|
if selected is None:
|
|
3309
3324
|
result["selected"] = None
|
|
3325
|
+
result["saved"] = False
|
|
3326
|
+
result["error"] = f"No produced file matches requested tabular document format: {format.lower()}"
|
|
3310
3327
|
return result
|
|
3311
3328
|
selected_path = selected.get("path")
|
|
3312
3329
|
requested_path = result.get("path")
|
|
@@ -3333,22 +3350,54 @@ def _find_saved_tabular_document_file(files: list[Any], format: str) -> dict[str
|
|
|
3333
3350
|
if not p.is_file():
|
|
3334
3351
|
continue
|
|
3335
3352
|
try:
|
|
3336
|
-
|
|
3353
|
+
data = p.read_bytes()
|
|
3354
|
+
header = data[:8]
|
|
3337
3355
|
except OSError:
|
|
3338
3356
|
continue
|
|
3339
3357
|
if wanted == "pdf" and header.startswith(b"%PDF"):
|
|
3340
3358
|
return item
|
|
3341
|
-
if wanted
|
|
3359
|
+
if wanted == "xlsx" and _zip_contains(p, "xl/workbook.xml"):
|
|
3360
|
+
return item
|
|
3361
|
+
if wanted == "docx" and _zip_contains(p, "word/document.xml"):
|
|
3342
3362
|
return item
|
|
3343
3363
|
if wanted == "xls" and header.startswith(bytes.fromhex("d0cf11e0a1b11ae1")):
|
|
3344
3364
|
return item
|
|
3345
|
-
if wanted in {"html", "htm"} and (
|
|
3365
|
+
if wanted in {"html", "htm"} and (data.startswith(b"<") or data.startswith(b"\xef\xbb\xbf<")):
|
|
3346
3366
|
return item
|
|
3347
|
-
if wanted
|
|
3367
|
+
if wanted == "txt" and _looks_like_plain_text(data):
|
|
3348
3368
|
return item
|
|
3349
3369
|
return None
|
|
3350
3370
|
|
|
3351
3371
|
|
|
3372
|
+
def _zip_contains(path: Path, member: str) -> bool:
|
|
3373
|
+
import zipfile
|
|
3374
|
+
|
|
3375
|
+
try:
|
|
3376
|
+
with zipfile.ZipFile(path) as archive:
|
|
3377
|
+
return member in archive.namelist()
|
|
3378
|
+
except Exception:
|
|
3379
|
+
return False
|
|
3380
|
+
|
|
3381
|
+
|
|
3382
|
+
def _looks_like_plain_text(data: bytes) -> bool:
|
|
3383
|
+
if not data:
|
|
3384
|
+
return False
|
|
3385
|
+
sample = data[:4096]
|
|
3386
|
+
if sample.startswith(b"\xef\xbb\xbf"):
|
|
3387
|
+
sample = sample[3:]
|
|
3388
|
+
stripped = sample.lstrip().lower()
|
|
3389
|
+
if stripped.startswith((b"<", b"<!doctype", b"<html")):
|
|
3390
|
+
return False
|
|
3391
|
+
if b"\x00" in sample:
|
|
3392
|
+
return False
|
|
3393
|
+
try:
|
|
3394
|
+
sample.decode("utf-8")
|
|
3395
|
+
return True
|
|
3396
|
+
except UnicodeDecodeError:
|
|
3397
|
+
return False
|
|
3398
|
+
|
|
3399
|
+
|
|
3400
|
+
|
|
3352
3401
|
@mcp.tool()
|
|
3353
3402
|
async def toggle_field(
|
|
3354
3403
|
field_name: Annotated[str, "Field technical name"],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{answer42-0.2.22 → answer42-0.2.23}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|