Процедура СоздатьФайлыУроковПоФичеHTML(ИмяФайлаФичи, ИмяКаталога, Параметры)

	Файл = Новый Файл(Параметры.ResultPath);
	Если НЕ Файл.Существует() Тогда
		ВызватьИсключение "Неверно передан параметр ResultPath: " + Параметры.ResultPath;
	КонецЕсли;

	КаталогРезультата = ОбъединитьПути(Параметры.ResultPath, "FinalHTML");
	ОбеспечитьКаталог(КаталогРезультата);

	КаталогГлавы = ОбъединитьПути(КаталогРезультата, ИмяКаталога);
	ОбеспечитьКаталог(КаталогГлавы);

	ФайлИмяФайлаФичи = Новый Файл(ИмяФайлаФичи);
	НовоеИмяФайлаИнструкции = ОбъединитьПути(КаталогГлавы, ФайлИмяФайлаФичи.ИмяБезРасширения + ".HTML");
	ФайлНовоеИмяФайлаИнструкции = Новый Файл(НовоеИмяФайлаИнструкции);
	Если ФайлНовоеИмяФайлаИнструкции.Существует() Тогда
		Сообщить("Файл уже существует:" + НовоеИмяФайлаИнструкции);
		Возврат;
	КонецЕсли;

	ВременныйКаталогАртефактов = ОбъединитьПути(Параметры.ResultPath, "temp");
	ОбеспечитьКаталог(ВременныйКаталогАртефактов);
	ОчиститьКаталог(ВременныйКаталогАртефактов);

	ПараметрыVA = Новый Структура;
	ПараметрыVA.Вставить("КаталогФич", ИмяФайлаФичи);
	ПараметрыVA.Вставить("ИспользоватьКомпонентуVanessaExt", Истина);
	ПараметрыVA.Вставить("ВыполнитьСценарии", Истина);
	ПараметрыVA.Вставить("ВыгружатьСтатусВыполненияСценариевВФайл", Истина);
	ИмяФайлаСтатуса = ОбъединитьПути(ВременныйКаталогАртефактов, "status.txt");
	УдалитьФайлы(ИмяФайлаСтатуса);
	ПараметрыVA.Вставить("ПутьКФайлуДляВыгрузкиСтатусаВыполненияСценариев", ИмяФайлаСтатуса);

	Для Каждого КлючЗначение Из Параметры Цикл
		ПараметрыVA.Вставить(КлючЗначение.Ключ, КлючЗначение.Значение);
	КонецЦикла;

	ИмяФайлаПараметрыVA = ОбъединитьПути(ВременныйКаталогАртефактов, "VAParams.json");

	ЗаписьJson = Новый ЗаписьJson();
	ЗаписьJson.ОткрытьФайл(ИмяФайлаПараметрыVA);
	ЗаписатьJSON(ЗаписьJson, ПараметрыVA);
	ЗаписьJson.Закрыть();

	СтрокаЗапускаСборки = ПолучитьСтрокуЗапускаСборки(Параметры, ИмяФайлаПараметрыVA);
	//Сообщить(СтрокаЗапускаСборки);
	ЗапуститьСборку(СтрокаЗапускаСборки, Параметры);

	ФайлСтатуса = Новый Файл(ИмяФайлаСтатуса);
	Если НЕ ФайлСтатуса.Существует() Тогда
		ВызватьИсключение "Не найден файл статуса: " + ИмяФайлаСтатуса;
	КонецЕсли;

	ЗначениеСтатуса = ЗначениеСтатуса(ИмяФайлаСтатуса);
	Если ЗначениеСтатуса <> "0" Тогда
		ВызватьИсключение "Получен статус выполнения сценария: " + ЗначениеСтатуса;
	КонецЕсли;

	Файлы = НайтиФайлы(Параметры.КаталогВыгрузкиИнструкцийHTML, "*.HTML", Истина);
	Если Файлы.Количество() = 0 Тогда
		ВызватьИсключение "Не найден файл сформированной инструкции.";
	КонецЕсли;

	Если Файлы.Количество() > 1 Тогда
		ВызватьИсключение "Найдено больше одного HTML файла в каталоге: " + Параметры.КаталогВыгрузкиИнструкцийHTML;
	КонецЕсли;

	ИмяФайлаИнструкции = Файлы[0].ПолноеИмя;
	ФайлИнструкции = Новый Файл(ИмяФайлаИнструкции);
	Если НЕ ФайлИнструкции.Существует() Тогда
		ВызватьИсключение "Не найден файл инструкции: " + ИмяФайлаИнструкции;
	КонецЕсли;

	ФайлИмяФайлаИнструкции = Новый Файл(ИмяФайлаИнструкции);
	КаталогImagesИсточник = ОбъединитьПути(ФайлИмяФайлаИнструкции.Путь,"images");

	ФайлНовоеИмяФайлаИнструкции = Новый Файл(НовоеИмяФайлаИнструкции);
	КаталогImagesПриемник = ОбъединитьПути(ФайлНовоеИмяФайлаИнструкции.Путь,"images");
	ОбеспечитьКаталог(КаталогImagesПриемник);

	УдалитьФайлы(НовоеИмяФайлаИнструкции);
	КопироватьФайл(ИмяФайлаИнструкции, НовоеИмяФайлаИнструкции);

	Файлы = НайтиФайлы(КаталогImagesИсточник, "*", Ложь);
	Для Каждого Файл Из Файлы Цикл
		НовыйПутьККартинке = ОбъединитьПути(КаталогImagesПриемник,Файл.Имя);
		УдалитьФайлы(НовыйПутьККартинке);
		КопироватьФайл(Файл.ПолноеИмя, НовыйПутьККартинке);
	КонецЦикла;

КонецПроцедуры

Процедура СоздатьФайлыУроковПоФичеMD(ИмяФайлаФичи, ИмяКаталога, Параметры)

	Файл = Новый Файл(Параметры.ResultPath);
	Если НЕ Файл.Существует() Тогда
		ВызватьИсключение "Неверно передан параметр ResultPath: " + Параметры.ResultPath;
	КонецЕсли;

	КаталогРезультата = ОбъединитьПути(Параметры.ResultPath, "FinalMD");
	ОбеспечитьКаталог(КаталогРезультата);

	КаталогГлавы = ОбъединитьПути(КаталогРезультата, ИмяКаталога);
	ОбеспечитьКаталог(КаталогГлавы);

	ФайлИмяФайлаФичи = Новый Файл(ИмяФайлаФичи);
	НовоеИмяФайлаИнструкции = ОбъединитьПути(КаталогГлавы, ФайлИмяФайлаФичи.ИмяБезРасширения + ".MD");
	ФайлНовоеИмяФайлаИнструкции = Новый Файл(НовоеИмяФайлаИнструкции);
	Если ФайлНовоеИмяФайлаИнструкции.Существует() Тогда
		Сообщить("Файл уже существует:" + НовоеИмяФайлаИнструкции);
		Возврат;
	КонецЕсли;

	ВременныйКаталогАртефактов = ОбъединитьПути(Параметры.ResultPath, "temp");
	ОбеспечитьКаталог(ВременныйКаталогАртефактов);
	ОчиститьКаталог(ВременныйКаталогАртефактов);

	ПараметрыVA = Новый Структура;
	ПараметрыVA.Вставить("КаталогФич", ИмяФайлаФичи);
	ПараметрыVA.Вставить("ИспользоватьКомпонентуVanessaExt", Истина);
	ПараметрыVA.Вставить("ВыполнитьСценарии", Истина);
	ПараметрыVA.Вставить("ВыгружатьСтатусВыполненияСценариевВФайл", Истина);
	ИмяФайлаСтатуса = ОбъединитьПути(ВременныйКаталогАртефактов, "status.txt");
	УдалитьФайлы(ИмяФайлаСтатуса);
	ПараметрыVA.Вставить("ПутьКФайлуДляВыгрузкиСтатусаВыполненияСценариев", ИмяФайлаСтатуса);

	Для Каждого КлючЗначение Из Параметры Цикл
		ПараметрыVA.Вставить(КлючЗначение.Ключ, КлючЗначение.Значение);
	КонецЦикла;

	ИмяФайлаПараметрыVA = ОбъединитьПути(ВременныйКаталогАртефактов, "VAParams.json");

	ЗаписьJson = Новый ЗаписьJson();
	ЗаписьJson.ОткрытьФайл(ИмяФайлаПараметрыVA);
	ЗаписатьJSON(ЗаписьJson, ПараметрыVA);
	ЗаписьJson.Закрыть();

	СтрокаЗапускаСборки = ПолучитьСтрокуЗапускаСборки(Параметры, ИмяФайлаПараметрыVA);
	//Сообщить(СтрокаЗапускаСборки);
	ЗапуститьСборку(СтрокаЗапускаСборки, Параметры);

	ФайлСтатуса = Новый Файл(ИмяФайлаСтатуса);
	Если НЕ ФайлСтатуса.Существует() Тогда
		ВызватьИсключение "Не найден файл статуса: " + ИмяФайлаСтатуса;
	КонецЕсли;

	ЗначениеСтатуса = ЗначениеСтатуса(ИмяФайлаСтатуса);
	Если ЗначениеСтатуса <> "0" Тогда
		ВызватьИсключение "Получен статус выполнения сценария: " + ЗначениеСтатуса;
	КонецЕсли;

	Файлы = НайтиФайлы(Параметры.КаталогВыгрузкиИнструкцийMarkdown, "*.MD", Истина);
	Если Файлы.Количество() = 0 Тогда
		ВызватьИсключение "Не найден файл сформированной инструкции.";
	КонецЕсли;

	Если Файлы.Количество() > 1 Тогда
		ВызватьИсключение "Найдено больше одного MD файла в каталоге: " + Параметры.КаталогВыгрузкиИнструкцийMarkdown;
	КонецЕсли;

	ИмяФайлаИнструкции = Файлы[0].ПолноеИмя;
	ФайлИнструкции = Новый Файл(ИмяФайлаИнструкции);
	Если НЕ ФайлИнструкции.Существует() Тогда
		ВызватьИсключение "Не найден файл инструкции: " + ИмяФайлаИнструкции;
	КонецЕсли;

	ФайлИмяФайлаИнструкции = Новый Файл(ИмяФайлаИнструкции);
	КаталогImagesИсточник = ОбъединитьПути(ФайлИмяФайлаИнструкции.Путь,"images");

	ФайлНовоеИмяФайлаИнструкции = Новый Файл(НовоеИмяФайлаИнструкции);
	КаталогImagesПриемник = ОбъединитьПути(ФайлНовоеИмяФайлаИнструкции.Путь,"images");
	ОбеспечитьКаталог(КаталогImagesПриемник);

	УдалитьФайлы(НовоеИмяФайлаИнструкции);
	КопироватьФайл(ИмяФайлаИнструкции, НовоеИмяФайлаИнструкции);

	Файлы = НайтиФайлы(КаталогImagesИсточник, "*", Ложь);
	Для Каждого Файл Из Файлы Цикл
		НовыйПутьККартинке = ОбъединитьПути(КаталогImagesПриемник,Файл.Имя);
		УдалитьФайлы(НовыйПутьККартинке);
		КопироватьФайл(Файл.ПолноеИмя, НовыйПутьККартинке);
	КонецЦикла;

КонецПроцедуры


Функция СформироватьСодержимоеВкладкиРасширенногоВызова(Знач ИмяМодуля, Знач ИмяМетода)

	НастройкиМетода = НайтиНастройкиРасширенногоВызоваМетода(ИмяМодуля, ИмяМетода);

	Если НастройкиМетода = Неопределено Тогда

		ТекстПусто = ?(ТекущийЯзык = "ru"
			, "*Для текущего метода нет дополнительных параметров расширенного вызова.*"
			, "*This method has no additional advanced call parameters.*");

		Возврат ТекстПусто + Символы.ПС;

	КонецЕсли;

	Возврат СформироватьТаблицуДопПараметровРасширенногоВызова(НастройкиМетода);

КонецФункции

#КонецОбласти

#КонецОбласти


Function GetRecordsFilterStructure(Val Clear = False) Export

    FilterStructure = New Structure;

    FilterStructure.Insert("field", "<filtering field name>");
    FilterStructure.Insert("type" , "<comparison type>");
    FilterStructure.Insert("value", "<comparison value>");
    FilterStructure.Insert("union", "<connection with the following condition: AND, OR, etc..>");
    FilterStructure.Insert("raw"  , "<true - the value will be inserted by text as it is, false - through the parameter>");

    If Clear Then
        FilterStructure = OPI_Tools.ClearCollectionRecursively(FilterStructure);
    EndIf;

    //@skip-check constructor-function-return-section
    Return FilterStructure;

EndFunction

Function ExecuteQueryWithProcessing(Connector, Val QueryText, Val ForceResult, Val Parameters) Export

    QueryKey = InitializeQuery(Connector, QueryText, ForceResult);

    If Not QueryKey["result"] Then
        Return QueryKey;
    Else
        QueryKey = QueryKey["key"];
    EndIf;

    Adding = SetQueryParams(Connector, QueryKey, Parameters);

    If Not Adding["result"] Then
        Return Adding;
    EndIf;

    Result = Connector.Execute(QueryKey);
    Result = ProcessQueryResult(Connector, QueryKey, Result);

    Return Result;

EndFunction

Function ProcessParameters(Val AddIn, Val Parameters, Val TypesStructure) Export

    If Not ValueIsFilled(Parameters) Then
        Return New Array;
    EndIf;

    OPI_TypeConversion.GetArray(Parameters);

    For N = 0 To Parameters.UBound() Do

        CurrentParameter = Parameters[N];

        CurrentParameter = ProcessParameter(AddIn, CurrentParameter, TypesStructure);

        Parameters[N] = CurrentParameter;

    EndDo;

    Return Parameters;

EndFunction

#EndRegion

#Region Private

#Region Scheme

Function NewSQLScheme(Val Action, Val Module = Undefined)

    OPI_TypeConversion.GetLine(Action);

    Action = Upper(Action);

    If Action = "SELECT" Then

        Scheme = EmptySchemeSelect();

    ElsIf Action = "INSERT" Then

        Scheme = EmptySchemeInsert();

    ElsIf Action = "UPDATE" Then

        Scheme = EmptySchemeUpdate();

    ElsIf Action = "MERGE" Then

        Scheme = EmptySchemeMerge();

    ElsIf Action = "DELETE" Then

        Scheme = EmptySchemeDelete();

    ElsIf Action = "CREATE" Then

        Scheme = EmptySchemeCreate();

    ElsIf Action = "DROP" Then

        Scheme = EmptySchemeDrop();

    ElsIf Action = "TRUNCATE" Then

        Scheme = EmptySchemeTruncate();

    ElsIf Action = "CREATEDATABASE" Then

        Scheme = EmptySchemeCreateDatabase();

    ElsIf Action = "DROPDATABASE" Then

        Scheme = EmptySchemeDropDatabase();

    ElsIf Action = "TABLESCHEMA" Then

        Scheme = EmptySchemeTableSchema();

    ElsIf Action = "ALTERTABLEADD" Then

        Scheme = EmptySchemeAlterTableAdd();

    ElsIf Action = "ALTERTABLEDROP" Then

        Scheme = EmptySchemeAlterTableDrop();

    Else

        Scheme = New Structure;

    EndIf;

    Features = Module.GetFeatures();

    ReplaceDefaultFeatures(Features);

    Scheme.Insert("nump" , Features["ParameterNumeration"]);
    Scheme.Insert("markp", Features["ParameterMarker"]);
    Scheme.Insert("dbms" , Features["DBMS"]);

    Return Scheme;

EndFunction

Function EmptySchemeSelect()

    Scheme = New Structure("type", "SELECT");

    Scheme.Insert("table"  , "");
    Scheme.Insert("filter" , New Array);
    Scheme.Insert("order"  , New Array);
    Scheme.Insert("limit"  , 0);
    Scheme.Insert("set"    , New Array);
    Scheme.Insert("values" , New Array);

    Return Scheme;

EndFunction

Function EmptySchemeInsert()

    Scheme = New Structure("type", "INSERT");

    Scheme.Insert("table", "");
    Scheme.Insert("set"  , New Array);

    Return Scheme;

EndFunction

Function EmptySchemeUpdate()

    Scheme = New Structure("type", "UPDATE");

    Scheme.Insert("table"  , "");
    Scheme.Insert("set"    , New Array);
    Scheme.Insert("filter" , New Array);
    Scheme.Insert("values" , New Array);

    Return Scheme;

EndFunction

Function EmptySchemeMerge()

    Scheme = New Structure("type", "MERGE");

    Scheme.Insert("table" , "");
    Scheme.Insert("set"   , New Array);
    Scheme.Insert("keys"  , New Array);
    Scheme.Insert("values", New Array);

    Return Scheme;

EndFunction

Function EmptySchemeDelete()

    Scheme = New Structure("type", "DELETE");

    Scheme.Insert("table" , "");
    Scheme.Insert("filter", New Array);
    Scheme.Insert("values", New Array);

    Return Scheme;

EndFunction

Function EmptySchemeCreate()

    Scheme = New Structure("type", "CREATE");

    Scheme.Insert("table"  , "");
    Scheme.Insert("columns", New Array);

    Return Scheme;

EndFunction

Function EmptySchemeDrop()

    Scheme = New Structure("type", "DROP");

    Scheme.Insert("table" , "");

    Return Scheme;

EndFunction

Function EmptySchemeTruncate()

    Scheme = New Structure("type", "TRUNCATE");

    Scheme.Insert("table" , "");

    Return Scheme;

EndFunction

Function EmptySchemeCreateDatabase()

    Scheme = New Structure("type", "CREATEDATABASE");

    Scheme.Insert("database" , "");

    Return Scheme;

EndFunction

Function EmptySchemeDropDatabase()

    Scheme = New Structure("type", "DROPDATABASE");

    Scheme.Insert("database" , "");

    Return Scheme;

EndFunction

Function EmptySchemeTableSchema()

    Scheme = New Structure("type", "TABLESCHEMA");

    Scheme.Insert("table" , "");
    Scheme.Insert("common", True);

    Return Scheme;

EndFunction

Function EmptySchemeAlterTableAdd()

    Scheme = New Structure("type", "ALTERTABLEADD");

    Scheme.Insert("table", "");
    Scheme.Insert("name" , "");
    Scheme.Insert("dtype", "");

    Return Scheme;

EndFunction

Function EmptySchemeAlterTableDrop()

    Scheme = New Structure("type", "ALTERTABLEDROP");

    Scheme.Insert("table", "");
    Scheme.Insert("name" , "");

    Return Scheme;

EndFunction

#EndRegion

#Region Processors

Function FormSQLText(Val Scheme)

    ErrorText = "The value passed is not a valid SQL query schema";
    OPI_TypeConversion.GetKeyValueCollection(Scheme, ErrorText);

    SchemeType = "";

    If Not OPI_Tools.CollectionFieldExists(Scheme, "type", SchemeType) Then
        Raise ErrorText;
    EndIf;

    SchemeType = Upper(SchemeType);

    If SchemeType = "SELECT" Then

        QueryText = FormTextSelect(Scheme);

    ElsIf SchemeType = "INSERT" Then

        QueryText = FormTextInsert(Scheme);

    ElsIf SchemeType = "UPDATE" Then

        QueryText = FormTextUpdate(Scheme);

    ElsIf SchemeType = "MERGE" Then

        QueryText = FormTextMerge(Scheme);

    ElsIf SchemeType = "DELETE" Then

        QueryText = FormTextDelete(Scheme);

    ElsIf SchemeType = "CREATE" Then

        QueryText = FormTextCreate(Scheme);

    ElsIf SchemeType = "DROP" Then

        QueryText = FormTextDrop(Scheme);

    ElsIf SchemeType = "TRUNCATE" Then

        QueryText = FormTextTruncate(Scheme);

    ElsIf SchemeType = "CREATEDATABASE" Then

        QueryText = FormTextCreateDatabase(Scheme);

    ElsIf SchemeType = "DROPDATABASE" Then

        QueryText = FormTextDropDatabase(Scheme);

    ElsIf SchemeType = "TABLESCHEMA" Then

        QueryText = FormTextTableSchema(Scheme);

    ElsIf SchemeType = "ALTERTABLEADD" Then

        QueryText = FormTextAlterTableAdd(Scheme);

    ElsIf SchemeType = "ALTERTABLEDROP" Then

        QueryText = FormTextAlterTableDrop(Scheme);

    Else

        QueryText = "";

    EndIf;

    Return QueryText;

EndFunction


Function FormTextAlterTableDrop(Val Scheme)

    Table = Scheme["table"];
    Name  = Scheme["name"];
    DBMS  = Scheme["dbms"];

    SQLTemplate   = "ALTER TABLE %1 DROP %2 %3";
    Clarification = ?(DBMS = "mssql", "COLUMN", "");

    TextSQL = StrTemplate(SQLTemplate, Table, Clarification, Name);

    Return TextSQL;

EndFunction

#EndRegion

#Region Auxiliary

Function CreateConnection(Val Module, Val Connection, Val Tls)

    If Tls     = Undefined Then
        Result = Module.CreateConnection(Connection);
    Else
        Result = Module.CreateConnection(Connection, Tls);
    EndIf;

    Return Result;

EndFunction

Function ExecuteSQLQuery(Val Module
    , Val QueryText
    , Val Parameters  = ""
    , Val ForceResult = False
    , Val Connection  = ""
    , Val Tls         = Undefined)

    If ValueIsFilled(Tls) Then
        Result = Module.ExecuteSQLQuery(QueryText, Parameters, ForceResult, Connection, Tls);
    Else
        Result = Module.ExecuteSQLQuery(QueryText, Parameters, ForceResult, Connection);
    EndIf;

    Return Result;

EndFunction

Function ProcessRecords(Val Module, Val Table, Val DataArray, Val KeyFields, Val Transaction, Val Connection)

    If OPI_AddIns.FileTransferRequired() Then
        Result = ProcessRecordsBatch(Module, Table, DataArray, KeyFields, Transaction, Connection);
    Else
        Result = ProcessRecordsSeparately(Module, Table, DataArray, KeyFields, Transaction, Connection);
    EndIf;

    Return Result;

EndFunction

Function ProcessRecordsSeparately(Val Module
    , Val Table
    , Val DataArray
    , Val KeyFields
    , Val Transaction
    , Val Connection)

    ErrorsArray  = New Array;
    Counter      = 0;
    SuccessCount = 0;

    For Each Record In DataArray Do

        Counter = Counter + 1;

        If Not CheckRecordCorrectness(Record, ErrorsArray, Counter) Then
            If Transaction Then
                Break;
            Else
                Continue;
            EndIf;
        EndIf;

        Result = ProcessRecord(Module, Table, Record, KeyFields, Connection);

        Success = CheckSingleQueryExecution(Result, Transaction, Counter, SuccessCount, ErrorsArray);

        If Not Success Then
            Break;
        EndIf;

    EndDo;

    Result = New Structure("ErrorsArray,SuccessCount", ErrorsArray, SuccessCount);

    Return Result;

EndFunction

Function ProcessRecordsBatch(Val Module
    , Val Table
    , Val DataArray
    , Val KeyFields
    , Val Transaction
    , Val Connection)

    BlanksArray = New Array;
    ErrorsArray = New Array;

    // BSLLS:MissingTemporaryFileDeletion-off

    //@skip-check missing-temporary-file-deletion
    BlanksPath = GetTempFileName();
    //@skip-check missing-temporary-file-deletion
    KeysPath = GetTempFileName();

    // BSLLS:MissingTemporaryFileDeletion-on

    Counter      = 0;
    SuccessCount = 0;

    For Each Record In DataArray Do

        Counter = Counter + 1;

        If Not CheckRecordCorrectness(Record, ErrorsArray, Counter) Then
            If Transaction Then
                Break;
            Else
                Continue;
            EndIf;
        EndIf;

        Result = ProcessRecord(Module, Table, Record, KeyFields, Connection, False);
        BlanksArray.Add(Result);

    EndDo;

    Try

        OPI_Tools.WriteJSONFile(BlanksPath, BlanksArray);

        Initialization = Connection.BatchQuery(BlanksPath, KeysPath);
        Initialization = OPI_Tools.JsonToStructure(Initialization);

        OPI_Tools.RemoveFileWithTry(BlanksPath, "Failed to delete query package file after installation");

        If Not Initialization["result"] Then
            Raise Initialization["error"];
        EndIf;

        Keys = OPI_Tools.ReadJSONFile(KeysPath, True);

        OPI_Tools.RemoveFileWithTry(KeysPath, "Failed to delete key file after initialization");

    Except

        Error = StrTemplate("Batch query error: %1", ErrorDescription());
        ErrorsArray.Add(New Structure("row,error", -1, Error));

        Return New Structure("ErrorsArray,SuccessCount", ErrorsArray, 0);

    EndTry;

    For Each QueryKey In Keys Do

        Result = Connection.Execute(QueryKey);
        Result = ProcessQueryResult(Connection, QueryKey, Result);

        Success = CheckSingleQueryExecution(Result, Transaction, Counter, SuccessCount, ErrorsArray);

        If Not Success Then
            Break;
        EndIf;

    EndDo;

    Result = New Structure("ErrorsArray,SuccessCount", ErrorsArray, SuccessCount);

    Return Result;

EndFunction

Function CheckSingleQueryExecution(Val Result
    , Val Transaction
    , Val Counter
    , SuccessCount
    , ErrorsArray)

    If Result["result"] Then
            SuccessCount = SuccessCount + 1;
        Else

            ErrorsArray.Add(New Structure("row,error", Counter, Result["error"]));

            If Transaction Then
                Return False;
            EndIf;

    EndIf;

    Return True;

EndFunction

Function CheckRecordCorrectness(Record, ErrorsArray, Val Counter)

    CollectionError = "Invalid data";
    Correct         = True;

    Try
        OPI_TypeConversion.GetKeyValueCollection(Record, CollectionError);
    Except
        ErrorsArray.Add(New Structure("row,error", Counter, CollectionError));
        Correct = False;
    EndTry;

    Return Correct;

EndFunction

Function ProcessRecord(Val Module
    , Val Table
    , Val Record
    , Val KeyFields
    , Val Connection
    , Val ExecuteNow = True)

    FieldArray  = New Array;
    ValuesArray = New Array;

    If KeyFields = Undefined Then
        Scheme   = NewSQLScheme("INSERT", Module);
    Else

        Scheme = NewSQLScheme("MERGE", Module);

        OPI_TypeConversion.GetArray(KeyFields);
        Scheme["keys"] = KeyFields;

    EndIf;

    SetTableName(Scheme, Table);
    SplitDataCollection(Record, FieldArray, ValuesArray);

    For Each Field In FieldArray Do
        AddField(Scheme, Field);
    EndDo;

    Request = FormSQLText(Scheme);

    If ExecuteNow Then
        Result = Module.ExecuteSQLQuery(Request, ValuesArray, , Connection);
    Else

        Parameters = ProcessParameters(Connection
            , ValuesArray
            , Module.GetTypesStructure());

        Result = New Map;
        Result.Insert("text"        , Request);
        Result.Insert("params"      , Parameters);
        Result.Insert("force_result", False);

    EndIf;

    Return Result;

EndFunction

Function ProcessRecordsStart(Val Module, Val Transaction, Val Connection)

    If Not OPI_AddIns.IsAddIn(Connection) Then
        Return Connection;
    EndIf;

    If Transaction Then

        Text  = Module.GetFeatures()["TransactionStart"];
        Start = Module.ExecuteSQLQuery(Text, , , Connection);

        If Not Start["result"] Then
            Return Start;
        EndIf;

    EndIf;

EndFunction

Function ProcessRecordsEnd(Val ProcessedStructure, Val Module, Val Transaction, Val Connection)

    ResultStructure = New Structure;

    ErrorsArray  = ProcessedStructure["ErrorsArray"];
    SuccessCount = ProcessedStructure["SuccessCount"];

    If Transaction Then

        If ErrorsArray.Count() > 0 Then

            Rollback = Module.ExecuteSQLQuery("ROLLBACK;", , , Connection);

            SuccessCount = 0;
            ResultStructure.Insert("rollback", Rollback);

        Else

            Completion = Module.ExecuteSQLQuery("COMMIT;", , , Connection);
            ResultStructure.Insert("commit", Completion);

        EndIf;

    EndIf;

    ResultStructure.Insert("result", ErrorsArray.Count() = 0);
    ResultStructure.Insert("rows"  , SuccessCount);
    ResultStructure.Insert("errors", ErrorsArray);

    Return ResultStructure;

EndFunction


Function UseEncoding(Val Encoding) Export

    Try

        If StopExecution() Then Return ThisObject; EndIf;

        AddLog("UseEncoding: setting the value");
        OPI_TypeConversion.GetLine(Encoding);

        SetSetting("EncodeRequestBody", Encoding);

        Return ThisObject;

    Except
        Return Error(DetailErrorDescription(ErrorInfo()));
    EndTry;

EndFunction

// Use Gzip compression !NOCLI
// Enables or disables the header for receiving data from the server in compressed form
//
// Note:
// By default, the response from the server is requested with gzip compression
//
// Parameters:
// Flag - Boolean - Flag for gzip using - gzip
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object

Function UseGzipCompression(Val Flag) Export

    Try

        If StopExecution() Then Return ThisObject; EndIf;

        AddLog("UseGzipCompression: setting the value");
        OPI_TypeConversion.GetBoolean(Flag);

        SetSetting("gzip", Flag);

        Return ThisObject;

    Except
        Return Error(DetailErrorDescription(ErrorInfo()));
    EndTry;

EndFunction

// Use body fields at OAuth !NOCLI
// Includes or excludes body fields when calculating the OAuth signature depending on server requirements
//
// Note:
// By default, the body data is used in the signature calculation if it was set using the `SetFormBody` function
//
// Parameters:
// Flag - Boolean - Flag to use body fields in OAuth signature calculation - use
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object

Function UseBodyFiledsAtOAuth(Val Flag) Export

    Try

        If StopExecution() Then Return ThisObject; EndIf;

        AddLog("UseBodyFiledsAtOAuth: setting the value");
        OPI_TypeConversion.GetBoolean(Flag);

        SetSetting("BodyFieldsAtOAuth", Flag);

        Return ThisObject;

    Except
        Return Error(DetailErrorDescription(ErrorInfo()));
    EndTry;

EndFunction

// Use URL encoding !NOCLI
// Enables or disables standard encoding of special characters in URLs
//
// Note:
// URL encoding is enabled by default
//
// Parameters:
// Flag - Boolean - Flag to use URL encoding - enc
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object

Function UseURLEncoding(Val Flag) Export

    Try

        If StopExecution() Then Return ThisObject; EndIf;

        AddLog("UseURLEncoding: setting the value");
        OPI_TypeConversion.GetBoolean(Flag);

        SetSetting("URLencoding", Flag);

        Return ThisObject;

    Except
        Return Error(DetailErrorDescription(ErrorInfo()));
    EndTry;

EndFunction

// Max attempts !NOCLI
// Sets the maximum number of retry attempts for sending a request in case of 5** codes or client internal errors
//
// Note:
// By default: `0`
//
// Parameters:
// Value - Number - Retry count - val
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object

Function MaxAttempts(Val Value) Export

    Try

        If StopExecution() Then Return ThisObject; EndIf;

        AddLog("MaxAttempts: setting the value");

        OPI_TypeConversion.GetNumber(Value);
        SetSetting("MaxAttempts", Value);

        Return ThisObject;

    Except
        Return Error(DetailErrorDescription(ErrorInfo()));
    EndTry;

EndFunction

// Max redirects !NOCLI
// Sets the maximum number of allowed redirects
//
// Note:
// By default: `5`
//
// Parameters:
// Value - Number - Redirect count - val
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object

Function MaxRedirects(Val Value) Export

    Try

        If StopExecution() Then Return ThisObject; EndIf;

        AddLog("MaxRedirects: setting the value");

        OPI_TypeConversion.GetNumber(Value);
        SetSetting("MaxRedirects", Value);

        Return ThisObject;

    Except
        Return Error(DetailErrorDescription(ErrorInfo()));
    EndTry;

EndFunction

// Split arrays in URL
// Defines the representation of arrays in URL parameters: as a whole JSON array or separate parameters for each element
//
// Note:
// By default, arrays are interpreted as a single parameter with JSON array in value
// By default, square brackets to parameter keys are not set when array splitting is performed
//
// Parameters:
// Flag           - Boolean - Flag for dividing the array into individual URL parameters        - split
// SquareBrackets - Boolean - Add PHP style empty brackets to keys (key[]=value) if Flag = True - php
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object

Function SplitArraysInURL(Val Flag, Val SquareBrackets = Undefined) Export

    Try

        If StopExecution() Then Return ThisObject; EndIf;

        AddLog("SplitArraysInURL: setting the value");
        OPI_TypeConversion.GetBoolean(Flag);

        If SquareBrackets <> Undefined Then

            AddLog("SplitArraysInURL: square brackets option setting");
            OPI_TypeConversion.GetBoolean(SquareBrackets);

            SetSetting("ArraysSquareBrackets", SquareBrackets);

        EndIf;

        SetSetting("SplitArrayParams", Flag);

        Return ThisObject;

    Except
        Return Error(DetailErrorDescription(ErrorInfo()));
    EndTry;

EndFunction

// Return settings
//
// Parameters:
// Filter - String, Array of String - Filter by setting name or names for retrieval - filter
//
// Returns:
// Arbitrary - Settings structure, specific setting value, or handler object (on error)

Function ReturnSettings(Val Filter = Undefined) Export

    Try

        If StopExecution() Then Return ThisObject; EndIf;

        AddLog("ReturnSettings: getting the value");

        CurrentSettings = OPI_Tools.CopyCollection(Settings);

        If Filter <> Undefined Then

            OPI_TypeConversion.GetArray(Filter);

            If Filter.Count() = 1 Then

                CurrentSettings = OPI_Tools.GetOr(CurrentSettings, String(Filter[0]), Undefined);

            Else

                FilterList = New ValueList();
                FilterList.LoadValues(Filter);

                CurrentSettings_ = New Structure;

                For Each Setting In CurrentSettings Do

                    If FilterList.FindByValue(Setting.Key) <> Undefined Then
                        CurrentSettings_.Insert(Setting.Key, Setting.Value);
                    EndIf;

                EndDo;

                CurrentSettings = CurrentSettings_;

            EndIf;

        EndIf;

        Return CurrentSettings;

    Except
        Return Error(DetailErrorDescription(ErrorInfo()));
    EndTry;

EndFunction

#EndRegion

#Region BodySet

// Set binary body !NOCLI
// Sets the request body from binary data, file or string (with conversion to binary data)
//
// Parameters:
// Data       - String, BinaryData - File, string, or request body data           - data
// SetIfEmpty - Boolean            - Sets the body even when empty data is passed - empty
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object


Функция ПолучитьКомментарийЗадачи(Знач URL, Знач IDЗадачи, Знач IDКомментария, Знач Токен = "") Экспорт

    Параметры = НормализоватьАвторизацию(URL, Токен, "task.commentitem.get");
    OPI_Инструменты.ДобавитьПоле("TASKID", IDЗадачи     , "Строка", Параметры);
    OPI_Инструменты.ДобавитьПоле("ITEMID", IDКомментария, "Строка", Параметры);

    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Добавить комментарий задачи
// Создает комментарий к задаче
//
// Примечание:
// Метод в документации API: [task.commentitem.add](@dev.1c-bitrix.ru/rest_help/tasks/task/commentitem/add.php)
//
// Параметры:
//  URL      - Строка        - URL внешнего вебхука или адрес Bitrix24 при использовании токена - url
//  IDЗадачи - Число, Строка - ID задачи                                                        - task
//  Текст    - Строка        - Текст комментария                                                - text
//  Токен    - Строка        - Токен авторизации, если используется не вебхук                   - token
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Bitrix24

Функция ДобавитьКомментарийЗадачи(Знач URL, Знач IDЗадачи, Знач Текст, Знач Токен = "") Экспорт

    Параметры = НормализоватьАвторизацию(URL, Токен, "task.commentitem.add");

    Комментарий = Новый Структура;
    OPI_Инструменты.ДобавитьПоле("POST_MESSAGE", Текст, "Строка", Комментарий);

    OPI_Инструменты.ДобавитьПоле("TASKID", IDЗадачи   , "Строка"   , Параметры);
    OPI_Инструменты.ДобавитьПоле("FIELDS", Комментарий, "Коллекция", Параметры);

    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Изменить комментарий задачи
// Изменяет текст комментария задачи
//
// Примечание:
// Метод в документации API: [task.commentitem.update](@dev.1c-bitrix.ru/rest_help/tasks/task/commentitem/update.php)
//
// Параметры:
//  URL           - Строка        - URL внешнего вебхука или адрес Bitrix24 при использовании токена - url
//  IDЗадачи      - Число, Строка - ID задачи                                                        - task
//  IDКомментария - Число, Строка - ID комментария                                                   - comment
//  Текст         - Строка        - Текст комментария                                                - text
//  Токен         - Строка        - Токен авторизации, если используется не вебхук                   - token
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Bitrix24

Функция ИзменитьКомментарийЗадачи(Знач URL, Знач IDЗадачи, Знач IDКомментария, Знач Текст, Знач Токен = "") Экспорт

    Строка_ = "Строка";

    Параметры = НормализоватьАвторизацию(URL, Токен, "task.commentitem.update");

    Комментарий = Новый Структура;
    OPI_Инструменты.ДобавитьПоле("POST_MESSAGE", Текст, "Строка", Комментарий);

    OPI_Инструменты.ДобавитьПоле("TASKID", IDЗадачи     , Строка_    , Параметры);
    OPI_Инструменты.ДобавитьПоле("ITEMID", IDКомментария, Строка_    , Параметры);
    OPI_Инструменты.ДобавитьПоле("FIELDS", Комментарий  , "Коллекция", Параметры);

    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Удалить комментарий
// Удаляет существующий комментарий задачи
//
// Примечание:
// Метод в документации API: [task.commentitem.delete](@dev.1c-bitrix.ru/rest_help/tasks/task/commentitem/delete.php)
//
// Параметры:
//  URL           - Строка        - URL внешнего вебхука или адрес Bitrix24 при использовании токена - url
//  IDЗадачи      - Число, Строка - ID задачи                                                        - task
//  IDКомментария - Число, Строка - ID удаляемого комментария                                        - comment
//  Токен         - Строка        - Токен авторизации, если используется не вебхук                   - token
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Bitrix24

Функция УдалитьКомментарийЗадачи(Знач URL, Знач IDЗадачи, Знач IDКомментария, Знач Токен = "") Экспорт

    Параметры = НормализоватьАвторизацию(URL, Токен, "task.commentitem.delete");

    OPI_Инструменты.ДобавитьПоле("TASKID", IDЗадачи     , "Строка", Параметры);
    OPI_Инструменты.ДобавитьПоле("ITEMID", IDКомментария, "Строка", Параметры);

    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Получить список результатов
// Получает список результатов задачи
//
// Примечание:
// Метод в документации API: [tasks.task.result.list](@dev.1c-bitrix.ru/rest_help/tasks/task/tasks/tasks_task_result.list.php)
//
// Параметры:
//  URL      - Строка        - URL внешнего вебхука или адрес Bitrix24 при использовании токена - url
//  IDЗадачи - Число, Строка - ID задачи                                                        - task
//  Токен    - Строка        - Токен авторизации, если используется не вебхук                   - token
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Bitrix24

Функция ПолучитьСписокРезультатов(Знач URL, Знач IDЗадачи, Знач Токен = "") Экспорт

    Ответ = ОсновноеУправляющееДействие(URL, IDЗадачи, "tasks.task.result.list", Токен, "taskId");
    Возврат Ответ;

КонецФункции

// Создать результат из комментария
// Создает результат задачи из комментария
//
// Примечание:
// Метод в документации API: [tasks.task.result.addFromComment](@dev.1c-bitrix.ru/rest_help/tasks/task/tasks/tasks_task_result_addFromComment.php)
//
// Параметры:
//  URL           - Строка        - URL внешнего вебхука или адрес Bitrix24 при использовании токена - url
//  IDКомментария - Число, Строка - ID комментария                                                   - comment
//  Токен         - Строка        - Токен авторизации, если используется не вебхук                   - token
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Bitrix24

Функция СоздатьРезультатИзКомментария(Знач URL, Знач IDКомментария, Знач Токен = "") Экспорт

    Параметры = НормализоватьАвторизацию(URL, Токен, "tasks.task.result.addFromComment");
    OPI_Инструменты.ДобавитьПоле("commentId", IDКомментария, "Строка", Параметры);

    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Удалить результат из комментария
// Удаляет ранее созданный результат из комментария
//
// Примечание:
// Метод в документации API: [tasks.task.result.deleteFromComment](@dev.1c-bitrix.ru/rest_help/tasks/task/tasks/tasks_task_result_deleteFromComment.php)
//
// Параметры:
//  URL           - Строка        - URL внешнего вебхука или адрес Bitrix24 при использовании токена - url
//  IDКомментария - Число, Строка - ID комментария                                                   - comment
//  Токен         - Строка        - Токен авторизации, если используется не вебхук                   - token
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Bitrix24

Функция УдалитьРезультатИзКомментария(Знач URL, Знач IDКомментария, Знач Токен = "") Экспорт

    Параметры = НормализоватьАвторизацию(URL, Токен, "tasks.task.result.deleteFromComment");
    OPI_Инструменты.ДобавитьПоле("commentId", IDКомментария, "Строка", Параметры);

    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Получить структуру комментария
// Получает структуру полей комментария
//
// Параметры:
//  Пустая - Булево - Истина > структура с пустыми значениями, Ложь > в значениях будут описания полей - empty
//
// Возвращаемое значение:
//  Структура Из КлючИЗначение - Структура полей


Функция УдалитьУчастникаГруппы(Знач ПараметрыДоступа, Знач IDЧата, Знач IDУчастника) Экспорт

    Параметры = Новый Структура;
    OPI_Инструменты.ДобавитьПоле("chatId"           , IDЧата     , "Строка", Параметры);
    OPI_Инструменты.ДобавитьПоле("participantChatId", IDУчастника, "Строка", Параметры);

    URL   = СформироватьОсновнойURL(ПараметрыДоступа, "removeGroupParticipant");
    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Назначить права администратора
// Назначает права администратора указанному участнику группового чата
//
// Примечание:
// Метод в документации API: [SetGroupAdmin](@green-api.com/v3/docs/api/groups/SetGroupAdmin/)
//
// Параметры:
//  ПараметрыДоступа - Структура Из КлючИЗначение - Параметры доступа. См. СформироватьПараметрыДоступа - access
//  IDЧата           - Строка                     - ID группового чата                                  - chat
//  IDУчастника      - Строка                     - ID участника                                        - member
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Green API

Функция НазначитьПраваАдминистратора(Знач ПараметрыДоступа, Знач IDЧата, Знач IDУчастника) Экспорт

    Параметры = Новый Структура;
    OPI_Инструменты.ДобавитьПоле("chatId"           , IDЧата     , "Строка", Параметры);
    OPI_Инструменты.ДобавитьПоле("participantChatId", IDУчастника, "Строка", Параметры);

    URL   = СформироватьОсновнойURL(ПараметрыДоступа, "setGroupAdmin");
    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Отозвать права администратора
// Отзывает права администратора у указанного участника группового чата
//
// Примечание:
// Метод в документации API: [RemoveAdmin](@green-api.com/v3/docs/api/groups/RemoveAdmin/)
//
// Параметры:
//  ПараметрыДоступа - Структура Из КлючИЗначение - Параметры доступа. См. СформироватьПараметрыДоступа - access
//  IDЧата           - Строка                     - ID группового чата                                  - chat
//  IDУчастника      - Строка                     - ID участника                                        - member
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Green API

Функция ОтозватьПраваАдминистратора(Знач ПараметрыДоступа, Знач IDЧата, Знач IDУчастника) Экспорт

    Параметры = Новый Структура;
    OPI_Инструменты.ДобавитьПоле("chatId"           , IDЧата     , "Строка", Параметры);
    OPI_Инструменты.ДобавитьПоле("participantChatId", IDУчастника, "Строка", Параметры);

    URL   = СформироватьОсновнойURL(ПараметрыДоступа, "removeAdmin");
    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Выйти из группы
// Исключает текущий аккаунт из указанной группы
//
// Примечание:
// Метод в документации API: [LeaveGroup](@green-api.com/v3/docs/api/groups/LeaveGroup/)
//
// Параметры:
//  ПараметрыДоступа - Структура Из КлючИЗначение - Параметры доступа. См. СформироватьПараметрыДоступа - access
//  IDЧата           - Строка                     - ID группового чата                                  - chat
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Green API

Функция ВыйтиИзГруппы(Знач ПараметрыДоступа, Знач IDЧата) Экспорт

    Параметры = Новый Структура;
    OPI_Инструменты.ДобавитьПоле("chatId", IDЧата, "Строка", Параметры);

    URL   = СформироватьОсновнойURL(ПараметрыДоступа, "leaveGroup");
    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Получить структуру настроек группы
// Возвращает структуру полей настроек группового чата
//
// Параметры:
//  Пустая          - Булево - Истина > структура с пустыми значениями, Ложь > в значениях будут описания полей - empty
//  КакСоответствие - Булево - Истина > возвращает поля настроек как соответствие                               - map
//
// Возвращаемое значение:
//  Структура Из КлючИЗначение - Структура полей

Функция ПолучитьСтруктуруНастроекГруппы(Знач Пустая = Ложь, Знач КакСоответствие = Ложь) Экспорт

    OPI_ПреобразованиеТипов.ПолучитьБулево(Пустая);
    OPI_ПреобразованиеТипов.ПолучитьБулево(КакСоответствие);

    Если КакСоответствие Тогда
        Настройки = Новый Соответствие;
    Иначе
        Настройки = Новый Структура;
    КонецЕсли;

    Настройки.Вставить("allowParticipantsEditGroupSettings"  , "<разрешить участникам изменять настройки группы>");
    Настройки.Вставить("allowParticipantsPinMessages"        , "<разрешить участникам закреплять сообщения>");
    Настройки.Вставить("allowParticipantsAddMembers"         , "<разрешить участникам добавлять новых участников>");

    Если Пустая Тогда
        Настройки = OPI_Инструменты.ОчиститьКоллекциюРекурсивно(Настройки);
    КонецЕсли;

    //@skip-check constructor-function-return-section
    Возврат Настройки;

КонецФункции

#КонецОбласти

#Область ОтправкаСообщений

// Отправить текстовое сообщение
// Отправляет текстовое сообщение в выбранный чат
//
// Примечание:
// Метод в документации API: [SendMessage](@green-api.com/v3/docs/api/sending/SendMessage/)
//
// Параметры:
//  ПараметрыДоступа - Структура Из КлючИЗначение - Параметры доступа. См. СформироватьПараметрыДоступа         - access
//  IDЧата           - Строка                     - Идентификатор чата                                          - chat
//  Текст            - Строка                     - Текст сообщения                                             - text
//  ВремяНабора      - Число                      - Время показа признака набора текста перед отправкой (в мс.) - typing
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Green API

Функция ОтправитьТекстовоеСообщение(Знач ПараметрыДоступа, Знач IDЧата, Знач Текст, Знач ВремяНабора = 0) Экспорт

    Строка_   = "Строка";
    Параметры = Новый Структура;

    OPI_Инструменты.ДобавитьПоле("chatId"    , IDЧата     , Строка_, Параметры);
    OPI_Инструменты.ДобавитьПоле("message"   , Текст      , Строка_, Параметры);
    OPI_Инструменты.ДобавитьПоле("typingTime", ВремяНабора, "Число", Параметры);

    URL   = СформироватьОсновнойURL(ПараметрыДоступа, "sendMessage");
    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);

    Возврат Ответ;

КонецФункции

// Отправить файл
// Отправляет файл в выбранный чат
//
// Примечание:
// Метод в документации API: [SendFileByUpload](@green-api.com/v3/docs/api/sending/SendFileByUpload/)
//
// Параметры:
//  ПараметрыДоступа - Структура Из КлючИЗначение - Параметры доступа. См. СформироватьПараметрыДоступа  - access
//  IDЧата           - Строка                     - Идентификатор чата                                   - chat
//  Файл             - Строка, ДвоичныеДанные     - Данные или путь к файлу                              - file
//  ИмяФайла         - Строка                     - Имя загружаемого файла с расширением                 - filename
//  Описание         - Строка                     - Текст сообщения под файлом                           - caption
//  ВремяНабора      - Число                      - Время показа признака набора перед отправкой (в мс.) - typing
//  ТипНабора        - Строка                     - Тип набора: text, recording, video, image, file      - ttype
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Green API


Функция ПолучитьВерсию(Знач URL, Знач ДопЗаголовки = "") Экспорт

    ДополнитьURL(URL, "api/version");

    ОбработатьЗаголовки(ДопЗаголовки);

    Ответ = OPI_ЗапросыHTTP.Get(URL, , ДопЗаголовки);

    Возврат Ответ;

КонецФункции

// Получить ответ
// Генерирует ответ по заданному текстовому запросу
//
// Примечание:
// Метод в документации API: [Generate a completion](@github.com/ollama/ollama/blob/main/docs/api.md#generate-a-completion)
//
// Параметры:
//  URL          - Строка                        - URL сервера Ollama                                     - url
//  Модель       - Строка                        - Имя модели                                             - model
//  Вопрос       - Строка                        - Текст запроса                                          - prompt
//  ДопПараметры - Структура Из КлючИЗначение    - Доп. параметры. См. ПолучитьСтруктуруПараметровЗапроса - options
//  ДопЗаголовки - Соответствие Из КлючИЗначение - Доп заголовки запроса, если необходимо                 - headers
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - Результат обработки

Функция ПолучитьОтвет(Знач URL, Знач Модель, Знач Вопрос, Знач ДопПараметры = "", Знач ДопЗаголовки = "") Экспорт

    ДополнитьURL(URL, "api/generate");

    Параметры = Новый Структура;
    Параметры.Вставить("suffix", "");

    OPI_Инструменты.ДобавитьПоле("model" , Модель, "Строка", Параметры);
    OPI_Инструменты.ДобавитьПоле("prompt", Вопрос, "Строка", Параметры);
    OPI_Инструменты.ДобавитьПоле("stream", Ложь  , "Булево", Параметры);

    ОбработатьПараметры(Параметры, ДопПараметры);
    ОбработатьЗаголовки(ДопЗаголовки);

    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры, ДопЗаголовки);

    Возврат Ответ;

КонецФункции

// Получить ответ в контексте
// Получает очередной ответ от модели в соответствии с историей сообщений
//
// Примечание:
// Метод в документации API: [Generate a chat completion](@github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion)
//
// Параметры:
//  URL          - Строка                        - URL сервера Ollama                                         - url
//  Модель       - Строка                        - Имя модели                                                 - model
//  Сообщения    - Массив Из Структура           - История сообщений. См. ПолучитьСтруктуруСообщенияКонтекста - msgs
//  ДопПараметры - Структура Из КлючИЗначение    - Доп. параметры. См. ПолучитьСтруктуруПараметровЗапроса     - options
//  ДопЗаголовки - Соответствие Из КлючИЗначение - Доп заголовки запроса, если необходимо                     - headers
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - Результат обработки

Функция ПолучитьОтветВКонтексте(Знач URL, Знач Модель, Знач Сообщения, Знач ДопПараметры = "", Знач ДопЗаголовки = "") Экспорт

    ДополнитьURL(URL, "api/chat");

    Параметры = Новый Структура;

    OPI_Инструменты.ДобавитьПоле("model"   , Модель   , "Строка"   , Параметры);
    OPI_Инструменты.ДобавитьПоле("messages", Сообщения, "Коллекция", Параметры);
    OPI_Инструменты.ДобавитьПоле("stream"  , Ложь     , "Булево"   , Параметры);

    ОбработатьПараметры(Параметры, ДопПараметры);
    ОбработатьЗаголовки(ДопЗаголовки);

    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры, ДопЗаголовки);

    Возврат Ответ;

КонецФункции

// Получить представления
// Получает представления (embeddings) для заданных вводных
//
// Примечание:
// Метод в документации API: [Generate Embeddings](@github.com/ollama/ollama/blob/main/docs/api.md#generate-embeddings)
//
// Параметры:
//  URL          - Строка                        - URL сервера Ollama                                           - url
//  Модель       - Строка                        - Имя модели                                                   - model
//  Вопрос       - Массив Из Строка              - Строка или массив строк запросов                             - input
//  ДопПараметры - Структура Из КлючИЗначение    - Доп. параметры. См. ПолучитьСтруктуруПараметровПредставлений - options
//  ДопЗаголовки - Соответствие Из КлючИЗначение - Доп заголовки запроса, если необходимо                       - headers
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - Результат обработки

Функция ПолучитьПредставления(Знач URL, Знач Модель, Знач Вопрос, Знач ДопПараметры = "", Знач ДопЗаголовки = "") Экспорт

    ДополнитьURL(URL, "api/embed");

    Параметры = Новый Структура;

    OPI_Инструменты.ДобавитьПоле("model", Модель, "Строка", Параметры);
    OPI_Инструменты.ДобавитьПоле("input", Вопрос, "Строка", Параметры);

    ОбработатьПараметры(Параметры, ДопПараметры);
    ОбработатьЗаголовки(ДопЗаголовки);

    Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры, ДопЗаголовки);

    Возврат Ответ;

КонецФункции

// Получить структуру параметров запроса
// Получает структуру дополнительных параметров для обработки запроса
//
// Параметры:
//  Пустая - Булево - Истина > структура с пустыми значениями, Ложь > в значениях будут описания полей - empty
//
// Возвращаемое значение:
//  Структура Из КлючИЗначение - Структура полей

Функция ПолучитьСтруктуруПараметровЗапроса(Знач Пустая = Ложь) Экспорт

    OPI_ПреобразованиеТипов.ПолучитьБулево(Пустая);

    СтруктураПолей = Новый Структура;
    СтруктураПолей.Вставить("format"    , "<формат, в котором возвращается ответ: json или схема JSON>");
    СтруктураПолей.Вставить("options"   , "<дополнительные параметры модели, перечисленные в документации к Modelfile>");
    СтруктураПолей.Вставить("system"    , "<системное сообщение (переопределяет то, что определено в Modelfile)>");
    СтруктураПолей.Вставить("template"  , "<шаблон промпта (переопределяет то, что определено в Modelfile)>");
    СтруктураПолей.Вставить("raw"       , "<истина > отключить форматирование промпта>");
    СтруктураПолей.Вставить("keep_alive", "<как долго модель будет оставаться загруженной в память после запроса>");
    СтруктураПолей.Вставить("suffix"    , "<текст после ответа модели>");
    СтруктураПолей.Вставить("images"    , "<список картинок в формате Base64 (для многомодальных моделей, вроде llava)>");

    Если Пустая Тогда
        СтруктураПолей = OPI_Инструменты.ОчиститьКоллекциюРекурсивно(СтруктураПолей);
    КонецЕсли;

    //@skip-check constructor-function-return-section
    Возврат СтруктураПолей;

КонецФункции

// Получить структуру параметров представлений
// Получает структуру дополнительных параметров для обработки запросов получения представлений
//
// Параметры:
//  Пустая - Булево - Истина > структура с пустыми значениями, Ложь > в значениях будут описания полей - empty
//
// Возвращаемое значение:
//  Структура Из КлючИЗначение - Структура полей

Функция ПолучитьСтруктуруПараметровПредставлений(Знач Пустая = Ложь) Экспорт

    OPI_ПреобразованиеТипов.ПолучитьБулево(Пустая);

    СтруктураПолей = Новый Структура;
    СтруктураПолей.Вставить("options"   , "<дополнительные параметры модели, перечисленные в документации к Modelfile>");
    СтруктураПолей.Вставить("keep_alive", "<как долго модель будет оставаться загруженной в память после запроса>");
    СтруктураПолей.Вставить("truncate"  , "<обрезает конец каждого ответа, чтобы уложиться в длину контекста. Возвращает ошибку, если false и длина превышена>");

    Если Пустая Тогда
        СтруктураПолей = OPI_Инструменты.ОчиститьКоллекциюРекурсивно(СтруктураПолей);
    КонецЕсли;

    //@skip-check constructor-function-return-section
    Возврат СтруктураПолей;

КонецФункции

// Получить структуру параметров в контексте
// Получает структуру дополнительных параметров для обработки запроса в контексте
//
// Параметры:
//  Пустая - Булево - Истина > структура с пустыми значениями, Ложь > в значениях будут описания полей - empty
//
// Возвращаемое значение:
//  Структура Из КлючИЗначение - Структура полей


Функция ПолучитьПодборкиПоИД(Знач Подборки, Знач Параметры = "") Экспорт

    OPI_ПреобразованиеТипов.ПолучитьКоллекцию(Подборки);

    Параметры_     = ПолучитьСтандартныеПараметры(Параметры);
    Подборки_      = Новый Массив;
    СтрокаПодборок = "";

    Для Каждого Подборка Из Подборки Цикл
        Подборки_.Добавить(OPI_Инструменты.ЧислоВСтроку(Подборка));
    КонецЦикла;

    СтрокаПодборок = СтрСоединить(Подборки_, ",");

    Параметры_.Вставить("album_ids", СтрокаПодборок);

    Ответ = OPI_ЗапросыHTTP.Get("api.vk.com/method/market.getAlbumById", Параметры_);

    Возврат Ответ;

КонецФункции

// Создать подборку товаров
// Создает пустую подборку товаров
//
// Параметры:
//  Название  - Строка                 - Название подборки                                       - title
//  Картинка  - Строка, ДвоичныеДанные - Файл картинки                                           - picture
//  Основная  - Булево                 - Основная                                                - main
//  Скрытая   - Булево                 - Скрытая                                                 - hidden
//  Параметры - Структура из Строка    - Параметры авторизации. См. ПолучитьПараметрыАвторизации - auth
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от VK

Функция СоздатьПодборкуТоваров(Знач Название
    , Знач Картинка
    , Знач Основная = Ложь
    , Знач Скрытая = Ложь
    , Знач Параметры = "") Экспорт

    Возврат УправлениеПодборкой(Название, Картинка, , Основная, Скрытая, Параметры);

КонецФункции

// Изменить подборку товаров
// Изменяет свойства подборки товаров
//
// Параметры:
//  Название  - Строка                 - Новое название подборки                                 - title
//  Подборка  - Строка                 - ID подборки                                             - sel
//  Картинка  - Строка, ДвоичныеДанные - Новая картинка подборки                                 - picture
//  Основная  - Булево                 - Основная                                                - main
//  Скрытая   - Булево                 - Скрытая                                                 - hidden
//  Параметры - Структура из Строка    - Параметры авторизации. См. ПолучитьПараметрыАвторизации - auth
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от VK

Функция ИзменитьПодборкуТоваров(Знач Название
    , Знач Подборка
    , Знач Картинка = ""
    , Знач Основная = Ложь
    , Знач Скрытая = Ложь
    , Знач Параметры = "") Экспорт

    Возврат УправлениеПодборкой(Название, Картинка, Подборка, Основная, Скрытая, Параметры);

КонецФункции

// Добавить товар в подборку
// Добавляет товар в подборку
//
// Параметры:
//  МассивТоваров - Массив из Строка, Число - Массив товаров или товар                                - items
//  Подборка      - Строка                  - ID подборки                                             - sel
//  Параметры     - Структура из Строка     - Параметры авторизации. См. ПолучитьПараметрыАвторизации - auth
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от VK

Функция ДобавитьТоварВПодборку(Знач МассивТоваров, Знач Подборка, Знач Параметры = "") Экспорт

    OPI_ПреобразованиеТипов.ПолучитьКоллекцию(МассивТоваров);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(Подборка);

    Параметры_     = ПолучитьСтандартныеПараметры(Параметры);
    МассивТоваров_ = Новый Массив;

    Для Каждого Товар Из МассивТоваров Цикл
        МассивТоваров_.Добавить(OPI_Инструменты.ЧислоВСтроку(Товар));
    КонецЦикла;

    СписокТоваров = СтрСоединить(МассивТоваров_, ",");

    Параметры_.Вставить("item_ids" , СписокТоваров);
    Параметры_.Вставить("album_ids", Подборка);

    Ответ = OPI_ЗапросыHTTP.Get("api.vk.com/method/market.addToAlbum", Параметры_);

    Возврат Ответ;

КонецФункции

// Удалить товар из подборки
// Удаляет ранее добавленный товар из подборки
//
// Параметры:
//  Товар     - Строка              - ID товара                                               - item
//  Подборка  - Строка              - ID подборки                                             - sel
//  Параметры - Структура из Строка - Параметры авторизации. См. ПолучитьПараметрыАвторизации - auth
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от VK

Функция УдалитьТоварИзПодборки(Знач Товар, Знач Подборка, Знач Параметры = "") Экспорт

    OPI_ПреобразованиеТипов.ПолучитьСтроку(Товар);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(Подборка);

    Параметры_ = ПолучитьСтандартныеПараметры(Параметры);

    Параметры_.Вставить("item_id"  , Товар);
    Параметры_.Вставить("album_ids", Подборка);

    Ответ = OPI_ЗапросыHTTP.Get("api.vk.com/method/market.removeFromAlbum", Параметры_);

    Возврат Ответ;

КонецФункции

// Удалить подборку
// Удаляет подборку по ID
//
// Параметры:
//  Подборка  - Строка              - ID подборки                                             - sel
//  Параметры - Структура из Строка - Параметры авторизации. См. ПолучитьПараметрыАвторизации - auth
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от VK

Функция УдалитьПодборку(Знач Подборка, Знач Параметры = "") Экспорт

    OPI_ПреобразованиеТипов.ПолучитьСтроку(Подборка);

    Параметры_ = ПолучитьСтандартныеПараметры(Параметры);
    Параметры_.Вставить("album_id", Подборка);

    Ответ = OPI_ЗапросыHTTP.Get("api.vk.com/method/market.deleteAlbum", Параметры_);

    Возврат Ответ;

КонецФункции

#КонецОбласти

#Область РаботаСоСвойствамиТоваров

// Получить список свойств
// Получает список свойств товаров группы
//
// Параметры:
//  Параметры - Структура из Строка - Параметры авторизации. См. ПолучитьПараметрыАвторизации - auth
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от VK

Функция ПолучитьСписокСвойств(Знач Параметры = "") Экспорт

    Response_  = "response";
    Параметры_ = ПолучитьСтандартныеПараметры(Параметры);

    Ответ    = OPI_ЗапросыHTTP.Get("api.vk.com/method/market.getProperties", Параметры_);
    Свойства = Ответ[Response_]["items"];

    Возврат Свойства;

КонецФункции

// Создать свойство товара
// Создает новое свойство для использования в товарах
//
// Параметры:
//  Название  - Строка              - Название свойства                                       - title
//  Параметры - Структура из Строка - Параметры авторизации. См. ПолучитьПараметрыАвторизации - auth
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от VK

Функция СоздатьСвойствоТовара(Знач Название, Знач Параметры = "") Экспорт

    OPI_ПреобразованиеТипов.ПолучитьСтроку(Название);

    Параметры_ = ПолучитьСтандартныеПараметры(Параметры);
    Параметры_.Вставить("title", Название);

    Ответ = OPI_ЗапросыHTTP.Get("api.vk.com/method/market.addProperty", Параметры_);

    Возврат Ответ;

КонецФункции

// Изменить свойство товара
// Изменяет существующее свойство товара
//
// Параметры:
//  Название  - Строка              - Новое название                                          - title
//  Свойство  - Строка, Число       - ID свойства                                             - prop
//  Параметры - Структура из Строка - Параметры авторизации. См. ПолучитьПараметрыАвторизации - auth
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от VK


Функция ПолучитьСсылкуЗагрузкиФайла(Знач Токен, Знач Путь, Знач Перезаписывать = Ложь) Экспорт

    OPI_ПреобразованиеТипов.ПолучитьСтроку(Путь);
    OPI_ПреобразованиеТипов.ПолучитьБулево(Перезаписывать);

    Заголовки = OPI_YandexID.ПолучитьЗаголовокАвторизации(Токен);

    Параметры = Новый Структура;
    Параметры.Вставить("path"       , Путь);
    Параметры.Вставить("overwrite"  , Перезаписывать);

    Ответ = OPI_ЗапросыHTTP.Get("https://cloud-api.yandex.net/v1/disk/resources/upload", Параметры, Заголовки);

    Возврат Ответ;

КонецФункции

// Скачать файл
// Скачивает файл по указанному пути
//
// Параметры:
//  Токен          - Строка - Токен                       - token
//  Путь           - Строка - Путь к файлу для скачивания - path
//  ПутьСохранения - Строка - Путь сохранения файла       - out
//
// Возвращаемое значение:
//  ДвоичныеДанные,Строка - Двоичные данные или путь к файлу при указании параметра ПутьСохранения

Функция СкачатьФайл(Знач Токен, Знач Путь, Знач ПутьСохранения = "") Экспорт

    OPI_ПреобразованиеТипов.ПолучитьСтроку(ПутьСохранения);

    Ответ = ПолучитьСсылкуДляСкачивания(Токен, Путь);
    URL   = Ответ["href"];

    Если Не ЗначениеЗаполнено(URL) Тогда
        Возврат Ответ;
    КонецЕсли;

    Ответ = OPI_ЗапросыHTTP.Get(URL, , , ПутьСохранения);

    Возврат Ответ;

КонецФункции

// Получить список файлов
// Получает список файлов с или без отбора по типу
// Список доступных типов: audio, backup, book, compressed, data, development,
//                         diskimage, document, encoded, executable, flash, font,
//                         mage, settings, spreadsheet, text, unknown, video, web
//
// Параметры:
//  Токен             - Строка        - Токен                                               - token
//  Количество        - Число, Строка - Количество возвращаемых объектов                    - amount
//  СмещениеОтНачала  - Число         - Смещение для получение объектов не из начала списка - offset
//  ОтборПоТипу       - Строка        - Отбор по типу файла                                 - type
//  СортироватьПоДате - Булево        - Истина > сортировать по дате, Ложь > по алфавиту    - datesort
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Yandex

Функция ПолучитьСписокФайлов(Знач Токен
    , Знач Количество = 0
    , Знач СмещениеОтНачала = 0
    , Знач ОтборПоТипу = ""
    , Знач СортироватьПоДате = Ложь) Экспорт

    OPI_ПреобразованиеТипов.ПолучитьСтроку(Количество);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(СмещениеОтНачала);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(ОтборПоТипу);
    OPI_ПреобразованиеТипов.ПолучитьБулево(СортироватьПоДате);

    Заголовки = OPI_YandexID.ПолучитьЗаголовокАвторизации(Токен);

    Параметры = Новый Структура;

    Если ЗначениеЗаполнено(Количество) Тогда
        Параметры.Вставить("limit", OPI_Инструменты.ЧислоВСтроку(Количество));
    КонецЕсли;

    Если ЗначениеЗаполнено(СмещениеОтНачала) Тогда
        Параметры.Вставить("offset", OPI_Инструменты.ЧислоВСтроку(СмещениеОтНачала));
    КонецЕсли;

    Если ЗначениеЗаполнено(ОтборПоТипу) Тогда
        Параметры.Вставить("media_type", ОтборПоТипу);
    КонецЕсли;

    Если СортироватьПоДате Тогда
        Назначение = "last-uploaded";
    Иначе
        Назначение = "files";
    КонецЕсли;

    Ответ = OPI_ЗапросыHTTP.Get("https://cloud-api.yandex.net/v1/disk/resources/" + Назначение, Параметры, Заголовки);

    Возврат Ответ;

КонецФункции

// Переместить объект
// Перемещает объект по заданному пути и пути к оригиналу
//
// Параметры:
//  Токен          - Строка - Токен                                                  - token
//  Оригинал       - Строка - Путь к оригинальному файлу или папке                   - from
//  Путь           - Строка - Путь назначение для перемещения                        - to
//  Перезаписывать - Булево - Перезаписывать если файл с таким именем уже существует - rewrite
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Yandex

Функция ПереместитьОбъект(Знач Токен, Знач Оригинал, Знач Путь, Знач Перезаписывать = Ложь) Экспорт

    OPI_ПреобразованиеТипов.ПолучитьСтроку(Оригинал);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(Путь);
    OPI_ПреобразованиеТипов.ПолучитьБулево(Перезаписывать);

    Заголовки = OPI_YandexID.ПолучитьЗаголовокАвторизации(Токен);
    URL       = "https://cloud-api.yandex.net/v1/disk/resources/move";
    Href      = "href";

    Параметры = Новый Структура;
    Параметры.Вставить("from"       , Оригинал);
    Параметры.Вставить("path"       , Путь);
    Параметры.Вставить("overwrite"  , Перезаписывать);

    Параметры = OPI_Инструменты.ПараметрыЗапросаВСтроку(Параметры);
    Ответ     = OPI_ЗапросыHTTP.PostСТелом(URL + Параметры, , Заголовки, Ложь);
    URLОтвета = Ответ[Href];

    Если Не ЗначениеЗаполнено(URLОтвета) Тогда
        Возврат Ответ;
    КонецЕсли;

    Ответ = OPI_ЗапросыHTTP.Get(URLОтвета, , Заголовки);

    Возврат Ответ;

КонецФункции

// Загрузить файл
// Загружает файл на диск по заданному пути
//
// Параметры:
//  Токен          - Строка                 - Токен                                                   - token
//  Путь           - Строка                 - Путь для сохранение файла на Диске                      - path
//  Файл           - Строка, ДвоичныеДанные - Файл для загрузки                                       - file
//  Перезаписывать - Булево                 - Перезаписывать, если файл с таким именем уже существует - rewrite
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Yandex

Функция ЗагрузитьФайл(Знач Токен, Знач Путь, Знач Файл, Знач Перезаписывать = Ложь) Экспорт

    Заголовки = OPI_YandexID.ПолучитьЗаголовокАвторизации(Токен);

    OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(Файл);
    Файл = Новый Структура("file", Файл);

    Ответ = ПолучитьСсылкуЗагрузкиФайла(Токен, Путь, Перезаписывать);
    URL   = Ответ.Получить("href");

    Если Не ЗначениеЗаполнено(URL) Тогда
        Возврат Ответ;
    КонецЕсли;

    Ответ = OPI_ЗапросыHTTP.PutMultipart(URL, Новый Структура(), Файл, "multipart", Заголовки);

    Возврат Ответ;

КонецФункции

// Загрузить файл частями
// Загружает файл на диск по заданному пути частями
//
// Параметры:
//  Токен          - Строка                 - Токен                                                   - token
//  Путь           - Строка                 - Путь для сохранение файла на Диске                      - path
//  Файл           - Строка, ДвоичныеДанные - Файл для загрузки                                       - file
//  РазмерЧасти    - Число                  - Размер одной части при загрузке                         - psize
//  Перезаписывать - Булево                 - Перезаписывать, если файл с таким именем уже существует - rewrite
//
// Возвращаемое значение:
//  Соответствие Из КлючИЗначение - сериализованный JSON ответа от Yandex


Функция СоздатьБазуДанных(Знач Модуль, Знач База, Знач Соединение = "", Знач Tls = Неопределено) Экспорт

    Схема = ПустаяСхемаSQL("CREATEDATABASE", Модуль);

    УстановитьИмяБазы(Схема, База);

    Запрос    = СформироватьТекстSQL(Схема);
    Результат = ВыполнитьЗапросSQL(Модуль, Запрос, , , Соединение, Tls);

    Возврат Результат;

КонецФункции

Функция УдалитьБазуДанных(Знач Модуль, Знач База, Знач Соединение = "", Знач Tls = Неопределено) Экспорт

    Схема = ПустаяСхемаSQL("DROPDATABASE", Модуль);

    УстановитьИмяБазы(Схема, База);

    Запрос    = СформироватьТекстSQL(Схема);
    Результат = ВыполнитьЗапросSQL(Модуль, Запрос, , , Соединение, Tls);

    Возврат Результат;

КонецФункции

Функция СоздатьТаблицу(Знач Модуль
    , Знач Таблица
    , Знач СтруктураКолонок
    , Знач Соединение = ""
    , Знач Tls        = Неопределено) Экспорт

    ТекстОшибки = "Структура колонок не является валидной структурой ключ-значение";
    OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(СтруктураКолонок, ТекстОшибки);

    Схема = ПустаяСхемаSQL("CREATE", Модуль);

    УстановитьИмяТаблицы(Схема, Таблица);

    Для Каждого Колонка Из СтруктураКолонок Цикл
        ДобавитьКолонку(Схема, Колонка.Ключ, Колонка.Значение);
    КонецЦикла;

    Запрос    = СформироватьТекстSQL(Схема);
    Результат = ВыполнитьЗапросSQL(Модуль, Запрос, , , Соединение, Tls);

    Возврат Результат;

КонецФункции

Функция ДобавитьКолонкуТаблицы(Знач Модуль
    , Знач Таблица
    , Знач Имя
    , Знач ТипДанных
    , Знач Соединение = ""
    , Знач Tls        = Неопределено) Экспорт

    Схема = ПустаяСхемаSQL("ALTERTABLEADD", Модуль);

    УстановитьИмяТаблицы(Схема, Таблица);

    УстановитьПроизвольноеПоле(Схема, "name" , Имя      , "Строка");
    УстановитьПроизвольноеПоле(Схема, "dtype", ТипДанных, "Строка");

    Запрос    = СформироватьТекстSQL(Схема);
    Результат = ВыполнитьЗапросSQL(Модуль, Запрос, , , Соединение, Tls);

    Возврат Результат;

КонецФункции

Функция УдалитьКолонкуТаблицы(Знач Модуль
    , Знач Таблица
    , Знач Имя
    , Знач Соединение = ""
    , Знач Tls        = Неопределено) Экспорт

    Схема = ПустаяСхемаSQL("ALTERTABLEDROP", Модуль);

    УстановитьИмяТаблицы(Схема, Таблица);
    УстановитьПроизвольноеПоле(Схема, "name", Имя, "Строка");

    Запрос    = СформироватьТекстSQL(Схема);
    Результат = ВыполнитьЗапросSQL(Модуль, Запрос, , , Соединение, Tls);

    Возврат Результат;

КонецФункции

Функция ГарантироватьТаблицу(Знач Модуль
    , Знач Таблица
    , Знач СтруктураКолонок
    , Знач Соединение = ""
    , Знач Tls        = Неопределено) Экспорт

    ТекстОшибки = "Структура колонок не является валидной структурой ключ-значение";
    OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(СтруктураКолонок, ТекстОшибки);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(Таблица);

    Result_ = "result";

    СтруктураРезультата = Новый Структура(Result_, Истина);

    Соединение     = ОткрытьСоединение(Модуль, Соединение, Tls);
    ПроблемныйЭтап = ОбработатьНачалоЗаписи(Модуль, Истина, Соединение);

    Если ЗначениеЗаполнено(ПроблемныйЭтап) Тогда
        Возврат ПроблемныйЭтап;
    КонецЕсли;

    Попытка

        ОписаниеТаблицы = ПолучитьСтруктуруТаблицы(Модуль, Таблица, Соединение, Tls);

        Если Не ОписаниеТаблицы[Result_] Тогда
            Возврат ОписаниеТаблицы;
        КонецЕсли;

        КолонкиТаблицы = ОписаниеТаблицы["data"];

        Если Не ЗначениеЗаполнено(КолонкиТаблицы) Тогда
            СтруктураРезультата = СоздатьТаблицу(Модуль, Таблица, СтруктураКолонок, Соединение, Tls);
        Иначе

            Ошибка = НормализоватьТаблицу(Модуль, Таблица, СтруктураКолонок, КолонкиТаблицы, Соединение, Tls);

            Если Ошибка <> Неопределено Тогда
                Возврат Ошибка;
            КонецЕсли;

        КонецЕсли;

        Завершение = Модуль.ВыполнитьЗапросSQL("COMMIT;", , , Соединение);
        СтруктураРезультата.Вставить("commit", Завершение);

    Исключение

        Откат = Модуль.ВыполнитьЗапросSQL("ROLLBACK;", , , Соединение);

        СтруктураРезультата.Вставить(Result_   , Ложь);
        СтруктураРезультата.Вставить("error"   , ОписаниеОшибки());
        СтруктураРезультата.Вставить("rollback", Откат);

    КонецПопытки;

    Возврат СтруктураРезультата;

КонецФункции

Функция ДобавитьЗаписи(Знач Модуль
    , Знач Таблица
    , Знач МассивДанных
    , Знач Транзакция = Истина
    , Знач Соединение = ""
    , Знач Tls        = Неопределено) Экспорт

    OPI_ПреобразованиеТипов.ПолучитьМассив(МассивДанных);
    OPI_ПреобразованиеТипов.ПолучитьБулево(Транзакция);

    Соединение     = ОткрытьСоединение(Модуль, Соединение, Tls);
    ПроблемныйЭтап = ОбработатьНачалоЗаписи(Модуль, Транзакция, Соединение);

    Если ЗначениеЗаполнено(ПроблемныйЭтап) Тогда
        Возврат ПроблемныйЭтап;
    КонецЕсли;

    СтруктураОбработки  = ОбработатьЗаписи(Модуль, Таблица, МассивДанных, Неопределено, Транзакция, Соединение);
    СтруктураРезультата = ОбработатьКонецЗаписи(СтруктураОбработки, Модуль, Транзакция, Соединение);

    Возврат СтруктураРезультата;

КонецФункции

Функция ПолучитьЗаписи(Знач Модуль
    , Знач Таблица
    , Знач Поля       = "*"
    , Знач Фильтры    = ""
    , Знач Сортировка = ""
    , Знач Количество = ""
    , Знач Соединение = ""
    , Знач Tls        = Неопределено) Экспорт

    Схема = ПустаяСхемаSQL("SELECT", Модуль);

    УстановитьИмяТаблицы(Схема, Таблица);
    УстановитьЛимит(Схема, Количество);

    ЗаполнитьПоля(Схема, Поля);
    ЗаполнитьФильтры(Схема, Фильтры);
    ЗаполнитьСортировку(Схема, Сортировка);

    Запрос = СформироватьТекстSQL(Схема);

    Результат = ВыполнитьЗапросSQL(Модуль, Запрос, Схема["values"], , Соединение, Tls);

    Возврат Результат;

КонецФункции

Функция ОбновитьЗаписи(Знач Модуль
    , Знач Таблица
    , Знач СтруктураЗначений
    , Знач Фильтры    = ""
    , Знач Соединение = ""
    , Знач Tls        = Неопределено) Экспорт

    Схема = ПустаяСхемаSQL("UPDATE", Модуль);

    МассивПолей    = Новый Массив;
    МассивЗначений = Новый Массив;

    УстановитьИмяТаблицы(Схема, Таблица);
    РазделитьКоллекциюДанных(СтруктураЗначений, МассивПолей, МассивЗначений);

    Схема["values"] = МассивЗначений;

    Для Каждого Поле Из МассивПолей Цикл
        ДобавитьПоле(Схема, Поле);
    КонецЦикла;

    ЗаполнитьФильтры(Схема, Фильтры);

    Запрос    = СформироватьТекстSQL(Схема);
    Результат = ВыполнитьЗапросSQL(Модуль, Запрос, Схема["values"], , Соединение, Tls);

    Возврат Результат;

КонецФункции

Функция ГарантироватьЗаписи(Знач Модуль
    , Знач Таблица
    , Знач МассивДанных
    , Знач КлючевыеПоля = ""
    , Знач Транзакция   = Истина
    , Знач Соединение   = ""
    , Знач Tls          = Неопределено) Экспорт

    Если ЗначениеЗаполнено(КлючевыеПоля) Тогда
        OPI_ПреобразованиеТипов.ПолучитьМассив(КлючевыеПоля);
    Иначе
        КлючевыеПоля = Новый Массив;
    КонецЕсли;

    OPI_ПреобразованиеТипов.ПолучитьМассив(МассивДанных);
    OPI_ПреобразованиеТипов.ПолучитьБулево(Транзакция);

    Соединение     = ОткрытьСоединение(Модуль, Соединение, Tls);
    ПроблемныйЭтап = ОбработатьНачалоЗаписи(Модуль, Транзакция, Соединение);

    Если ЗначениеЗаполнено(ПроблемныйЭтап) Тогда
        Возврат ПроблемныйЭтап;
    КонецЕсли;

    СтруктураОбработки  = ОбработатьЗаписи(Модуль, Таблица, МассивДанных, КлючевыеПоля, Транзакция, Соединение);
    СтруктураРезультата = ОбработатьКонецЗаписи(СтруктураОбработки, Модуль, Транзакция, Соединение);

    Возврат СтруктураРезультата;

КонецФункции

Функция УдалитьЗаписи(Знач Модуль
    , Знач Таблица
    , Знач Фильтры    = ""
    , Знач Соединение = ""
    , Знач Tls        = Неопределено) Экспорт

    Схема = ПустаяСхемаSQL("DELETE", Модуль);

    УстановитьИмяТаблицы(Схема, Таблица);

    ЗаполнитьФильтры(Схема, Фильтры);

    Запрос    = СформироватьТекстSQL(Схема);
    Результат = ВыполнитьЗапросSQL(Модуль, Запрос, Схема["values"], , Соединение, Tls);

    Возврат Результат;

КонецФункции


Функция ПолучитьИли(Знач Коллекция, Знач Поле, Знач ЗначениеИначе) Экспорт

    ЗначениеПоля = Неопределено;
    Существует   = ПолеКоллекцииСуществует(Коллекция, Поле, ЗначениеПоля);

    Если Не Существует Тогда
        ЗначениеПоля = ЗначениеИначе;
    КонецЕсли;

    Возврат ЗначениеПоля;

КонецФункции

Функция СравнитьДвеКоллекции(Знач ПерваяКоллекция
    , Знач ВтораяКоллекция
    , Знач ИсключаемыеПоля  = Неопределено
    , Знач РодительскоеПоле = "Корень") Экспорт

    Если ТипЗнч(ПерваяКоллекция) <> ТипЗнч(ВтораяКоллекция) Тогда
        Если Не (ЭтоКоллекция(ПерваяКоллекция, Истина) И ЭтоКоллекция(ВтораяКоллекция, Истина)) Тогда
            ОтладочнаяИнформация(СтрШаблон("%1: Типы коллекций не равны", РодительскоеПоле), Истина);
            Возврат Ложь;
        КонецЕсли;
    КонецЕсли;

    СписокИсключаемых = Новый СписокЗначений();

    Если Не ИсключаемыеПоля = Неопределено Тогда
        OPI_ПреобразованиеТипов.ПолучитьМассив(ИсключаемыеПоля);
        СписокИсключаемых.ЗагрузитьЗначения(ИсключаемыеПоля);
    КонецЕсли;

    Попытка

        Если ЭтоКоллекция(ПерваяКоллекция, Истина) Тогда

            Для Каждого ЭлементКоллекции Из ПерваяКоллекция Цикл

                Если СписокИсключаемых.НайтиПоЗначению(ЭлементКоллекции.Ключ) <> Неопределено Тогда
                    Продолжить;
                КонецЕсли;

                ТекущийКлюч     = ЭлементКоллекции.Ключ;
                ТекущееЗначение = ЭлементКоллекции.Значение;
                ТекущееПоле     = СтрШаблон("%1.%2", РодительскоеПоле, Строка(ЭлементКоллекции.Ключ));

                Если Не СравнитьДвеКоллекции(ТекущееЗначение, ВтораяКоллекция[ТекущийКлюч], , ТекущееПоле) Тогда
                    Возврат Ложь;
                КонецЕсли;

            КонецЦикла;

        ИначеЕсли ЭтоКоллекция(ПерваяКоллекция) Тогда

            Для Н = 0 По ПерваяКоллекция.ВГраница() Цикл

                ТекущееПоле = СтрШаблон("%1.%2", РодительскоеПоле, Строка(Н));

                Если Не СравнитьДвеКоллекции(ПерваяКоллекция[Н], ВтораяКоллекция[Н], , ТекущееПоле) Тогда
                    Возврат Ложь;
                КонецЕсли;

            КонецЦикла;

        Иначе

             Если ПерваяКоллекция <> ВтораяКоллекция Тогда
                 ОтладочнаяИнформация(СтрШаблон("%1: Значения не равны", РодительскоеПоле), Истина);
                 Возврат Ложь;
             КонецЕсли;

        КонецЕсли;

    Исключение
        ОтладочнаяИнформация(СтрШаблон("Исключение: %1", ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())));
        Возврат Ложь;
    КонецПопытки;

    Возврат Истина;

КонецФункции

#КонецОбласти

#Область РаботаСДатой

Функция ПолучитьТекущуюДату() Экспорт

    Если ЭтоOneScript() Тогда

        ТекущаяДата = ТекущаяДата();

    Иначе

        // !OInt ТекущаяДата = Неопределено;

        ТекущаяДата = ТекущаяДатаСеанса(); // !OPI

    КонецЕсли;

    Возврат ТекущаяДата;

КонецФункции

Функция UNIXTime(Знач Дата) Экспорт

    ОТД  = Новый ОписаниеТипов("Дата");
    Дата = ОТД.ПривестиЗначение(Дата);

    UNIX = Формат(Дата - Дата(1970, 1, 1, 1, 0, 0), "ЧЦ=10; ЧДЦ=0; ЧГ=0");
    UNIX = СтрЗаменить(UNIX, ","        , "");
    UNIX = СтрЗаменить(UNIX, Символы.НПП, "");
    UNIX = СтрЗаменить(UNIX, " "        , "");
    UNIX = Лев(UNIX, 10);

    Возврат UNIX;

КонецФункции

Функция ВременнаяМеткаISO(Знач Дата) Экспорт

    Метка = Лев(XMLСтрока(Дата), 19) + "Z";
    Метка = СтрЗаменить(Метка, "-", "");
    Метка = СтрЗаменить(Метка, ":", "");

    Возврат Метка;

КонецФункции

Функция ДатаISO8601(Знач Дата) Экспорт

    Возврат XMLСтрока(Дата);

КонецФункции

Функция ДатаRFC3339(Знач Дата, Знач Смещение = "Z") Экспорт

    OPI_ПреобразованиеТипов.ПолучитьДату(Дата);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(Смещение);

    Возврат XMLСтрока(Дата) + Смещение;

КонецФункции

Функция ДатаRFC822(Дата) Экспорт

    ДниНедели = Новый Соответствие;
    ДниНедели.Вставить(1, "Mon");
    ДниНедели.Вставить(2, "Tue");
    ДниНедели.Вставить(3, "Wed");
    ДниНедели.Вставить(4, "Thu");
    ДниНедели.Вставить(5, "Fri");
    ДниНедели.Вставить(6, "Sat");
    ДниНедели.Вставить(7, "Sun");

    Месяцы = Новый Соответствие;
    Месяцы.Вставить(1 , "Jan");
    Месяцы.Вставить(2 , "Feb");
    Месяцы.Вставить(3 , "Mar");
    Месяцы.Вставить(4 , "Apr");
    Месяцы.Вставить(5 , "May");
    Месяцы.Вставить(6 , "Jun");
    Месяцы.Вставить(7 , "Jul");
    Месяцы.Вставить(8 , "Aug");
    Месяцы.Вставить(9 , "Sep");
    Месяцы.Вставить(10, "Oct");
    Месяцы.Вставить(11, "Nov");
    Месяцы.Вставить(12, "Dec");

    ДеньНедели = ДниНедели.Получить(ДеньНедели(Дата));
    День       = Формат(День(Дата), "ЧЦ=2; ЧВН=");
    Месяц      = Месяцы.Получить(Месяц(Дата));
    Год        = Формат(Год(Дата), "ЧГ=0");
    Время      = Формат(Дата, "ДФ='ЧЧ:мм:сс'");

    Результат = СтрШаблон("%1, %2 %3 %4 %5 +0000", ДеньНедели, День, Месяц, Год, Время);

    Возврат Результат;

КонецФункции

#КонецОбласти

#Область OneScript

Функция ЭтоOneScript() Экспорт

    Попытка

        Ответ = Ложь;

        // BSLLS:UnusedLocalVariable-off

        //@skip-check module-unused-local-variable
        Проверка = Новый ЗащищенноеСоединениеOpenSSL;

        // BSLLS:UnusedLocalVariable-on

    Исключение

        Ответ = Истина;

    КонецПопытки;

    Возврат Ответ;

КонецФункции

Функция ЭтоCLI() Экспорт

    Если ЭтоOneScript() Тогда

        ЭтоCLI          = Неопределено;
        // !OInt ЭтоCLI = ПолучитьПеременнуюСреды("OINT_CLI");
        ЭтоCLI          = ?(ЗначениеЗаполнено(ЭтоCLI), ЭтоCLI, "NO");

        Возврат ЭтоCLI = "YES";

    КонецЕсли;

    Возврат Ложь;

КонецФункции

Функция ПолучитьКаталогБиблиотеки() Экспорт

    КаталогСкрипта = "";

    // !OInt КаталогСкрипта = ТекущийСценарий().Каталог;

    Если Не ЗначениеЗаполнено(КаталогСкрипта) Тогда
        Возврат "";
    КонецЕсли;

    КаталогСкрипта = СтрЗаменить(КаталогСкрипта, "\", "/");
    МассивЧастей   = СтрРазделить(КаталогСкрипта, "/");

    МассивЧастей.Удалить(МассивЧастей.ВГраница());
    МассивЧастей.Удалить(МассивЧастей.ВГраница());
    МассивЧастей.Удалить(МассивЧастей.ВГраница());
    МассивЧастей.Удалить(МассивЧастей.ВГраница());
    МассивЧастей.Удалить(МассивЧастей.ВГраница());

    Путь = СтрСоединить(МассивЧастей, "/");

    Возврат Путь;

КонецФункции

Процедура ИнформацияОПрогрессе(Знач Текущее, Знач Всего, Знач ЕдИзм, Знач Делитель = 1) Экспорт

    Если Не ЭтоCLI() Тогда
        Возврат;
    КонецЕсли;

    Целое   = 100;
    Текущее = Окр(Текущее / Делитель, 2);
    Всего   = Окр(Всего / Делитель, 2);
    Процент = Цел(Текущее / Всего * Целое);

    СтрТекущее = ЧислоВСтроку(Текущее);
    СтрВсего   = ЧислоВСтроку(Всего);
    СтрПроцент = ЧислоВСтроку(Процент);

    Прогресс   = "Прогресс [" + СтрПроцент + "%" + "] ▐";
    Информация = "▌ " + СтрТекущее + "/" + СтрВсего + " " + ЕдИзм;

    // Прогресс бар
    ДлинаПолоски = 30;
    Счетчик      = 0;
    Буфер        = "";

    Показатель = Цел(Текущее / Всего * ДлинаПолоски);

    Пока Счетчик < ДлинаПолоски Цикл
        Буфер   = Буфер + ?(Счетчик < Показатель, "█", " ");
        Счетчик = Счетчик + 1;
    КонецЦикла;

    ВывестиТекстВТекущуюСтроку(Прогресс, , Истина);
    ВывестиТекстВТекущуюСтроку(Буфер   , "Зеленый");
    ВывестиТекстВТекущуюСтроку(Информация);

    Если Процент = Целое Тогда
        ВывестиТекстВТекущуюСтроку(Символы.ПС, , Истина);
    КонецЕсли;

КонецПроцедуры


Функция ВызватьСНастройками(Знач ИмяМодуля, Знач ИмяФункции, Знач Параметры = Неопределено,
    Знач Настройки = Неопределено) Экспорт

    НормализованныеНастройки = НормализоватьНастройки(Настройки);

    Если OPI_Инструменты.ПолучитьИли(НормализованныеНастройки, "dontwait", Ложь) Тогда

        UID = Новый УникальныйИдентификатор;

        АдресРезультата = Неопределено;
        АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, UID); // !OPI

        ПараметрыЗадания = Новый Массив;
        ПараметрыЗадания.Добавить(ИмяМодуля);
        ПараметрыЗадания.Добавить(ИмяФункции);
        ПараметрыЗадания.Добавить(Параметры);
        ПараметрыЗадания.Добавить(НормализованныеНастройки);
        ПараметрыЗадания.Добавить(АдресРезультата);

        ФоновоеЗадание = ФоновыеЗадания.Выполнить("OPI_РасширенныйВызов.ВызватьСНастройкамиСлужебный", ПараметрыЗадания);

        Результат = Новый Структура("ФоновоеЗадание,Адрес", ФоновоеЗадание, АдресРезультата);

    Иначе

        Результат = ВызватьСНастройкамиСлужебный(ИмяМодуля, ИмяФункции, Параметры, НормализованныеНастройки);

    КонецЕсли;

    //@skip-check constructor-function-return-section
    Возврат Результат;

КонецФункции

// Получить доступные настройки
// Получает описание доступных настроек для указанного метода (справочно)
//
// Параметры:
//  ИмяМодуля  - Строка - Имя модуля ОПИ
//  ИмяФункции - Строка - Имя функции в модуле
//
// Возвращаемое значение:
//  Строка - справка по доступным настройкам

Функция ПолучитьДоступныеНастройки(Знач ИмяМодуля, Знач ИмяФункции) Экспорт

    СтруктураМакета   = ПолучитьПолныйИндексНастроек();
    СтруктураНастроек = НайтиВИндексе(СтруктураМакета, ИмяМодуля, ИмяФункции);

    Если СтруктураНастроек = Неопределено Тогда
        Возврат "Для этой функции нет доступных настроек";
    КонецЕсли;

    МассивОписания = Новый Массив;

    Для Каждого Настройка Из СтруктураНастроек Цикл
        МассивОписания.Добавить(СтрШаблон("%1: %2", Настройка.Ключ, Настройка.Значение));
    КонецЦикла;

    Возврат СтрСоединить(МассивОписания, Символы.ПС);

КонецФункции

#КонецОбласти

#Область СлужебныйПрограммныйИнтерфейс

Функция ВызватьСНастройкамиСлужебный(Знач ИмяМодуля, Знач ИмяФункции, Знач Параметры, Знач Настройки,
    Знач АдресХранилища = Неопределено) Экспорт

    УстановитьНастройки(Настройки);

    Если ЗначениеЗаполнено(Параметры) Тогда
        OPI_ПреобразованиеТипов.ПолучитьМассив(Параметры);
    Иначе
        Параметры = Новый Массив;
    КонецЕсли;

    ПараметрыСтроки = Новый Массив;

    Для Н = 0 По Параметры.ВГраница() Цикл
        ПараметрыСтроки.Добавить(СтрШаблон("Параметры[%1]", Н));
    КонецЦикла;

    OPI_ПреобразованиеТипов.ПолучитьСтроку(ИмяМодуля);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(ИмяФункции);

    СтрокаВызова = СтрШаблон("Результат = %1.%2(%3);", ИмяМодуля, ИмяФункции, СтрСоединить(ПараметрыСтроки, ", "));

    Результат = Неопределено;

    Попытка
        //@skip-check server-execution-safe-mode
        Выполнить (СтрокаВызова);
    Исключение
        УдалитьНастройки();
        ВызватьИсключение;
    КонецПопытки;

    УдалитьНастройки();

    Если ЗначениеЗаполнено(АдресХранилища) Тогда
        ПоместитьВоВременноеХранилище(Результат, АдресХранилища); // !OPI
    КонецЕсли;

    //@skip-check constructor-function-return-section
    Возврат Результат;

КонецФункции

Функция ПолучитьТекущиеНастройки() Экспорт

    Попытка
        //@skip-check bsl-legacy-check-string-literal
        Возврат ПараметрыСеанса["OPI_НастройкиРасширенногоВызова"]; // !OPI
        // !OInt Возврат ТекущиеНастройки;
    Исключение
        Возврат Неопределено;
    КонецПопытки;

КонецФункции

Функция ПолучитьПолныйИндексНастроек() Экспорт

    ТекстМакета     = OPI_Инструменты.ПолучитьТекстовыйМакет("OPI_Text_MethodSettings");
    СтруктураМакета = OPI_Инструменты.JsonВСтруктуру(ТекстМакета, Ложь);

    Возврат СтруктураМакета;

КонецФункции

Функция НайтиВИндексе(Знач Индекс, Знач ИмяМодуля, Знач ИмяФункции) Экспорт

    OPI_ПреобразованиеТипов.ПолучитьСтроку(ИмяМодуля);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(ИмяФункции);

    НетНастроек = Ложь;

    Попытка
        СтруктураНастроек = Индекс[ИмяМодуля][ИмяФункции];
    Исключение

        НетНастроек = Истина;

    КонецПопытки;

    Если НетНастроек Или СтруктураНастроек.Количество() = 0 Тогда
        Возврат Неопределено;
    КонецЕсли;

    Возврат СтруктураНастроек;

КонецФункции

Функция УстановитьНастройки(Знач Настройки) Экспорт

    Если ЗначениеЗаполнено(Настройки) Тогда
        ТекущиеНастройки                                = Настройки;
        ПараметрыСеанса.OPI_НастройкиРасширенногоВызова = Новый ФиксированнаяСтруктура(ТекущиеНастройки); // !OPI
    КонецЕсли;

    Возврат ТекущиеНастройки;

КонецФункции

Процедура НормализоватьПромежуточныйРезультат(Результат) Экспорт

    НормализованныйРезультат = Неопределено;

    Если OPI_Инструменты.ПолучитьИли(ПолучитьТекущиеНастройки(), "adv_response", Ложь)
        И OPI_Инструменты.ПолеКоллекцииСуществует(Результат, "body", НормализованныйРезультат) Тогда

        Результат = НормализованныйРезультат;

    КонецЕсли;

КонецПроцедуры

Процедура УдалитьНастройки() Экспорт

    //@skip-check module-unused-local-variable
    ТекущиеНастройки = Новый Структура;

    ПараметрыСеанса.OPI_НастройкиРасширенногоВызова = Новый ФиксированнаяСтруктура; // !OPI

КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Функция НормализоватьНастройки(Знач Настройки)

    Если ЗначениеЗаполнено(Настройки) Тогда

        ТекстОшибки = "Переданные настройки не являются валидной структурой ключ и значение";
        OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(Настройки, ТекстОшибки);

        Если Не ТипЗнч(Настройки) = Тип("Структура") Тогда

            НормализованныеНастройки = Новый Структура;

            Для Каждого КлючЗначение Из Настройки Цикл
                НормализованныеНастройки.Вставить(КлючЗначение.Ключ, КлючЗначение.Значение);
            КонецЦикла;

        Иначе
            НормализованныеНастройки = OPI_Инструменты.КопироватьКоллекцию(Настройки);
        КонецЕсли;

    Иначе
        НормализованныеНастройки = Новый Структура;
    КонецЕсли;

    Возврат НормализованныеНастройки;

КонецФункции

#КонецОбласти


Функция ВызватьСНастройками(Знач ИмяМодуля, Знач ИмяФункции, Знач Параметры = Неопределено,
    Знач Настройки = Неопределено) Экспорт

    НормализованныеНастройки = НормализоватьНастройки(Настройки);

    Если OPI_Инструменты.ПолучитьИли(НормализованныеНастройки, "dontwait", Ложь) Тогда

        UID = Новый УникальныйИдентификатор;

        АдресРезультата = Неопределено;

        ПараметрыЗадания = Новый Массив;
        ПараметрыЗадания.Добавить(ИмяМодуля);
        ПараметрыЗадания.Добавить(ИмяФункции);
        ПараметрыЗадания.Добавить(Параметры);
        ПараметрыЗадания.Добавить(НормализованныеНастройки);
        ПараметрыЗадания.Добавить(АдресРезультата);

        ФоновоеЗадание = ФоновыеЗадания.Выполнить("OPI_РасширенныйВызов.ВызватьСНастройкамиСлужебный", ПараметрыЗадания);

        Результат = Новый Структура("ФоновоеЗадание,Адрес", ФоновоеЗадание, АдресРезультата);

    Иначе

        Результат = ВызватьСНастройкамиСлужебный(ИмяМодуля, ИмяФункции, Параметры, НормализованныеНастройки);

    КонецЕсли;

    //@skip-check constructor-function-return-section
    Возврат Результат;

КонецФункции

// Получить доступные настройки
// Получает описание доступных настроек для указанного метода (справочно)
//
// Параметры:
//  ИмяМодуля  - Строка - Имя модуля ОПИ
//  ИмяФункции - Строка - Имя функции в модуле
//
// Возвращаемое значение:
//  Строка - справка по доступным настройкам

Функция ПолучитьДоступныеНастройки(Знач ИмяМодуля, Знач ИмяФункции) Экспорт

    СтруктураМакета   = ПолучитьПолныйИндексНастроек();
    СтруктураНастроек = НайтиВИндексе(СтруктураМакета, ИмяМодуля, ИмяФункции);

    Если СтруктураНастроек = Неопределено Тогда
        Возврат "Для этой функции нет доступных настроек";
    КонецЕсли;

    МассивОписания = Новый Массив;

    Для Каждого Настройка Из СтруктураНастроек Цикл
        МассивОписания.Добавить(СтрШаблон("%1: %2", Настройка.Ключ, Настройка.Значение));
    КонецЦикла;

    Возврат СтрСоединить(МассивОписания, Символы.ПС);

КонецФункции

#КонецОбласти

#Область СлужебныйПрограммныйИнтерфейс

Функция ВызватьСНастройкамиСлужебный(Знач ИмяМодуля, Знач ИмяФункции, Знач Параметры, Знач Настройки,
    Знач АдресХранилища = Неопределено) Экспорт

    УстановитьНастройки(Настройки);

    Если ЗначениеЗаполнено(Параметры) Тогда
        OPI_ПреобразованиеТипов.ПолучитьМассив(Параметры);
    Иначе
        Параметры = Новый Массив;
    КонецЕсли;

    ПараметрыСтроки = Новый Массив;

    Для Н = 0 По Параметры.ВГраница() Цикл
        ПараметрыСтроки.Добавить(СтрШаблон("Параметры[%1]", Н));
    КонецЦикла;

    OPI_ПреобразованиеТипов.ПолучитьСтроку(ИмяМодуля);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(ИмяФункции);

    СтрокаВызова = СтрШаблон("Результат = %1.%2(%3);", ИмяМодуля, ИмяФункции, СтрСоединить(ПараметрыСтроки, ", "));

    Результат = Неопределено;

    Попытка
        //@skip-check server-execution-safe-mode
        Выполнить (СтрокаВызова);
    Исключение
        УдалитьНастройки();
        ВызватьИсключение;
    КонецПопытки;

    УдалитьНастройки();

    Если ЗначениеЗаполнено(АдресХранилища) Тогда
    КонецЕсли;

    //@skip-check constructor-function-return-section
    Возврат Результат;

КонецФункции

Функция ПолучитьТекущиеНастройки() Экспорт

    Попытка
        //@skip-check bsl-legacy-check-string-literal
        Возврат ТекущиеНастройки;
    Исключение
        Возврат Неопределено;
    КонецПопытки;

КонецФункции

Функция ПолучитьПолныйИндексНастроек() Экспорт

    ТекстМакета     = OPI_Инструменты.ПолучитьТекстовыйМакет("OPI_Text_MethodSettings");
    СтруктураМакета = OPI_Инструменты.JsonВСтруктуру(ТекстМакета, Ложь);

    Возврат СтруктураМакета;

КонецФункции

Функция НайтиВИндексе(Знач Индекс, Знач ИмяМодуля, Знач ИмяФункции) Экспорт

    OPI_ПреобразованиеТипов.ПолучитьСтроку(ИмяМодуля);
    OPI_ПреобразованиеТипов.ПолучитьСтроку(ИмяФункции);

    НетНастроек = Ложь;

    Попытка
        СтруктураНастроек = Индекс[ИмяМодуля][ИмяФункции];
    Исключение

        НетНастроек = Истина;

    КонецПопытки;

    Если НетНастроек Или СтруктураНастроек.Количество() = 0 Тогда
        Возврат Неопределено;
    КонецЕсли;

    Возврат СтруктураНастроек;

КонецФункции

Функция УстановитьНастройки(Знач Настройки) Экспорт

    Если ЗначениеЗаполнено(Настройки) Тогда
        ТекущиеНастройки = Настройки;
    КонецЕсли;

    Возврат ТекущиеНастройки;

КонецФункции

Процедура НормализоватьПромежуточныйРезультат(Результат) Экспорт

    НормализованныйРезультат = Неопределено;

    Если OPI_Инструменты.ПолучитьИли(ПолучитьТекущиеНастройки(), "adv_response", Ложь)
        И OPI_Инструменты.ПолеКоллекцииСуществует(Результат, "body", НормализованныйРезультат) Тогда

        Результат = НормализованныйРезультат;

    КонецЕсли;

КонецПроцедуры

Процедура УдалитьНастройки() Экспорт

    //@skip-check module-unused-local-variable
    ТекущиеНастройки = Новый Структура;

КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Функция НормализоватьНастройки(Знач Настройки)

    Если ЗначениеЗаполнено(Настройки) Тогда

        ТекстОшибки = "Переданные настройки не являются валидной структурой ключ и значение";
        OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(Настройки, ТекстОшибки);

        Если Не ТипЗнч(Настройки) = Тип("Структура") Тогда

            НормализованныеНастройки = Новый Структура;

            Для Каждого КлючЗначение Из Настройки Цикл
                НормализованныеНастройки.Вставить(КлючЗначение.Ключ, КлючЗначение.Значение);
            КонецЦикла;

        Иначе
            НормализованныеНастройки = OPI_Инструменты.КопироватьКоллекцию(Настройки);
        КонецЕсли;

    Иначе
        НормализованныеНастройки = Новый Структура;
    КонецЕсли;

    Возврат НормализованныеНастройки;

КонецФункции

#КонецОбласти

#Region Alternate

Function CallWithSettings(Val ModuleName, Val FunctionName, Val Parameters = Undefined, Val Settings = Undefined) Export
    Return ВызватьСНастройками(ModuleName, FunctionName, Parameters, Settings);
EndFunction

Function GetAvailableSettings(Val ModuleName, Val FunctionName) Export
    Return ПолучитьДоступныеНастройки(ModuleName, FunctionName);
EndFunction

Function CallWithSettingsService(Val ModuleName, Val FunctionName, Val Parameters, Val Settings, Val StorageAddress = Undefined) Export
    Return ВызватьСНастройкамиСлужебный(ModuleName, FunctionName, Parameters, Settings, StorageAddress);
EndFunction

Function GetCurrentSettings() Export
    Return ПолучитьТекущиеНастройки();
EndFunction

Function GetFullSettingsIndex() Export
    Return ПолучитьПолныйИндексНастроек();
EndFunction

Function FindInIndex(Val Index, Val ModuleName, Val FunctionName) Export
    Return НайтиВИндексе(Index, ModuleName, FunctionName);
EndFunction

Function SetSettings(Val Settings) Export
    Return УстановитьНастройки(Settings);
EndFunction

Procedure NormalizeIntermediateResult(Result) Export
    НормализоватьПромежуточныйРезультат(Result);
EndProcedure

Procedure DeleteSettings() Export
    УдалитьНастройки();
EndProcedure

#EndRegion