Обсуждение:Вывод в Excel через XML (TFE) лайт версия

0 (0)
  • Развернуть Обсуждение:Вывод в Excel через XML (TFE) лайт версия ( Обсуждение примера  04.02.2020 15:54 )
    0(0)
    Быстрое создание отчётов в Excel, не сильно отличающееся от использования стандартной printLn. Использована идея KaMPiLeR по быстрому выводу в Excel через XML и добавлены простота и удобство использования.
    Посмотреть пример
    >> Ответить
    • Развернуть Дополнительный пример ( Avantage  05.02.2020 12:00 )
      0(0)
      Внутри архива лежит также более реальный макрос для КФМ, который в случае затребования более подробной инфы по счетам, создаёт для каждого счёта, попадающего в список отловленных на первой странице, отдельный лист с операциями за данный период и привязывает к ячейке со счётом на первой странице ссылку на страницу с подробностями, а в первую ячейку страницы с подробностями впихивает обратную ссылку на ячейку со счётом на первой странице, чтобы пользователю не телепаться с поиском глазками среди кучи листов.
      В первых строках private макроса MakeFile() есть пара закомментированных строк, позволяющая напрямую создать файл на локальной машине во временной папке. Возможно в вашей системе этот вариант будет более быстрым, чем создание файла на сервере в TXTFile, а затем перенос его на локал через copyfile(), как сделано (в моей системе это оказалось наоборот в разы дольше). Желающие поэкспериментированть не забудьте закомментить тот самый copyfile в конце макроса.

      И спасибо огромное KaMPiLeRу за идею - скорость вывода в Excel возросла на порядок, если не больше, скорость написания макросов с выводом в Excel - тоже, и поскольку сам вызов Excel происходит когда сам файл уже полностью готов, то и пользователей перестали донимать коллизии, приводившие к невозможности работать в другом Excel-файле во время формирования нового из макроса, чтобы не помешать выводу.
      >> Ответить
      • Развернуть идея не нова - в похапе народ тож так делает - генерит xml потом отдает на скачку. ( KaMPiLeR  06.02.2020 10:23 )
        0(0)
        с вордом тож наверное так можно.и мне этот микрос в наследство от olegbarsky

        >> Ответить
        • Развернуть Да, с вордом так же можно. Я проверил. Но там проще взять готовый шаблон и делать подстановки. Типа: ( Avantage  06.02.2020 11:12 )
          2(1)

          Import Bankinter, rsexts, rcw;
          /*─────────────────────────────────────────────────────────────────────────┐
          │ Процедура для импорта при необходимости вывести что-то в ворд по шаблону│
          │ Шаблон копируется в локальную папку временных файлов, открывается в Word│
          │ и делается замена попарно │
          │ Пример: DoWord("..\\TXTFILE\\SHABLON\\Dog1.dot", │
          │ "@NumDog@", _NumDog, │
          │ "@DatDog@", String(_DogDat), │
          │ "@FIOclient@",_clFIO); // число пар замен не ограничено │
          │ │
          └─────────────────────────────────────────────────────────────────────────*/
          macro DoWord(_ShablonTName)
          var _ms,_wrd, _fso, _Doc, _fName, _fExt, _dir, _dat0=date(0,0,0), _tim0=Time(0,0,0), _dat1=date(0,0,0), _tim1=time(0,0,0);
          SplitFile(_ShablonTName, _fName, _fExt);
          _ms = CreateObject ("rsax","TRsAxServer","RsAxServer",IsStandAlone());
          _fso = _ms.CreateComObject ("Scripting.FileSystemObject", False);
          // Проверяем наличие шаблона
          _dir = TDIRLIST(_shablonTName,"F");
          if (_dir.count <= 0)
          MsgBox("Отсутствет шаблон:|" + _ShablonTName + "|обратитесь в IT отдел");
          return;
          else
          _dat0 = _dir.fDate(0);
          _tim0 = _dir.fTime(0);
          end;
          _fName = MergeFile(_fso.GetSpecialFolder(2).path, _fName, _fExt); //скопируем его на локал в системную папку временных файлов данного юзверя
          if (not _fso.FileExists(_fname)) // если там нет такого же - просто копируем
          if (not copyFile(_ShablonTName, "$"+_fName ))
          MsgBox("Hе удалось перенести шаблон на локальный компьютер!|из|" +_ShablonTName+"|в|"+_fname);
          end;
          else // если там есть уже такой
          DtTmSplit(_fso.GetFile(_fName).DateLastModified, _Dat1, _Tim1);
          if ((_dat1 != _dat0) or (_tim1 != _Tim0)) // если время или дата файлов не совпадают - копируем
          if (not copyFile(_ShablonTName, "$"+_fName ))
          MsgBox(String("Hе удалось перенести ИЗМЕНИВШИЙСЯ шаблон на локаль:|из [",_dat0:f,"] (",_tim0:f,")|"
          ,_ShablonTName,"|в [",_dat1:f,"] (",_tim1:f,")|",_fname));
          end;
          end;
          end;
          _wrd = _ms.CreateComObject ("Word.Application", False);
          _wrd.WindowState=1;
          _Doc=_wrd.Documents.Add(_fName, False);
          _wrd.Visible = False;
          _wrd.ActiveWindow.View=1;
          var _ShabWord="", _SubstWord="", _ii=0;
          for (_ii,2,ParmCount(), 2)
          GetParm(_ii-1, _ShabWord);
          GetParm(_ii, _SubstWord);
          _doc.Content.Find.Execute(_ShabWord, False, False, False, False, False, True, 1, False, _SubstWord, 2);
          end;
          _wrd.ActiveWindow.View.Type=3;
          _wrd.Visible = True;
          _fso = _doc = _wrd = _ms = null;
          end;

          >> Ответить
          • Развернуть Слегка поправленная версия: ( НоскиПоэта  25.10.2021 13:05 )
            0(0)

            /* TurboFileExcel - Lite версия Автор идеи: KaMPiLeR, допиливание Avantage, Носки Поэта
            Быстрое создание Excel файла через запись xml на сервере, а затем копирование его на локал с последующим открытием и созхранением в Excel на локале

            Использование: xxx=TFE("ИмяЛиста") , где ИмяЛиста - название первого листа с Strlen() <= 31

            Основные функции:
            .ColSizes("строка") - задание ширин столбцов в одной строке через запятую. Если первой стоит буква F и после неё число - это номер строки в которой будет автофильтр.
            Если после F нет числа, а сразу запятая - автофильтр ставится по строке ограничения области прокрутки (см. SplitRow)
            .SplitRow(номер) - задание номера строки для области прокрутки (не обязательно). Если есть хоть один заголовок ("H^") то строка для ограничения области прокрутки задаётся по нему.
            .SplitCol(номер) - задание номера колонки для области прокрутки (не обязательно).
            .AddRow(Зачение1, Значение2, Значение3, ...) - заполнение строки таблицы значениями. Значения желательно указвать в виде строки "ФОРМАТ^Значение". Типы ФОРМАТОВ описаны ниже.
            .Go(ИмяФайла) - Собственно создание, перенос и открытие файла в папке временных файлов данного пользователя (если не указан полный локальный путь)
            Если в ИмяФайла первым символом поставить "$" - файл будет создан и открыт в локальной папке "Мои Документы" данного пользователя.
            Если первым символом поставить "#" - файл будет создан в локальной папке "Мои Документы", но открываться не будет. Используется для пакетного создания кучи файлов.
            Если перед именем файла указаны подпапки (Типа: "$From_RS\\Клиенты\\1111\\2222\\ИмяФайла" - нужное дерево папок будет создано "Моих Документах", и файл ляжет в нужную.
            .AddSheet("ИмяЛиста") - добавляет новый лист с заданным именем и делает его активным
            .ActiveSheet(Номер) - делает текущим лист с заданным номером и все дальнейшие действия будут с ним. Возвращает текущий лист в виде объекта, даже если номер не задан.


            ФОРМАТ для .AddRow() может состоять из следующих символов:

            Типы Формата:
            Задаются в строке перед собственно значением и отделяются от него знаком ^
            #: - вокруг ячейки обводится решётка
            _: - под ячейкой проводится линия границы
            Nx: (x- цифра от 1 до 9) - число с "x" знаками после запятой. Если "x" нет, то по умолчанию 2 знака после запятой. Вместо N0 используйте I.
            I: Целое число ( аналог N0)
            D: Дата
            T: время
            S: Строка
            W: строка с переносом

            C: (center) - горизонтальное центрирование
            L: (left) - горизонтальное выравнивание влево
            R: (Right) - горизонтальное выравнивание вправо
            U: (Up) - вертикальное выравнивание по верху (по умолчанию вериткальное выравнивание по нижней границе ячейки)
            Ц: (Центр) - вертикальное выравнивание по центру

            B: Bold (жирный шрифт)
            Шxx: Размер шрифта xx, например Ш15 = выводить в эту ячейку шрифтом с размером 15 (по умолчанию размер шрифта = 11)

            H: полный аналог BWC# - применяется для стандартных заголовков колонок таблицы. Если в строке встретился формат H - по данной строке будет автоматически выставлен SplitRow

            M: Фон ячейки розовый (magenta)
            G: Фон ячейки зелёный
            Y: Фон ячейки жёлтый

            Есть ещё 2 типа для объединения ячеек, но, если есть возможность их не использовать - лучше не использовать, поскольку объединение мешает дальнейшей работе фильтров

            MLx: Объединить с левой ячейкой. Может использоваться несколько раз подряд. Информация и форматирование берутся из самой-самой левой ячейки
            MU: Объединить с верхней ячейкой. Тоже может использоваться несколько раз подряд друг под другом. Информация и форматирование берутся из самой-самой верхней ячейки

            И есть тип для ссылки из данной ячейки на лист, на конкретную ячейку другого листа, на конкретную ячейку данного листа

            @...@ : Ссылка должна быть заключена между собаками. Это может быть @ИмяЛиста@, или @ИмяЛиста!НомерЯчейки@ или просто @!НомерЯчейки@ - если она на этом же листе
            Если ссылка идёт в конце стиля, непосредственно перед ^ - тогда финальную собаку можно не ставить.

            */
            import Globals, rsexts, rslx, rcw;

            private const NumMask=String(V_DOUBLE,",",V_DOUBLEL,",",V_DECIMAL,",",V_NUMERIC,",",V_MONEY,",",V_MONEYL);

            private macro GetGUIDFileName // ну очень уникальное имя для файла.
            return strsubst(Substr(ActiveX("Scriptlet.TypeLib").guid,2,36), "-", "");
            end;
            macro GetLocalTmpPath // путь к локальной папке временных файлов данного пользователя
            return CreateObject ("rsax","TRsAxServer","RsAxServer",IsStandAlone()).CreateComObject("Scripting.FileSystemObject").GetSpecialFolder(2).Path;
            end;
            macro CheckLocalFolder(_dir) // Проверка наличия и досоздание папок в полном пути к локальной папке данного пользователя
            var _FSO = CreateObject("rsax","TRsAxServer","RsAxServer",IsStandAlone()).CreateComObject("Scripting.FileSystemObject");
            var _ret = _FSO.FolderExists(_dir);
            if (not _ret)
            _FSO.CreateFolder(_dir);
            _ret = _FSO.FolderExists(_dir);
            end;
            _FSO = null;
            return _ret;
            end;
            macro GetLocalDocumentsPath //Получить путь к папке "Мои Документы" данного пользователя на случай если хочется сохранять в "Мои документы" локального пользователя
            return CreateObject ("rsax","TRsAxServer","RsAxServer",IsStandAlone()).CreateComObject("wScript.Shell").SpecialFolders("MyDocuments");
            end;
            private macro GoToWindow(_winName)
            return CreateObject ("rsax","TRsAxServer","RsAxServer",IsStandAlone()).CreateComObject("wScript.Shell").AppActivate(_winName);
            end;

            macro CopyFile2Local(_FromFullName, _toFullNewName) //быстрое копирование файла на локал через упаковку в zip, копирование уже архива на локал и распаковку.
            var _zf, _zn,_zp, _fP="", _fn="", _fx="", _fz="";
            if (not ExistFile(_fromFullname))
            return false;
            end;
            _fP = SplitFile(_FromFullName, _fn, _fx);

            _fn = _fn + _fx;
            _zn = GetGuidFileName() +".zip";
            _zp = MergeFile(_fp,_zn);
            _zf = CreateZip(_zp);
            if (ZipAdd(_zf,_fn,_FromFullName))
            CloseZip(_zf);
            _fP = SplitFile(_toFullNewName, _fz,_fx);
            if (copyFile(_zp,_fp +"\\"+_zn,True, "Копирую на локаль"))
            RemoveFile(_zp);
            if (Substr(_toFullNewName,1,1) == "$") // если в направлении КУДА указан Локальный путь ($ в начале пути)
            _fp = Substr(_fp,2);
            CreateObject ("rsax","TRsAxServer","RsAxServer",False).CreateComObject("Shell.Application").NameSpace(_fp).CopyHere(MergeFile(_fp,_zn+"\\"+_fn),16);
            _fp = "$" +_fp;
            else // если указан путь от сервера RS-Bank
            if (not UnzipItem(_zf,0,MergeFile(_fp,_fn)))
            return False;
            end;
            end;
            RemoveFile(MergeFile(_fp,_zn));
            renameFile(MergeFile(_fp,_fn), _toFullNewName);
            return True;
            end;
            end;
            return False;
            end;

            private class TFESheet; // собсно рабочий класс листа
            var
            name : string="Лист1",//название листа
            data = TArray(),
            lSplitRow : Integer=0, //последняя строка заголовка - строки под ней будут скроллироваться
            lSplitCol : Integer=0, //последняя колонка заголовка - колонки правее неё будут скроллироваться
            cSize : String=""; //ширины колонок
            end;

            class TFE(aFirstSheetName: string); //рабочий класс всей таблицы совсеми листами
            private var
            fileName: string,
            ffout,
            sheets=TArray,
            Styles:string="^",
            iSheet:integer=0;

            macro ActiveSheet(_NewSheetNum): TFESheet; // Функция, которая делает активным и возвращает лист с заданным номером. Если такого номера ещё нет, то добавляет его
            if (valType(_NewSheetNum) == V_INTEGER)
            if ((_newSheetNum > 0) and (_NewSheetNum <= sheets.Size))
            iSheet = _newSheetNum;
            end;
            end;
            if(iSheet==0)
            return NULL;
            else
            return sheets[iSheet-1];
            end;
            end;
            private macro NormalizeStr(_str) // замена спецсимволов xml в строке
            return StrSubst(StrSubst(StrSubst(_str, "<", "<"), ">", ">"), "\"",""");
            end;
            private Macro NeedLandscape(_str) //вычисление по суммарной длине колонок - ненастолько ли наша таблица широка, что её пора уже печатать на горизонтально развёрнутых листах
            const SizeForNeedLandscape = 600;
            var _ii=0, _size=0;
            if (Strlen(_str) < 3) return False; end;
            _ii = index(_str,",");
            while (_ii > 0)
            _size = _size + int(Trim(Substr(_str,1,_ii-1)));
            if (_size > SizeForNeedLandscape)
            return True;
            end;
            _str = trim(Substr(_str,_ii+1));
            _ii = index(_str,",");
            end;
            _size = _size + int(_str);
            if (_size > SizeForNeedLandscape)
            return True;
            end;
            return False;
            end;
            Private macro ssType(_str)
            if ((index(_str, "N") > 0) or (index(_str,"I") > 0))
            return "Number";
            elif ((index(_str, "S") > 0) or (index(_str,"W") > 0) or (index(_str,"H") > 0))
            return "String";
            elif ((index(_str, "D") > 0) or (index(_str,"T") > 0))
            return "DateTime";
            end;
            return "String";
            end;
            Private macro PrintCell(_aData, _indexStr)
            var _Format:String="", _ii:integer, _RefStr="", _MergeStr="";
            if (ValType(_aData) != V_STRING)
            return;
            end;
            _ii = index(_aData, "^");
            if (_ii <= 0)
            ffout.WriteLine(String(" <Cell><Data ss:Type=\"String\">",NormalizeStr(_aData),"</Data></Cell>"));
            else
            _format = trim(substr(_aData,1,_ii-1));
            _aData = trim(substr(_aData,_ii+1));
            _ii = index(_format,"@");
            if (_ii > 0)
            _RefStr = Substr(_format,_ii+1);
            _format = Substr(_format,1,_ii-1);
            _ii = Index(_refStr,"@");
            if ( _ii > 0)
            if (_ii < StrLen(_refStr))
            _Format = _format + Substr( _refStr,_ii+1);
            end;
            _refStr = Substr(_refStr,1,_ii-1);
            end;
            _ii = index(_refStr,"!"); // есть ли ссылка на конкретную ячейку ??
            If (_ii <= 0) // нет, ссылка на лист
            _refStr = _refStr + "!A1"; // добавим ссылку на ячеёку.
            elif (_ii == 1) // ссылка на ячеку на данном листе
            _refStr = Substr(_refStr,2);
            end;
            if (StrLen(_refStr) > 0)
            _refStr = " ss:HRef=\"#" + _refStr + "\"";
            end;
            end;
            if (Index(_format, "M") > 0)
            if (index(_format,"ML") > 0)
            return True;
            elif (index(_format,"MU") > 0)
            return False;
            elif (index(_format,"MR") == 1)
            _format = substr(_format,3);
            while (StrIsNumber(substr(_format,1,1)))
            _MergeStr = _mergeStr + substr(_format,1,1);
            _format = Substr(_format,2);
            end;
            _MergeStr = String(" ss:MergeAcross=\"",_MergeStr,"\"");
            elif (index(_format,"MD") == 1)
            _format = substr(_format,3);
            while (StrIsNumber(substr(_format,1,1)))
            _MergeStr = _mergeStr + substr(_format,1,1);
            _format = Substr(_format,2);
            end;
            _MergeStr = String(" ss:MergeDown=\"",_MergeStr,"\"");
            end;
            end;
            if (index(_format,"D") > 0)
            if (CompareStrWithMasks("??.??.????",_adata) == 0)
            _aData = Substr(_aData,7,4) + "-" + Substr(_aData,4,2) + "-" + Substr(_aData,1,2) + "T00:00:00";
            elif (CompareStrWithMasks("?.??.????",_adata) == 0)
            _aData = Substr(_aData,6,4) + "-" + Substr(_aData,3,2) + "-0" + Substr(_aData,1,1) + "T00:00:00";
            end;
            elif (index(_format,"T") > 0)
            if (CompareStrWithMasks("??:??*",_adata) == 0)
            _aData = "1900-01-01T" + _aData;
            elif (CompareStrWithMasks("?:??*",_adata) == 0)
            _aData = "1900-01-01T0" + _aData;
            end;
            end;
            if (_format == "")
            if (_aData == "")
            return False; //пропустим эту ячейку
            end;
            ffout.WriteLine( String(" <Cell",_indexStr,_MergeStr,_RefStr,"><Data ss:Type=\"String\">",NormalizeStr(_aData),"</Data></Cell>"));
            elif (_aData == "")
            ffout.WriteLine( String(" <Cell",_indexStr,_MergeStr,_RefStr," ss:StyleID=\"",_format,"\"/>"));
            elif (substr(_aData,1,1) == "=")
            ffout.WriteLine( String(" <Cell",_indexStr,_MergeStr,_RefStr," ss:StyleID=\"",_format,"\" ss:Formula=\"",NormalizeStr(_aData),"\"><Data ss:Type=\"",ssType(_format),"\"/></Cell>"));
            else
            ffout.WriteLine( String(" <Cell",_indexStr,_MergeStr,_RefStr," ss:StyleID=\"",_format,"\"><Data ss:Type=\"",ssType(_format),"\">",NormalizeStr(_aData),"</Data></Cell>"));
            end;
            end;
            return True;
            end;
            Private macro PrintStyle(_str)
            var _ssAlign="", _ssFont="", _nn=0;
            ffout.WriteLine(String(" <Style ss:ID=\"",_str,"\">"));
            if ((index(_str,"#") > 0) or (index(_str,"H") > 0))
            ffout.WriteLine(" <Borders>");
            ffout.WriteLine(" <Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
            ffout.WriteLine(" <Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
            ffout.WriteLine(" <Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
            ffout.WriteLine(" <Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
            ffout.WriteLine(" </Borders>");
            elif (index(_str,"_") > 0)
            ffout.WriteLine(" <Borders>");
            ffout.WriteLine(" <Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
            ffout.WriteLine(" </Borders>");
            end;
            if ((index(_str,"C") > 0) or (index(_str,"H")>0))
            _ssAlign = _ssAlign + " ss:Horizontal=\"Center\"";
            elif (index(_str,"L") > 0)
            _ssAlign = _ssAlign + " ss:Horizontal=\"Left\"";
            elif (index(_str,"R") > 0)
            _ssAlign = _ssAlign + " ss:Horizontal=\"Right\"";
            end;
            if ((index(_str,"U") > 0) and (index(_str,"MU") <= 0))
            _ssAlign = _ssAlign + " ss:Vertical=\"Top\"";
            elif (index(_str,"Ц") > 0)
            _ssAlign = _ssAlign + " ss:Vertical=\"Center\"";
            end;
            if ((index(_str,"W") > 0) or (index(_str,"H")>0))
            _ssAlign = _ssAlign + " ss:WrapText=\"1\"";
            end;
            if (_ssAlign != "")
            ffout.WriteLine(String(" <Alignment",_ssAlign,"/>"));
            end;
            _ssFont = "";
            _nn = index(_str,"Ш");
            if ( _nn > 0)
            _nn = Int(Substr(_str,_nn+1,2));
            if (_nn > 4)
            _ssFont = String(" ss:Size=\"",_nn,"\"");
            end;
            end;
            _nn = 0;
            if ((index(_str,"B") > 0) or (index(_str,"H")>0))
            _ssFont = _ssFont + " ss:Bold=\"1\"";
            end;
            if (_ssFont != "")
            ffout.WriteLine(" <Font" + _ssFont + "/>");
            end;
            if (index(_str,"M") > 0)
            ffout.writeline(" <Interior ss:Color=\"#FF007F\" ss:Pattern=\"Solid\"/>");
            elif (index(_str,"Y") > 0)
            ffout.writeline(" <Interior ss:Color=\"#FFFF00\" ss:Pattern=\"Solid\"/>");
            elif (index(_str,"G") > 0)
            ffout.writeline(" <Interior ss:Color=\"#007F00\" ss:Pattern=\"Solid\"/>");
            end;
            _nn = index(_str,"N");
            if (_nn > 0)
            if ((_nn < StrLen(_str)) and (StrIsNumber(Substr(_str,_nn+1,1))))
            _nn = int(Substr(_str,_nn+1,1));
            if (_nn == 0)
            ffout.writeline(" <NumberFormat ss:Format=\"#,##0\"/>");
            else
            ffout.writeline(" <NumberFormat ss:Format=\"#,##0."+MkStr("0",_nn)+"\"/>");
            end;
            else
            ffout.writeline(" <NumberFormat ss:Format=\"Standard\"/>");
            end;
            elif (index(_str,"I") > 0)
            ffout.writeline(" <NumberFormat ss:Format=\"#,##0\"/>");
            elif ((index(_str,"S") > 0) or (index(_str,"W") > 0))
            ffout.writeline(" <NumberFormat ss:Format=\"@\"/>");
            elif (index(_str,"D") > 0)
            ffout.writeline(" <NumberFormat ss:Format=\"Short Date\"/>");
            elif (index(_str,"T") > 0)
            ffout.writeline(" <NumberFormat ss:Format=\"Short Time\"/>");
            else
            ffout.writeline(" <NumberFormat ss:Format=\"@\"/>");
            end;
            ffout.WriteLine(" </Style>");
            end;
            macro ColSizes(_str)
            ActiveSheet.cSize=StrSubst(_str," ","");
            end;
            Private macro AddStyle(_str)
            var _ii:integer, _ch:string, _ret="";
            _str = StrUpr(StrSubst(_str," ",""));
            if (StrLen(_str) > 0)
            if (index(Styles , "^"+_str+"^")<=0)
            Styles = Styles + _str + "^";
            end;
            end;
            end;
            Private macro AddMergeLeft(_row, _col, _count)
            var _ii=0, _str;
            for (_ii,_col-1,0,-1)
            _str = ActiveSheet.Data[_row][_ii];
            if (index(_str,"ML") <= 0)
            if (index(_str,"MR") == 1)
            _str = Substr(_str,4);
            while (StrIsNumber(substr(_str,1,1)))
            _str = Substr(_str,2);
            end;
            end;
            _str = String("MR",_col - _ii+_count-1, _str);
            ActiveSheet.Data[_row][_ii] = _str;
            break;
            end;
            end;
            for (_ii,0,_count-1,1)
            ActiveSheet.Data[_row][_col+_ii]="ML^";
            end;
            end;
            Private macro AddMergeUp(_row, _col)
            var _ii=0, _str;
            for (_ii,_row-1,0,-1)
            _str = ActiveSheet.Data[_ii][_col];
            if (index(_str,"MU") <= 0)
            if (index(_str,"MD") == 1)
            _str = Substr(_str,4);
            while (StrIsNumber(substr(_str,1,1)))
            _str = Substr(_str,2);
            end;
            end;
            _str = String("MD",_row - _ii,_str);
            ActiveSheet.Data[_ii][_col] = _str;
            break;
            end;
            end;
            ActiveSheet.Data[_row][_col]="MU^";
            end;
            private macro AddCell( _aData)
            var _format="",_ref="";
            var _ii:integer = ValType(_aData);
            if (_ii == V_ARRAY)
            for (_ii, 0, aSize(_aData)-1,1)
            AddCell(_aData(_ii));
            end;
            elif (_ii == V_GENOBJ) //TARRAY
            for (_ii, 0, _aData.Size-1,1)
            AddCell(_aData[_ii]);
            end;
            elif (_ii == V_DATE)
            AddCell(String("D^",_aData:f));
            elif (_ii == V_TIME)
            AddCell(String("T^",_aData:f));
            elif (_ii == V_INTEGER)
            AddCell(String("I^",_aData));
            elif (CompareStrWithMasks(NumMask,String(_ii)) == 0)
            AddCell(String("N^",_aData:0:2));
            elif (_ii == V_STRING)
            _ii = index(_aData,"^");
            if (_ii > 0)
            _format = Substr(_aData,1,_ii-1);
            _ii = index(_format,"@");
            if (_ii > 0)
            _ref = Substr(_format,_ii+1);
            _format = Substr(_format,1,_ii-1);
            _ii = index(_ref,"@");
            if ((_ii > 0) and (_ii < StrLen(_ref)))
            _format = _format + Substr(_Ref,_ii+1);
            end;
            end;
            _ii = index(_format,"ML");
            if (_ii>0)
            if (not StrIsNumber(Trim(Substr(_format,_ii+2,2))))
            _ii = 1;
            else
            _ii = Int(Trim(Substr(_format,_ii+2,2)));
            end;
            AddMergeLeft(ActiveSheet.data.Size-1,ActiveSheet.data[ActiveSheet.data.Size-1].Size, _ii);
            return;
            elif (index(_format,"MU")>0)
            AddMergeUp(ActiveSheet.data.Size-1,ActiveSheet.data[ActiveSheet.data.Size-1].Size);
            return;
            elif (index(_format,"H")>0)
            if (ActiveSheet.lSplitRow < ActiveSheet.data.Size)
            ActiveSheet.lSplitRow = ActiveSheet.data.Size;
            end;
            end;
            AddStyle(_format);
            end;
            ActiveSheet.data[ActiveSheet.data.Size-1][ActiveSheet.data[ActiveSheet.data.Size-1].Size]=_aData;
            end;
            end;
            macro AddRow;
            ActiveSheet.data[ActiveSheet.data.Size]=TArray;

            var _ii: integer, _jj:integer, _iCol:integer=0, _aData;
            for (_ii,1, ParmCount()-1, 1) // внутри объектов параметры должны начинаться с 1. А getparm(0) - ссылка на сам объект
            if (GetParm(_ii, _adata))
            AddCell( _aData);
            end;
            end;
            end;
            Private macro GetAllStringCount: integer;
            var _ii: integer,
            _ret:integer=0;
            for (_ii,0,sheets.Size-1,1)
            _ret=_ret+sheets[_ii].data.Size;
            end;
            return _ret;
            end;
            macro SplitRow(_RowNum)
            ActiveSheet.lSplitRow = _RowNum;
            end;
            macro SplitCol(_ColNum)
            ActiveSheet.lSplitCol = _ColNum;
            end;

            macro AddSheet(aName: string);
            if(aName==null)
            aName=string("Лист", sheets.Size+1);
            end;

            if(strlen(aName)>31)
            RunError ("Длинна названия листа не может превышать 31 символ");
            end;
            sheets[sheets.Size]=TFESheet;
            iSheet = sheets.Size;
            //начальные значения
            ActiveSheet.Name=aName;
            end;
            macro MakeFile(_xlsFilename, _zoom);
            var _ii: integer, _jj:integer, _tStyle:string, _tData, _afRow=0, _afrange = "",
            _shI: integer, _cnt = 0, _NotSkipped =True, _ffName;

            // _ffName = GetLocalTmpPath() + "\\"+FileName + ".xml";
            // ffout = CreateObject("rsax","TRsAxServer","RsAxServer",IsStandAlone()).CreateComObject("Scripting.FileSystemObject").CreateTextFile(_ffName,True,True);

            _ffName=GetTxtFilename(FileName);
            ffout =TSTREAMDOC(_ffName,"W","utf8");

            // заголовки xml-файла

            ffout.writeline("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            ffout.writeline("<?mso-application progid=\"Excel.Sheet\"?>");
            ffout.writeline("<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"");
            ffout.writeline(" xmlns:o=\"urn:schemas-microsoft-com:office:office\"");
            ffout.writeline(" xmlns:x=\"urn:schemas-microsoft-com:office:excel\"");
            ffout.writeline(" xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\"");
            ffout.writeline(" xmlns:html=\"http://www.w3.org/TR/REC-html40\">");
            ffout.writeline(" <ExcelWorkbook xmlns=\"urn:schemas-microsoft-com:office:excel\">");
            ffout.writeline(" <ProtectStructure>False</ProtectStructure>");
            ffout.writeline(" <ProtectWindows>False</ProtectWindows>");
            ffout.writeline(" </ExcelWorkbook>");

            ffout.writeline(" <Styles>");

            ffout.writeline(" <Style ss:ID=\"Default\" ss:Name=\"Normal\">");
            ffout.writeline(" <Alignment ss:Vertical=\"Bottom\"/>");
            ffout.writeline(" <Borders/>");
            ffout.writeline(" <Font ss:FontName=\"Times New Roman\" x:CharSet=\"204\" ss:Size=\"11\" ss:Color=\"#000000\"/>");
            ffout.writeline(" <Interior/>");
            ffout.writeline(" <NumberFormat/>");
            ffout.writeline(" <Protection/>");
            ffout.writeline(" </Style>");
            _tStyle = trim(Substr(Styles,2));
            _ii = index(_tStyle,"^");
            while (_ii > 1)
            PrintStyle(Substr(_tStyle,1,_ii-1));
            _tStyle = Trim(Substr(_tStyle,_ii+1));
            _ii = index(_tStyle,"^");
            end;
            ffout.writeline(" </Styles>");
            //вывели все стили...

            _shI=0;
            InitProgress(GetAllStringCount, "", "Формирую файл отчёта...");
            _cnt = 0;
            for (_shI,0,sheets.Size-1,1)
            ffout.writeline(" <Worksheet ss:Name=\""+sheets[_shI].Name+"\">");
            ffout.writeline(" <Table>");
            _tStyle = sheets[_shI].cSize;
            _afRow = 0; _afrange = "";
            if (Substr(_tStyle,1,1) == "F") // надо поставить автофильтр
            _tStyle = Trim(Substr(_tStyle,2));
            _ii = index(_tStyle,",");
            if (_ii > 1)
            _afRow = Int(substr(_tStyle,1,_ii-1));
            end;
            _tStyle = Trim(Substr(_tStyle,_ii+1));
            if (_afRow <= 0)
            _afRow = sheets[_shI].lSplitRow;
            end;
            if (_afRow > 0)
            if (sheets[_shI].lSplitCol == 0)
            _afRange = String("R", _afRow,"C1:R",_afRow,"C",sheets[_shI].Data[_afRow-1].Size);
            else
            _afRange = String("R", _afRow,"C",sheets[_shI].lSplitCol,":R",_afRow,"C",sheets[_shI].Data[_afRow-1].Size);
            end;
            end;
            end;
            _ii = index(_tStyle,",");
            while (_ii > 0)
            ffout.WriteLine(String(" <Column ss:Width=\"",trim(Substr(_tStyle,1,_ii-1)),"\"/>"));
            _tStyle = trim(Substr(_tStyle,_ii+1));
            _ii = index(_tStyle,",");
            end;
            ffout.WriteLine(String(" <Column ss:Width=\"",_tStyle,"\"/>"));
            // Таблица
            for (_ii, 0, sheets[_shI].data.Size-1, 1)
            ffout.writeline(" <Row>");
            _NotSkipped = True;
            for (_jj,0,sheets[_shI].data[_ii].Size-1,1)
            _tData = sheets[_shI].data[_ii][_jj];
            if ((index(_tData,"{UPROW}") > 0) and (index(_tData, "^=")>0)) //это формула и упрощенное обозначение всего столбца над этой ячейкой
            _tData = StrSubst(_tData,"{UPROW}",String("R[",sheets[_shI].lSplitRow-_ii,"]C:R[-1]C"));
            end;
            if (_notSkipped)
            _notSkipped = PrintCell( _tData,"");
            else
            _notSkipped = PrintCell( _tData,String(" ss:Index=\"",_jj+1,"\""));
            end;
            end;
            ffout.writeline(" </Row>");
            UseProgress(_cnt=_cnt+1);
            end;

            ffout.writeline(" </Table>");


            ffout.writeline(" <WorksheetOptions xmlns=\"urn:schemas-microsoft-com:office:excel\">");
            if (NeedLandscape(sheets[_ShI].cSize))
            ffout.writeline(" <PageSetup>");
            ffout.writeline(" <Layout x:Orientation=\"Landscape\"/>");
            ffout.writeline(" <Header x:Margin=\"0.3\"/>");
            ffout.writeline(" <Footer x:Margin=\"0.3\"/>");
            ffout.writeline(" <PageMargins x:Bottom=\"0.75\" x:Left=\"0.25\" x:Right=\"0.25\" x:Top=\"0.75\"/>");
            ffout.writeline(" </PageSetup>");
            ffout.writeline(" <FitToPage/>");
            ffout.writeline(" <Print>");
            ffout.writeline(" <FitHeight>0</FitHeight>");
            ffout.writeline(" <PaperSizeIndex>9</PaperSizeIndex>");
            ffout.writeline(" </Print>");
            end;
            if (String(_zoom) != "100")
            ffout.writeline(String(" <Zoom>",_zoom,"</Zoom>"));
            end;
            ffout.writeline(" <Selected/>");
            ffout.writeline(" <FreezePanes/>");
            ffout.writeline(" <FrozenNoSplit/>");
            if (sheets[_ShI].lSplitRow > 0)
            ffout.writeline(" <SplitHorizontal>"+string(sheets[_shI].lSplitRow)+"</SplitHorizontal>");
            ffout.writeline(" <TopRowBottomPane>"+string(sheets[_shI].lSplitRow)+"</TopRowBottomPane>");
            end;
            if (sheets[_ShI].lSplitCol > 0)
            ffout.writeline(" <SplitVertical>"+string(sheets[_shI].lSplitCol)+"</SplitVertical>");
            ffout.writeline(" <LeftColumnRightPane>"+string(sheets[_shI].lSplitCol)+"</LeftColumnRightPane>");
            end;
            if ((sheets[_ShI].lSplitRow > 0) or (sheets[_ShI].lSplitCol > 0))
            ffout.writeline(" <ActivePane>2</ActivePane>");
            ffout.writeline(" <Panes>");
            ffout.writeline(" <Pane>");
            ffout.writeline(" <Number>2</Number>");
            ffout.writeline(" <ActiveRow>"+String(sheets[_shI].lSplitRow)+"</ActiveRow>");
            ffout.writeline(" </Pane>");
            ffout.writeline(" </Panes>");
            end;
            ffout.writeline(" <ProtectObjects>False</ProtectObjects>");
            ffout.writeline(" <ProtectScenarios>False</ProtectScenarios>");
            ffout.writeline(" </WorksheetOptions>");
            if (_afRange != "")
            ffout.writeline(String(" <AutoFilter x:Range=\"", _afRange,"\" xmlns=\"urn:schemas-microsoft-com:office:excel\"/>"));
            end;
            ffout.writeline(" </Worksheet>");
            end;
            RemProgress();

            ffout.writeline("</Workbook>");
            ffout = null;

            copyFile2Local(_ffname,"$"+GetLocalTmpPath()+"\\"+fileName+".xml");
            end;
            private macro OpenFile(_XlsName, _WhatToDo, _passw);
            var xls= CreateObject ("rsax","TRsAxServer","RsAxServer",IsStandAlone()).CreateComObject("Excel.Application",true);
            var _ii = 0, _locPath=GetLocalTmpPath();
            xls.Workbooks.open(GetLocalTmpPath()+"\\"+fileName+".xml");

            xls.Sheets(1).Activate;

            if (ValType(_xlsName) == V_STRING) // надо сохранить в excel файл
            if (Strlen(_xlsname) > 0)
            if (ValType(_passw) != V_STRING)
            _passw = "";
            else
            _passw = trim(_passw);
            end;
            if ((index(_xlsname,":") == 2) or (index(_xlsname,"\\\\") == 1))// прописан полный путь с буквой диска, или именем сервера. Ничего не добавляем и считаем, что все папки уже есть

            else
            if ((_WhatToDo == "$") or (_WhatToDo == "#"))
            _locPath = GetLocalDocumentsPath();
            end;
            _ii = index(_xlsName,"\\");
            while (_ii > 0)
            if (_ii > 1)
            _locPath = MergeFile(_locPath,Substr(_xlsname,1,_ii-1));
            if (not CheckLocalFolder(_locPath))
            MsgBox("Невозможно создать папку " + _locPath);
            return;
            end;
            end;
            _xlsName = Substr(_xlsname,_ii+1);
            _ii = index(_xlsName,"\\");
            end;
            _xlsName = MergeFile(_locPath,_xlsname);
            end;

            if (StrLen(_passw) > 2)
            xls.ActiveWorkbook.Password = _passw;
            end;
            xls.ActiveWorkbook.SaveAs(_xlsname,51);
            removeFile("$" + GetLocalTmpPath()+"\\"+fileName+".xml");

            if ((_WhatToDo == "#") or (_WhatToDo == "^"))
            xls.quit();
            xls=null;
            else
            xls.Visible = true;
            GoToWindow(FileName);
            end;
            else
            xls.Visible = true;
            end;
            else
            xls.Visible = true;
            end;

            OnError()
            msgboxex("Невозможно открыть файл!|" + _xlsName);
            end;

            macro Go(_XlsFileName,_zoom, _passw); // программа формирования собственно файла, переноса его на локал и открытия/сохранения под нужным именем
            var _WhatToDo=""; // первый символ в имени файла, если он таки является управляющим символом. Или пустая строка.
            if (ValType(_zoom) == V_UNDEF)
            _zoom = 100;
            end;
            if (ValType(_passw) != V_STRING)
            _passw = "";
            else
            _passw = trim(_passw);
            end;
            if (ValType(_XlsFileName) == V_STRING)
            if (Strlen(_XlsFileName) > 0)
            _WhatToDo = substr(_XlsFileName,1,1);
            if ((_WhatToDo == "$") or (_WhatToDo == "#")or (_WhatToDo == "@")or (_WhatToDo == "^"))
            _XlsFileName = Substr(_xlsFileName,2);
            else
            _WhatToDo="";
            end;
            end;
            end;
            MakeFile(_XlsFileName, _zoom);
            if (_WhatToDo == "@")
            MsgBox("Файл "+filename + " перенесен на локал.");
            else
            openFile(_xlsFileName, _WhatToDo, _passw);
            end;
            end;

            AddSheet(aFirstSheetName);

            fileName=GetGUIDFileName;
            end;

            >> Ответить