procedure Exch2832_Build199_By_IK_UK( aUK_XML: Variant;
                                      aUK_XML_Index: integer;
                                      aBudgetId: string;
                                      aFinYear: string;
                                      aIK_LS_UBP: string;
                                      aCfg_ChangeStatusED199to10: boolean;
                                      aCfg_CreateMultiDoc: boolean);
var
  i,i2: integer;
  xIK_XML: Variant;
  xUK_LS_NUBP: string;
  xUK_DocNum: string;
  xUK_DocDate: string;
  xUK_GUID_FK: string;
  xUK_NAME_NUBP: string;
  xIK_INN: string;
  xIK_KPP: string;

  xLines_Amount: Variant;
  xDocInfo199: string;
  xDoc199: Variant;
  xXmlDoc199: Variant;
  xXmlOrg: Variant;
  xXmlGRBS: Variant;

  xDocXml: Variant;
  xDoc: Variant;
  xNodeUKPP: Variant;
  xNodeUKPP_N: Variant;

  xXmlDocLines: Variant;
  xXmlDocLine: Variant;

  xType_KBK: string;
  xKBK: string;

  xDocID: string;

  xXMLFound: integer;
  xUKPP_Found: boolean;
  xUKPP_LINE_NOM: string;
  xUKPP_KBK: string;
  xUKPP_ADD_CLASS: string;
  xUKPP_INN_PP: string;
  xUKPP_KPP_PP: string;
  xUKPP_NOM_PP: string;
  xUKPP_NOM_PP_199: string;
  xUKPP_DATE_PP: string;
  xUKPP_NAME_PP: string;
  xUKPP_SUM_PP: Currency;

  xKBK_KES: string;
  xKBK_Type: string;

  xIndustryCodes: TStringList;

  xOrgFound: string;
  xSQL: string;

  {---------------------------------------------------------------------------}
  {~DoMakeED199_
    формирование созданного эд - передача на сервер.}
  procedure DoMakeED199_;
  begin
  
      try
        // передаем эд только если он создан
        if VarIsNULL( xXmlDoc199) then EXIT;

        // если при формировании эд определено, что он уже есть в бд,
        // то такой эд не передаем на сервер и сообщений тоже не отображаем.
        if (LENGTH( GetNodeAttributeEx( xDoc199, '_FOUND_DOUBLE_')) <= 0) then begin
          // передаем документ на сервер, только если параметр проверки =false.

          // здесь создаем эд на статусе "черновик"
          //Exec_ChildDoc( aBudgetID, '', xDoc199, FALSE);
          // exchange #2902
          // проблема с экшеном link при создании эд 199го.
          // отправляется задание в АЦК-Ф. линковка не выполняется, поскольку
          // здесь созданный эд не доводится до нужного статуса.
          //ExecXml( xDoc199.xml, aBudgetID);
          // exchange #2945
          // добавлено изменение состояния эд если aCfg_ChangeStatusED199to10=true.
          // если aCfg_ChangeStatusED199to10=false, то блокируется изменение
          // статуса эд после создания.
          // это делается в подпрограмме Exec_ChildDoc с помощью дополнительных
          // параметров.
          if aCfg_ChangeStatusED199to10
            then Exec_ChildDoc( aBudgetID,
                                '',
                                xDoc199,
                                TRUE,
                                FALSE,
                                '')
            else Exec_ChildDoc( aBudgetID,
                                '',
                                xDoc199,
                                TRUE,
                                FALSE,
                                'no_change_status_199=1');

        end;
        // exchange #2945
        // после отправки эд 199го, начинаем создавать новые эд 199е
        xXmlDoc199 := NULL;
        except
          ExceptionProc( 'DoMakeED199_');
      end;
  
  end;
  {---------------------------------------------------------------------------}
  {~CheckDouble199_
    производим поиск ЭД 199 класса в списке ЭД системы
    (по Номеру, Дате, Номеру лицевого счета (UK.LS_NUBP).
    нужно учитывать бюджет загрузки IK.
    здесь так же проверяем гуид из UK.GUID_FK. это дает проверку
    загружен ли этот эд из файла ФК или создан вручную.
    при поиске учитывается включенный признак "От ФК".
    результат:
      количество найденных эд.
    exchange #2942
    вместо явной проверки guid_fk из поля файла,
    теперь проверяем значение guid_fk из параметра aGUID_FK.
    exchange #2912
      поправлено - вместо счета UK.LS_NUBP ищется по aIK_LS_UBP}
  function CheckDouble199_( aGUID_FK: string): integer;
  var
    Q: TRunQuery;
    xSQL: string;
    xXml: Variant;
  begin

      try
        Q:= TRunQuery.Create;
        try
          xSQL :=
                   ' Select A.* From Document D'
                  +' Left Join moneytransferorder A on A.Document_Id = D.Id'
                  +' Where D.Budget_Id = ? and D.DocumentClass_Id = 199'
                  // поиск только шапки ЭД
                  +' and (a.title_id = a.id or a.title_id is null)'
                  +' and LTrim(A.Doc_Number) = ? and A.Doc_Date = ?'
                  +' and A.PAY_ACCOUNT=? and A.FK_FLAG=1'
                  +' and A.dispstatus_id>-1';

          Q.AddStr( aBudgetId);
          Q.AddStr( xUK_DocNum);
          Q.AddDate( StrToDate( xUK_DocDate));
          Q.AddStr( aIK_LS_UBP{xUK_LS_NUBP});

          // если гуид не указан, в запросе нужно проверять не пустую строку
          // а проверку на is null.
          // exchange #2912
          // по этой записи поправлен запрос если строка guid_fk не указана.
          // здесь сравниваем и на null и на пустую строку.
          // в разных субд это работает по разному
          if (LENGTH( aGUID_FK) <= 0)
            then xSQL := xSQL + ' and (A.FOREIGN_KEY='#39#39
                              + ' or A.FOREIGN_KEY is null)'
            else begin
              xSQL := xSQL + ' and A.FOREIGN_KEY=?';
              Q.AddStr( aGUID_FK);
            end;

          xXml := CreateOleObject('Microsoft.XmlDom');
          xXml.LoadXml( Q.Run( xSQL));   // кеш не используем

          finally
            Q.Free;
        end;

        Result := xXML.documentElement.childNodes.Length;
        // Если ЭД однозначно найден - не загружаем повторно
        // exchange #2902
        // сообщения о повторной загрузке переведены в журнал info
        if (Result = 1)
          then WriteInfo( FORMAT( 'Повторная загрузка ЭД %s с GUID=%s - не обрабатывается.',
                                  [ xDocInfo199,
                                    aGUID_FK]))
          else
        // если найдено несколько ЭД, тогда записываем сообщение в лог
        // и переходим к следующему ЭД
        if (Result > 1)
          then WriteInfo( FORMAT( 'Найдено несколько ЭД %s с GUID=%s - не обрабатывается.',
                                  [ xDocInfo199,
                                    aGUID_FK]));
        except
          ExceptionLog( 'Сбой CheckDouble199_');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~DoCreateED199_
    exchange #2942
    создание эд 199го.
    эд описывается из текущей строки UKPP, в переменной xNodeUKPP.
    здесь же проверяется эд на дубли в бд.

   создаем один эд 199й на несколько разделов ukpp, которые имеют
   одно значение UKPP.NOM_PP.
   особенность разбора такова, что проверяются два соседних ukpp,
   а не группировка всех ukpp в одном uk. поэтому тут сверяем
   текущее значение UKPP.NOM_PP со значением xUKPP_NOM_PP_199,
   по которому создали эд 199й

    результат:
      =true - эд создан
      =false - не создан}
  function DoCreateED199_{( aCheckCfgMultiDoc: boolean = TRUE)}: boolean;
  var
    xGUID_FK: string;
    xFoundDouble: boolean;
  begin

      try
        if NOT aCfg_CreateMultiDoc then begin
          // если нужно создавать один эд 199й на все строки, проверяем,
          // если эд уже создан - заполняем его и новый не создаем.
          if NOT VarIsNULL( xXmlDoc199) then begin

            Result := TRUE;
            EXIT;
          end;
        end else begin
          // если эд создается на группу, проверяем, если эд еще не создан
          // создаем его, и фиксируем начало группы эд по полю UKPP.NOM_PP.
          if NOT VarIsNULL( xXmlDoc199) then begin
            if (xUKPP_NOM_PP_199 = xUKPP_NOM_PP) then begin

              Result := TRUE;
              EXIT;
            end;
            // если группа меняется, то предыдущий 199й отправляем
            // и создаем новую группу
            DoMakeED199_;
          end;
        end;



        Result := FALSE;

        // создаем один эд 199й на несколько разделов ukpp, которые имеют
        // одно значение UKPP.NOM_PP.
        xUKPP_NOM_PP_199 := xUKPP_NOM_PP;

        // уникальный guid_fk с учетом guid из файла + номер строки,
        // если по конфигу нужно создавать один эд на каждую строку UKPP.
        // если создается один эд на все строки, то проверяем дубля
        // выполнилась уже давно и повторно ее не выполняем, а только
        // определяем уникальный GUID для эд из поля UK.GUID_FK.
        if aCfg_CreateMultiDoc
          then xGUID_FK := xUK_GUID_FK + GetNodeAttributeEx( xNodeUKPP, 'LINE_NOM')
          else xGUID_FK := xUK_GUID_FK;
        // если найден эд, то приходится создавать эд, чтобы небыло сбоя
        // его заполнения, но выставляем флаг, что его передавать не нужно.
        xFoundDouble := (CheckDouble199_( xGUID_FK) <> 0);
        //if (CheckDouble199_( xGUID_FK) <> 0) then EXIT;

        // отображаем формирование эд, если только эд не найден в бд
        if NOT xFoundDouble
          then WriteInfo( FORMAT( 'ЭД %s с GUID=%s.', [ xDocInfo199, xGUID_FK]), TRUE);

        xXmlDoc199 := CreateOleObject('Microsoft.XmlDom');
        xDoc199 := xXmlDoc199.appendChild( xXmlDoc199.createElement('DOCUMENT'));
        xDoc199.setAttribute('F_BUDGET_ID', aBudgetID);
        xDoc199.setAttribute('F_ORIGINAL_TYPE', 'UK');
        xDoc199.setAttribute('action', 'insert');
        xDoc199.setAttribute('class', 199);

        xDoc199.setAttribute( 'FOREIGN_KEY', xGUID_FK);

        // признак "от ФК" - значение "включен"
        xDoc199.SetAttribute( 'FK_FLAG', '1');

        // Номер документа 	UK.NOM_UK
        xDoc199.setAttribute( 'DOC_NUMBER', xUK_DocNum);
        // Дата справки	UK.DATE_SP
        // в документе должна быть в формате YYYY.dd.mm
        xDoc199.setAttribute( 'DOC_DATE',
  //                            DDdMMdYYYY_To_YYYYmMMmDD( xUK_DocDate));
                              DateTimeToXml( StrToDate( xUK_DocDate)));
        // тип операции: По-умолчанию заполняется значением, выбранным
        // для Справки по операциям БУ/АУ в справочнике «Типы операций
        // по-умолчанию», а в случае отсутствия настройки, значением
        // «Не указано»
        xDoc199.setAttribute( 'OPERTYPE_ID', OperTypeDefaultForClass( 199));
        // Заполнение блока "Учреждение"
        xDoc199.setAttribute( 'PAY_ID'      , xXmlOrg.getAttribute('ID'));
        xDoc199.setAttribute( 'PAY_TAXCODE' , xXmlOrg.getAttribute('TAXCODE'));
        xDoc199.setAttribute( 'PAY_KPP'     , xXmlOrg.getAttribute('KPP'));
        xDoc199.setAttribute( 'PAY_NAME'    , xXmlOrg.getAttribute('DESCRIPTION'));

        // счет
        // exchange #2959
        // заполнять значением поля «Счет», равным значению IK.LS_UBP
        // вместо UK.LS_NUBP
        xDoc199.setAttribute( 'PAY_ACC_ID'  ,
                              QueryField( ' select oa.ID'
                                         +' from ORGACCOUNT OA where OA.ORG_ID=?'
  
                                         +' and OA.ACCOUNT_NUMBER = ? and'
                                         +' OA.CLOSE_DATE is null',
                                          [ xXmlOrg.getAttribute('ID'),
                                            aIK_LS_UBP {xUK_LS_NUBP}],
                                          'ID'));
                              //xXmlOrg.getAttribute('ACCOUNT_ID'));
        xDoc199.setAttribute( 'PAY_ACCOUNT' , aIK_LS_UBP{xUK_LS_NUBP});

        // учредитель
        xDoc199.setAttribute( 'GRBS_ID'     , xXmlGRBS.getAttribute('ID'));
        xDoc199.setAttribute( 'GRBS_NAME'   , xXmlGRBS.getAttribute('DESCRIPTION'));

        // раздел "платежный документ" заполняется из текущей строки UKPP,
        // в переменной xNodeUKPP
        // номер
        xDoc199.SetAttribute(
                    'PAYDOC_DOC_NUMBER',
                    VarToStr( xNodeUKPP.GetAttribute( 'NOM_PP')));
        // дата в файле в формате "DD.MM.YYYY"
        xUKPP_DATE_PP := VarToStr( xNodeUKPP.GetAttribute( 'DATE_PP'));
        if (LENGTH( xUKPP_DATE_PP) > 0)
          then xDoc199.SetAttribute(
                            'PAYDOC_DOC_DATE',
                            DDdMMdYYYY_To_YYYYmMMmDD( xUKPP_DATE_PP));
        // наименование
        xDoc199.SetAttribute(
                    'PAYDOC_NAME',
                    VarToStr( xNodeUKPP.GetAttribute( 'NAME_PP')));
        // основание
        xDoc199.SetAttribute(
                    'DESCRIPTION',
                    VarToStr( xNodeUKPP.GetAttribute( 'PURPOSE')));

        // комментарий в шапке - не заполняем
        xDoc199.SetAttribute( 'REMARK', '');

        // выставляем флаг, если эд найден в бд
        // здесь важно чтобы значение атрибута было не пустым и не важно
        // чем заполнено.
        if xFoundDouble
          then xDoc199.SetAttribute( '_FOUND_DOUBLE_', 'TRUE');

        // формируем строки
        xXmlDocLines:= xDoc199.appendChild( xXmlDoc199.createElement('LINES'));

        Result := TRUE;
        except
          ExceptionProc( 'DoCreateED199_');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~Do_Check_KBK_
    определение КОСГУ (по КБК) и тип КБК по типу КОСГУ.
    exchange #2912
    поскольку эта же функция вызывается, если строка формируется по строке
    найденного эд (ФТ-5.1.1), то если параметр aSection содержит пустую строку,
    значит кбк определяется не по разделу UKPP/UKPP_N а по строке эд с
    номером aLineNom.}
  procedure Do_Check_KBK_( aKBK, aSection, aLineNom: string;
                           aCheckKESmsg: boolean = TRUE);
  begin

      try
        // расширяем код до 20 символов
        // поправлено в #2912 - дозаполнение нулями слева
        // exchange #3062 - унификация кода
        aKBK := KBKExtend( aKBK);
        xKBK_KES := DoEncodeKBK_KES( aKBK, TRUE);
        ValidateKBK_KES( xKBK_KES, aBudgetId, TRUE, TRUE);
        {
        while (LENGTH( aKBK) < 20) do aKBK := '0' + aKBK;
        // проверяем КОСГУ - последние 3 символа строки кбк
        xKBK_KES := COPY( aKBK, 18, 3);
        // если не найден в бд - заменяем на 000 (не указан)
        if NOT KbkExists( aBudgetId, xKBK_KES, 'Kes')
          then xKBK_KES := '000';
          }
        // определяем тип КБК
        // по согласованию с АО, если КОСГУ=000 или тип КОСГУ=НЕТ,
        // формируем доходную строку.
        if KBK_is_0( xKBK_KES)
          then xKBK_Type := '1'
          else xKBK_Type := QueryField(  ' Select ANAL_KIND X From kes'
                                        +' Where Budget_Id = ? and Code = ?',
                                         [ aBudgetId,
                                           xKBK_KES],
                                         'X');
        // exchange #2907
        // теперь допускаются строки по источникам
        if NOT (xKBK_Type in [ '1','2','3']) then begin
          // для типа косгу = 0, всегда подставляется =1.
          // сообщение выдаем только если есть разрешение для этого.
          // сообщение не всегда нужно отображать, посколкьку иногда
          // выполняется проверка из разных ФТ, вложенных друг в друга.
          if aCheckKESmsg then
            if (LENGTH( aSection) > 0)
              then WriteInfo(
                      FORMAT( 'ЭД %s в строке %s.LINE_NOM %s тип КОСГУ не определен - формируется доходная строка.',
                              [ xDocInfo199,
                                aSection,
                                aLineNom]))
              else WriteInfo(
                      FORMAT( 'ЭД %s в строке %s найденного ЭД тип КОСГУ не определен - формируется доходная строка.',
                              [ xDocInfo199,
                                aLineNom]));
          xKBK_Type := '1';   // если тип не определен - то тип кбк =доходы
        end;

        except
          ExceptionLog( 'Сбой Do_Check_KBK_');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~DoSearch_INDUSTRYCODE_System_
    поиск нулевого отр.кода.
    возвращает ID нулевого отр.кода}
  function DoSearch_INDUSTRYCODE_System_: string;
  var
    xXml: Variant;
    xRootXml: Variant;
  begin

      try
        // системный отр.код - по умолчанию
        // если код не будет найден - возвращается пустое значение
        xXml :=  QueryXml( ' select g.id from INDUSTRYCODE g'
                          +' where g.budget_id=? and g.code=?',
                           [ aBudgetID,
                             FormatKbk( 'INDUSTRYCODE', '0', aBudgetID)]);
        try
          xRootXml := xXml.documentElement;
          if (xRootXml.childNodes.Length = 1)
            then Result := xRootXml.childNodes.Item[0].GetAttribute('ID')
            else begin
              Result := '0';
              WriteInfo( FORMAT( 'Не найден нулевой отраслевой код для бюджета %s.',
                                 [ aBudgetID]));
            end;
          finally
            xXml.LoadXml( '');
        end;

        except
          ExceptionLog( 'Сбой Un_ImpIK_2832_UK.DoSearch_INDUSTRYCODE_System_');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~DoSearchOtrKod_
    Заполнять значением поля «Отраслевой код» строки справочника
    «Правила соответствия классификаторов БУ/АУ», отвечающей следующим условиям:
      1.	Значение поля «Лицевой счет учреждения» либо указанная в нем маска
          должны соответствовать значению UK.LS_NUBP;
      2.	Значение поля «КВФО» должно быть равно по значению полю «КВФО»
          формируемой бюджетной строки;
      3.	Значение поля «Тип субсидии» должно соответствовать значению поля
          «Тип субсидии» строки справочника «Коды субсидий», код которой указан
          в формируемой строке ЭД.
    Если отраслевой код определить не удалось, то заполнять нулевым
    значением (системным «не указан»).

    параметры:
      aFSR_ID - КВФО из строки
      aGRANTINVESTMENT_ID - ид.кода субсидии}
  function DoSearch_INDUSTRYCODE_( aGRANTINVESTMENT_ID, aFSR_ID: string): string;
  var
    xXml: Variant;
    xRootXml: Variant;
    xNodeXml: Variant;
    i,j: integer;
    xFoundCount: integer;
    xIndustryCode: string;
    xIndustryCodeID: string;
    xFoundIndustryCodeID: string;
    xLSMaska: string;
    xIndustryMaska: string;
    xFound: boolean;
    xGIType: string;
  begin

      try
        // системный отр.код - по умолчанию
        Result := DoSearch_INDUSTRYCODE_System_;

        xIndustryCodes.Clear;    // еще ничего не нашли

        // ищем тип кода субсидий
        xGIType := QueryField( ' select FTYPE from GRANTINVESTMENT'
                              +' where id=?',
                                [ aGRANTINVESTMENT_ID],
                                'FTYPE');

        // здесь "тип субсидии" привязываем к типу кода субсидий.
        // exchange #2872
        // добавлена привязка к бюджету
        xXml :=  QueryXml( ' Select Accountnumber_Mask, Industrycode_Mask'
                          +' From AubuclassifiersRules'
                          +' where GrantinvestmentType = ? and Fsr_Id=?'
                          +' and budget_id=?',
                            [ xGIType,
                              aFSR_ID,
                              aBudgetID]);
        try
          xRootXml := xXml.documentElement;
          // здесь ищем все маски отр кодов в соответствии с л/с.
          // причем игнорируем одинаковые маски отр.кодов
          for i := 0 to xRootXml.childNodes.Length - 1 do begin
            xNodeXml      := xRootXml.childNodes.Item[i];
            xLSMaska      := VarToStr( xNodeXml.getAttribute('ACCOUNTNUMBER_MASK'));
            xIndustryMaska:= VarToStr( xNodeXml.getAttribute('INDUSTRYCODE_MASK'));
            // проверяем маску на идентичность счета
            if (xLSMaska = xUK_LS_NUBP) then begin

              if (xIndustryCodes.IndexOf( xIndustryMaska) = -1)
                then xIndustryCodes.Add( xIndustryMaska);
            end else
              // проверяем л/с на совпадение по маске
              if MatchesMask( xUK_LS_NUBP, xLSMaska) then begin

                if (xIndustryCodes.IndexOf( xIndustryMaska) = -1)
                  then xIndustryCodes.Add( xIndustryMaska);
              end;
          end;//for//
          finally
            xXml.LoadXml( '');
        end;


        // теперь пробуем понять, что из найденных масок есть в справочнике
        // отр. кодов
        if (xIndustryCodes.Count > 0) then begin
          // пока считаем что ничего не нашли
          xFoundCount          := 0;
          xFoundIndustryCodeID := '';
          // получаем список отр.кодов для бюджета из справочника
          xXml :=  QueryXml( ' select g.id, g.code from INDUSTRYCODE g'
                            +' where g.budget_id=?',
                             [ aBudgetID]);
          try
            // пробуем найти отр.код, соответствующий найденным маскам
            // из правил
            xRootXml := xXml.documentElement;
            for i := 0 to xIndustryCodes.Count - 1 do begin
              xIndustryMaska := xIndustryCodes.Strings[i];
              for j := 0 to xRootXml.childNodes.Length - 1 do begin
                xNodeXml        := xRootXml.childNodes.Item[j];
                xIndustryCodeID := VarToStr( xNodeXml.GetAttribute('ID'));
                xIndustryCode   := VarToStr( xNodeXml.GetAttribute('CODE'));

                // проверяем маску на идентичность отр.кода
                if (xIndustryMaska = xIndustryCode) then begin
                  xFound := TRUE;

                end else
                  // проверяем отр.код на совпадение по маске
                  if MatchesMask( xIndustryCode, xIndustryMaska) then begin
                    xFound := TRUE;

                  end else
                    xFound := FALSE;

                // здесь проверяем, если это один и тот же отр код,
                // тогда его за найденный не считаем.
                // такое может быть, если есть разные маски, которые
                // удовлетворяют одному и тому же отрг.коду.
                if xFound then
                  if (xFoundIndustryCodeID <> xIndustryCodeID) then begin
                    INC( xFoundCount);
                    if (xFoundCount > 1) then begin

                      // найдено несколько отр кодов - заполняем отр. код нулевым
                      EXIT;
                    end;
                    xFoundIndustryCodeID := xIndustryCodeID;
                  end;
              end;//for//
            end;//for//
            finally
              xXml.LoadXml( '');
          end;
          // если найден единственный код - возвращаем его как результат.
          // проверка нужна на случай если не найдено ни одного отр.кода.
          if (xFoundCount = 1)
            then Result := xFoundIndustryCodeID;
        end;
        except
          ExceptionLog( 'Сбой DoSearch_INDUSTRYCODE_');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~DoSearch_GRANTINVESTMENT_
    определение кода субсидии по полю кода aADD_CLASS.
    Если значение не найдено, то заполнять нулевым значением (Не указан).
    для нулевого кода субсидии в АЦК-Ф указывается тип=3
    возвращается:
      идентификатор найденного кода.}
  function DoSearch_GRANTINVESTMENT_( aADD_CLASS: string): string;
  var
    xCode: string;
  begin

      try
        // код субсидий - Значением поля «Код» строки справочника
        // «Коды субсидий» равным значению  aADD_KLASS.
        // Если значение не найдено, то заполнять нулевым значением (Не указан).
        // для нулевого кода в АЦК-Ф указывается тип=3
        if (LENGTH( aADD_CLASS) <= 0)
          then xCode := FormatKbk( 'GRANTINVESTMENT', '0', aBudgetID)
          else
            if (QueryField(  ' select count( * ) N from GRANTINVESTMENT'
                            +' where code=? and BUDGET_ID=?',
                             [ aADD_CLASS,
                               aBudgetId],
                             'N') <> '1')
              then xCode := FormatKbk( 'GRANTINVESTMENT', '0', aBudgetID)
              else xCode := aADD_CLASS;
        // ищем ID найденного кода
        Result := QueryField(  ' select ID from GRANTINVESTMENT'
                              +' where code=? and BUDGET_ID=?',
                               [ xCode,
                                 aBudgetId],
                               'ID');
        except
          ExceptionLog( 'Сбой DoSearch_GRANTINVESTMENT_');
      end;

  end;
  //----------------------------------------------------------------------------
  {~Do_Search_UKPP_
    поиск раздела UKPP с номером aLINE_NOM}
  function Do_Search_UKPP_( aLINE_NOM: string): Variant;
  var
    j: integer;
    xXmlNode_N: Variant;
  begin

      try
        Result := NULL;
        for j := 0 to aUK_XML.childNodes.Length - 1 do begin
          xXmlNode_N := aUK_XML.childNodes.Item[j];
          if ( xXmlNode_N.NodeName = 'UKPP') then
            if ( xXmlNode_N.GetAttribute( 'LINE_NOM') = aLINE_NOM) then begin

              Result := xXmlNode_N;
              BREAK;
            end;
        end;//for//
        except
          ExceptionLog( 'Сбой Do_Search_UKPP_');
      end;

  end;
  //----------------------------------------------------------------------------
  {~GetDirectionKES_
    возвращает поле "направление" для кода КОСГУ в переменной xKBK_KES.}
  function GetDirectionKES_: string;
  begin

      try
        Result := QueryField(  ' Select DIRECTION X From kes'
                              +' Where Budget_Id = ? and Code = ?',
                               [ aBudgetId,
                                 xKBK_KES],
                               'X');
        except
          ExceptionLog( 'Сбой GetDirectionKES_');
      end;

  end;
  //----------------------------------------------------------------------------
  {~Do_Build_Lines_From_UKPP_N_
    создание строк в ЭД по блокам UKPP_N, для которых:
      1) UKPP_N.LINE_NOM = UKPP.LINE_NOM (текущий номер блока UKPP).
    выполняется по ФТ-5.2 или по ФТ-5.3 пп.2.
    описание строк определяется по таблице 3.

    exchange #2709
    то поле "Направление операции" нужно заполнять в зависимости от
    класса уточняемого эд.
    добавлен параметр:
      aDetailClass
          если значение пустое, то поле "Направление операции" не заполняется.
          если значение не пустое, то по нему заполняется поле "Направление
            операции".
      aDetailLine - заполняется, если строка формируется из строки ЭД.
          в остальных случаях =NULL.
      aED_UKPP_Line - это строка в создаваемом ЭД 199го класса,
          созданная по UKPP, и по которой формируется
          строка в эд по блоку UKPP_N.
          если строка в эд формируется только по строке UKPP_N, то значение
          этого параметра должно быть =NULL.}
  procedure Do_Build_Lines_From_UKPP_N_( aDetailClass: string = '';
//                                         aDetailLine: Variant = NULL;
                                         aED_UKPP_Line: Variant = NULL);
  var
    j: integer;
    xXmlNode_N: Variant;
    xUKPP_N_LINE_NOM: string;
    xUKPP_N_KBK: string;
    xUKPP_N_ADD_CLASS: string;
    xUKPP_N_SUM: currency;
  begin

      try
        for j := 0 to aUK_XML.childNodes.Length - 1 do begin
          xXmlNode_N := aUK_XML.childNodes.Item[j];
          if (xXmlNode_N.NodeName <> 'UKPP_N') then CONTINUE;

          // создаем строки в ЭД только если UKPP_N.LINE_NOM = UKPP.LINE_NOM
          xUKPP_N_LINE_NOM  := VarToStr( xXmlNode_N.GetAttribute( 'LINE_NOM'));
          if (xUKPP_N_LINE_NOM <> xUKPP_LINE_NOM) then CONTINUE;


          // параметры строки
          xUKPP_N_KBK       := VarToStr( xXmlNode_N.GetAttribute( 'KBK'));
          xUKPP_N_ADD_CLASS := VarToStr( xXmlNode_N.GetAttribute( 'ADD_KLASS'));
          xUKPP_N_SUM       := StrToCurrDef(
                                  VarToStr( xXmlNode_N.GetAttribute( 'SUM')), 0);

          // создаем строку в ЭД
          xXmlDocLine := xXmlDocLines.appendChild(
                                            xXmlDoc199.createElement('LINE'));


          // примечание
          xXmlDocLine.SetAttribute( 'NOTE',
                                    VarToStr( xXmlNode_N.GetAttribute( 'NOTE')));
          // сумма
          xXmlDocLine.SetAttribute( 'AMOUNT',
                                    FORMAT( '%0.2f', [ xUKPP_N_SUM]));

          // разбираем КБК
          //Если значение UKPP_N.KBK пустое и первые два знака значения
          // UK.LS_NUBP равны «30», то заполнять поле значением технического
          // экономического кода «888». Иначе заполнять нулевым значением
          // (Не указан)
          // exchange #2912
          // проверять счет нужно теперь IK.LS_UBP, а не UK.LS_NUBP
          if ((LENGTH( xUKPP_N_KBK) <= 0) AND (COPY( aIK_LS_UBP, 1, 2) = '30'))
            then xUKPP_N_KBK := '00000000000000000888';
          Do_Check_KBK_( xUKPP_N_KBK, 'UKPP_N', xUKPP_N_LINE_NOM);

          // вид классификации
          xXmlDocLine.SetAttribute( 'CR_ANAL_KIND', xKBK_Type);

          // КОСГУ
          xXmlDocLine.SetAttribute( 'KESR_CODE', xKBK_KES);
          // КВФО - Если первые два знака значения UK.LS_NUBP равно «21»
          // или «31», то «9», в остальных случаях «8».
          // exchange #2912
          // проверять счет нужно теперь IK.LS_UBP, а не UK.LS_NUBP
          if (COPY( aIK_LS_UBP, 1, 2) in ['21','31'])
            then xXmlDocLine.SetAttribute( 'FSR_ID', '9')
            else xXmlDocLine.SetAttribute( 'FSR_ID', '8');

{
          // направление операции - зависит от типа КБК
          case xKBK_Type of
            // если доходы то присваивать полю значение «поступления»
            '1': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '2');
            // «Расходы», то присваивать полю значение «выплаты»
            '2': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '1');
          else showmessage( 'требуется уточнить, что подставлять в направление операции'#13#10
                            +'для строки с типом "источники" в UKPP_N (exchange #2832).');
          end;//case//}
          { exchange #2907
            Если вид классификации имеет значение:
            - «Доходы» И
                1. уточняемый ЭД имеет класс:
                  193 – присваивать полю значение «Поступление»;
                  195 или 194 – присваивать полю значение «Возврат поступлений»;
                  199 – если в уточняемой строке вид классификации равен «Доходы»,
                        то значение поля брать из уточняемой строки,
                        иначе присваивать значение «Поступление».
                2. уточняемая строка сформирована не из документа и имеет вид
                    классификации «Доходы», то брать значение из уточняемой
                    строки, иначе присваивать значение «Поступление».
                3. нет уточняемого документа, нет уточняемой строки,
                    присваивать значение «Поступление».

            - «Расходы» И
                1. уточняемый ЭД имеет класс:
                  193 – присваивать полю значение «Восстановление выплат»;
                  195 или 194 – присваивать полю значение «Выплаты»;
                  199 – если в уточняемой строке вид классификации равен:
                     «Расходы», то значение поля брать из уточняемой строки,
                     «Доходы», то присваивать значение «Восстановление выплат»;
                     иначе присваивать значение «Выплаты».
                2. уточняемая строка сформирована не из документа и имеет вид
                    классификации «Расходы», то брать значение из уточняемой строки,
                    «Доходы», то присваивать значение «Восстановление выплат»;
                    иначе присваивать значение «Выплаты».
                3. нет уточняемого документа, нет уточняемой строки,
                    присваивать значение «Выплаты».

            - «Источники» И
               если значение признака «Направление» в строке справочника
               «Экономические классификаторы» со значением поля «Код»,
               указанным в поле «КОСГУ» формируемой строки, имеет значение:
                - «Погашение», то указать значение «Выплаты»;
                - «Привлечение», то то указать значение «Поступления»;}

          case xKBK_Type of
            // если доходы
            '1': case aDetailClass of
                   '193': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '2'); //«поступления»
                   '194',
                   '195': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '4'); // «возврат поступлений»
                          // если строка в эд не создана из уточняемой строки
                          // UKPP, то направление операции = «поступления»
                   '199': if VarIsNULL( aED_UKPP_Line)
                            then xXmlDocLine.SetAttribute( 'CR_DIRECTION', '2') //«поступления»
                            else
                              // проверяем, если в 199м создана строка по UKPP
                              // и тип строки = доходы, то поле направление
                              // операции берем из этой строки.
                              if (aED_UKPP_Line.GetAttribute( 'CR_ANAL_KIND') = '1')
                                then xXmlDocLine.SetAttribute(
                                        'CR_DIRECTION',
                                        aED_UKPP_Line.GetAttribute( 'CR_DIRECTION'))
                                else xXmlDocLine.SetAttribute( 'CR_DIRECTION', '2'); //«поступления»
                   // если уточненная строка формируется не из документа
                   else xXmlDocLine.SetAttribute( 'CR_DIRECTION', '2'); //«поступления»
                 end;//case//

            // «Расходы»
            '2': case aDetailClass of
                   '193': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '3');  //«восстановление выплат»
                   '194',
                   '195': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '1'); // «выплаты»
                          // если строка в эд не создано из уточняемой строки
                          // UKPP, то направление операции = «поступления»
                   '199': if VarIsNULL( aED_UKPP_Line)
                            then xXmlDocLine.SetAttribute( 'CR_DIRECTION', '1') // «выплаты»
                            else
                              // проверяем, если в 199м создана строка по UKPP
                              case aED_UKPP_Line.GetAttribute( 'CR_ANAL_KIND') of
                                // доходы
                                '1': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '3');  //«восстановление выплат»
                                // расходы, то поле направление операции берем
                                // из этой строки.
                                '2': xXmlDocLine.SetAttribute(
                                        'CR_DIRECTION',
                                        aED_UKPP_Line.GetAttribute( 'CR_DIRECTION'));
                                // прочее
                                else xXmlDocLine.SetAttribute( 'CR_DIRECTION', '1'); // «выплаты»
                              end;//case//
                   // если уточненная строка формируется не из документа
                   else xXmlDocLine.SetAttribute( 'CR_DIRECTION', '1'); // «выплаты»
                 end;//case//

            // источники - проверяем "направление" в коде КОСГУ
            '3': case GetDirectionKES_ of
                  // привлечение
                  '1': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '2');  //«поступления»
                  // погашение
                  '2': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '1'); // «выплаты»
                end;//case//
          end;//case//}


          // код субсидий - Значением поля «Код» строки справочника
          // «Коды субсидий» равным значению  UKPP.ADD_KLASS.
          // Если значение не найдено, то заполнять нулевым значением (Не указан).
          // для нулевого кода в АЦК-Ф указывается тип=3
          xXmlDocLine.SetAttribute(
                            'GRANTINVESTMENT_ID',
                            DoSearch_GRANTINVESTMENT_( xUKPP_N_ADD_CLASS));

          // отраслевой код
          xXmlDocLine.SetAttribute(
                            'INDUSTRYCODE_ID',
                            DoSearch_INDUSTRYCODE_(
                                xXmlDocLine.GetAttribute( 'GRANTINVESTMENT_ID'),
                                xXmlDocLine.GetAttribute( 'FSR_ID')));

          // exchange #2962
          // допускается несколько строк UKPP_N на одну строку UKPP
          // поэтому здесь перебираем все строки UKPP_N которые соответствуют
          // номеру UKPP.LINE_NOM.
//          BREAK;
        end;//for//
        except
          ExceptionLog( 'Сбой Do_Build_Lines_From_UKPP_N_');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~Do_Build_Line_From_UKPP_
    создание строки в ЭД 199го класса с частичным заполнением из UKPP.
    кбк (косгу) уже должно быть разобрано и тип кбк
    заполняется:
      примечание - из UKPP.NOTE
      сумма - из параметра aSumma всегда с отрицательным значением
      вид классификации и КОСГУ - из разобранного кбк

    exchange #2709
    то поле "Направление операции" нужно заполнять в зависимости от
    класса уточняемого эд.
    добавлен параметр:
      aDetailClass
          если значение пустое, то поле "Направление операции" не заполняется.
          если значение не пустое, то по нему заполняется поле "Направление
            операции".
      aDetailLine - заполняется, если строка формируется из строки ЭД.
          в остальных случаях =NULL.}
  procedure Do_Build_Line_From_UKPP_( aSumma: Currency;
                                      aDetailClass: string = '';
                                      aDetailLine: Variant = NULL);
  var
    xFill_CR_DIRECTION_ByClass: boolean;
  begin

      try
        xXmlDocLine := xXmlDocLines.appendChild(
                                          xXmlDoc199.createElement('LINE'));

        // примечание - из UKPP.NOTE
        xXmlDocLine.SetAttribute( 'NOTE',
                                  VarToStr( xNodeUKPP.GetAttribute( 'NOTE')));
        // сумма - из параметра aSumma
        // меняем знак, на случай, если исходная сумма отрицательная
        xXmlDocLine.SetAttribute( 'AMOUNT',
                                  FORMAT( '%0.2f', [ 0 - aSumma]));

        // КБК уже должен быть разобран
        // вид классификации
        xXmlDocLine.SetAttribute( 'CR_ANAL_KIND', xKBK_Type);

        // КОСГУ
        xXmlDocLine.SetAttribute( 'KESR_CODE', xKBK_KES);

        // направление операции - зависит от типа КБК
{        case xKBK_Type of
          // если доходы то присваивать полю значение «возврат поступлений»
          '1': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '4');
          // «Расходы», то присваивать полю значение «восстановление выплат»
          '2': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '3');
        end;//case//}
        { exchange #2907
          если найден уточняемый документ и строки формируются по ФТ-5.1,
          то поле направление операции заполняется так:
          Если вид классификации имеет значение:
          - «Доходы» И Уточняемый документ имеет класс:
              193, то полю присваивать значение «Поступления»;
              195 или 194, то полю присваивать значение «Возврат поступлений»;
              199, то полю присваивать значение одноименного поля строки
                   уточняемого ЭД.
          - «Расходы» И  Уточняемый документ имеет класс:
              193, то полю присваивать значение «Восстановление выплат»;
              195 или 194, то полю присваивать значение «Выплаты»;
              199, то полю присваивать значение одноименного поля строки
                  уточняемого ЭД.
          - «Источники» И Уточняемый документ имеет класс:
              193: если значение признака «Направление» в строке справочника
                    «Экономические классификаторы» со значением поля «Код»,
                    указанным в поле «КОСГУ» формируемой строки, имеет значение:
                    - «Погашение», то указать значение «Восстановление выплат»;
                    - «Привлечение», то то указать значение «Поступление»;
              195 или 194: если значение признака «Направление» в строке
                    справочника «Экономические классификаторы» со значением
                    поля «Код», указанным в поле «КОСГУ» формируемой строки,
                    имеет значение:
                    - «Погашение», то указать значение «Выплаты»;
                    - «Привлечение», то то указать значение «Возврат поступлений»;
              199, то полю присваивать значение одноименного поля строки
                    уточняемого ЭД.
          Иначе поле оставлять пустым.

          если уточняемый эд не определен, либо для эд не найдено строк,
          и строки формируются по ФТ-5.2 и ФТ-5.3, тогда направление операции
          заполняется так:
            Если вид классификации имеет значение:
            - «Доходы», то присваивать полю значение «Поступления»;
            - «Расходы», то присваивать полю значение «Выплаты».
            - «Источники», то присваивать полю значение:
                «Выплаты», если значение признака «Направление» в строке
                  справочника «Экономические классификаторы» со значением поля
                  «Код», указанным в поле «КОСГУ» формируемой строки,
                  имеет значение «Погашение»;
                «Поступления» -   если значение признака «Направление» в строке
                  справочника «Экономические классификаторы» со значением поля
                  «Код», указанным в поле «КОСГУ» формируемой строки, имеет
                  значение «Привлечение»;
            Иначе поле оставлять пустым.

          для случая, когда строки формируются по найденному эд 199го класса,
          но в не по строкам эд, работает правило заполнения как если эд не
          найден (по ФТ-5.2 и ФТ-5.3).}
        if (LENGTH( aDetailClass) <= 0)
          then xFill_CR_DIRECTION_ByClass := FALSE
          else
            case aDetailClass of
              '199': if VarIsNULL( aDetailLine)
                        then xFill_CR_DIRECTION_ByClass := FALSE
                        else xFill_CR_DIRECTION_ByClass := TRUE;
              else xFill_CR_DIRECTION_ByClass := TRUE;
            end;

        if xFill_CR_DIRECTION_ByClass
          then
            case xKBK_Type of
              // если доходы
              '1': case aDetailClass of
                     '193': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '2');  //«поступления»
                     '194',
                     '195': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '4'); // «возврат поступлений»
                     '199': xXmlDocLine.SetAttribute(
                                    'CR_DIRECTION',
                                    VarToStr(
                                      aDetailLine.GetAttribute( 'CR_DIRECTION')));
                   end;//case//

              // если «Расходы»
              '2': case aDetailClass of
                     '193': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '3');  //«восстановление выплат»
                     '194',
                     '195': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '1'); // «выплаты»
                     '199': xXmlDocLine.SetAttribute(
                                    'CR_DIRECTION',
                                    VarToStr(
                                      aDetailLine.GetAttribute( 'CR_DIRECTION')));
                   end;//case//

              // если источники
              '3': case aDetailClass of
                    // проверяем "направление" в коде КОСГУ
                    '193': case GetDirectionKES_ of
                             // привлечение
                             '1': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '2');  //«поступления»
                             // погашение
                             '2': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '3');  //«восстановление выплат»
                           end;//case//

                    // проверяем "направление" в коде КОСГУ
                    '194',
                    '195': case GetDirectionKES_ of
                             // привлечение
                             '1': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '4'); // «возврат поступлений»
                             // погашение
                             '2': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '1'); // «выплаты»
                           end;//case//

                    // берем из строки ЭД
                    '199':  xXmlDocLine.SetAttribute(
                                    'CR_DIRECTION',
                                    VarToStr(
                                      aDetailLine.GetAttribute( 'CR_DIRECTION')));
                   end;//case//
            end//case//
          else
            case xKBK_Type of
              // если доходы то присваивать полю значение «поступления»
              '1': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '2');
              // «Расходы», то присваивать полю значение «выплаты»
              '2': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '1');
              // источники - проверяем "направление" в коде КОСГУ
              '3': case GetDirectionKES_ of
                     // привлечение
                     '1': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '2'); // «поступления»
                     // погашение
                     '2': xXmlDocLine.SetAttribute( 'CR_DIRECTION', '1'); // «выплаты»
                   end;//CASE//
            end;//case//

        except
          ExceptionLog( 'Сбой Do_Build_Line_From_UKPP_');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~Do_FT_5_3
    создание строки в ЭД 199м по UKPP
    exchange #2907
    добавлен номер класса, если строка создается на основании найденного эд.}
  procedure Do_FT_5_3( aDocClass: string = '');
  begin

      try
        // разбираем КБК
        Do_Check_KBK_( xUKPP_KBK, 'UKPP', xUKPP_LINE_NOM);

        // формируем строку из UKPP
        // сумма - отрицательная сумма из UKPP
        // exchange #2907
        // если уточняемый эд не определен, то поле "Направление операции"
        // заполняется не по уточняемому эд, поэтому клас эд не указываем.
        Do_Build_Line_From_UKPP_( xUKPP_SUM_PP);

        // КВФО - Если первые два знака значения UK.LS_NUBP равно «21»
        // или «31», то «9», в остальных случаях «8».
        if (COPY( xUK_LS_NUBP, 1, 2) in ['21','31'])
          then xXmlDocLine.SetAttribute( 'FSR_ID', '9')
          else xXmlDocLine.SetAttribute( 'FSR_ID', '8');

        // код субсидий - Значением поля «Код» строки справочника
        // «Коды субсидий» равным значению  UKPP.ADD_KLASS.
        // Если значение не найдено, то заполнять нулевым значением (Не указан).
        // для нулевого кода в АЦК-Ф указывается тип=3
        xXmlDocLine.SetAttribute(
                          'GRANTINVESTMENT_ID',
                          DoSearch_GRANTINVESTMENT_( xUKPP_ADD_CLASS));

        // отраслевой код
        xXmlDocLine.SetAttribute(
                          'INDUSTRYCODE_ID',
                          DoSearch_INDUSTRYCODE_(
                              xXmlDocLine.GetAttribute( 'GRANTINVESTMENT_ID'),
                              xXmlDocLine.GetAttribute( 'FSR_ID')));

        // формирование строк из разделов UKPP_N
        // по текущей строке UKPP.LINE_NOM
        // exchange #2907
        // строка создана не по уточненному эд, а может и по уточненному
        Do_Build_Lines_From_UKPP_N_( aDocClass, xXmlDocLine);


        except
          ExceptionLog( 'Сбой Do_FT_5_3');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~Do_FT_2_1
    поиск ЭД в списке ЭД класса 195 требуется искать документ с полями:
     «Номер документа», «Дата документа», «ИНН» и «КПП» блока получателя,
     равными по значению UKPP.NOM_PP, UKPP.DATA_PP, UKPP.INN_PP, UKPP.KPP_PP
     соответственно;
    И полем «Вид документа в ФК» равным по значению UKPP.NAME_PP;
    И значением поля «Счет» блока «Плательщик» равным по значению UK.LS_NUBP;
    И в статусе «Обработан».
    по ТФ-2.3 вызывается этот же поиск с кодом PP.
    для этого случая инн+кпп не должен использоваться.
    для этого указывается в параметре aWithoutINN_KPP=true. для остальных
    случаев, когда инн+кпп должны участвовать в поиске, значение
    параметра aWithoutINN_KPP=false.}
  procedure Do_FT_2_1( aPAYDOC_CODE: string;
                       aWithoutINN_KPP: boolean = FALSE);
  var
    Q: TRunQuery;
    xSQL: string;
  begin

      try
        Q:= TRunQuery.Create;
        try
          xSQL :=
                   ' Select A.* From aubucashrequest A'
                  +' Join Document D on A.Document_Id = D.Id'
                  +' Where D.Budget_Id = ? and D.DocumentClass_Id = 195'
                  +' and LTrim(A.Doc_Number) = ? and A.Doc_Date = ?'
                  +' and A.PAYDOC_CODE=?'
                  +' and a.PAY_ACCOUNT = ?'
                  +' and A.dispstatus_id=10'
                  +' and (a.title_id is null or a.title_id=a.id)';
          Q.AddStr( aBudgetId);
          Q.AddStr( xUKPP_NOM_PP);
          Q.AddDate( StrToDate( xUKPP_DATE_PP));
          Q.AddStr( aPAYDOC_CODE);              // Тип документа в ФК
          // exchange #2912
          // счет нужно проверять как IK.LS_UBP
          Q.AddStr( aIK_LS_UBP);//xUK_LS_NUBP);

          if NOT aWithoutINN_KPP then begin
            xSQL := xSQL + ' and A.REC_TAXCODE = ?';
            Q.AddStr( xUKPP_INN_PP);

            if (LENGTH( xUKPP_KPP_PP) > 0) then begin
              xSQL := xSQL + ' and A.REC_KPP = ?';
              Q.AddStr( xUKPP_KPP_PP);
            end else begin
              // exchange #2902
              // если кпп не указан - ищем с кпп is null
              xSQL := xSQL + ' and (A.REC_KPP = '#39#39' or A.REC_KPP is null)';
            end;
          end;


          xDocXml.LoadXml( Q.Run( xSQL));
          finally
            Q.Free;
        end;

        except
          ExceptionLog( 'Сбой Do_FT_2_1');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~Do_FT_2_2
    поиск ЭД в списке ЭД класса 194 требуется искать документ с полями:
     «Номер документа», «Дата документа»,  равными по значению UKPP.NOM_PP,
     UKPP.DATA_PP,  соответственно;
    И с включенным признаком «Чек» или «Карта» соответственно значению
      UKPP.NAME_PP «Заявка на получение наличных денег» или
      «Заявка на получение денежных средств, перечисляемых на карту»;
    И значением поля «Счет» блока «Плательщик» равным по значению UK.LS_NUBP;
    И в статусе «Обработан».
    при поиске не используется инн и кпп.}
  procedure Do_FT_2_2( aWAYTOGETCASH: string);
  var
    Q: TRunQuery;
    xSQL: string;
  begin

      try
        Q:= TRunQuery.Create;
        try
          xSQL :=
                   ' Select A.* From aubucashrequest A'
                  +' Join Document D on A.Document_Id = D.Id'
                  +' Where D.Budget_Id = ? and D.DocumentClass_Id = 194'
                  +' and LTrim(A.Doc_Number) = ? and A.Doc_Date = ?'
                  +' and A.WAYTOGETCASH=?'
                  +' and a.PAY_ACCOUNT = ?'
                  +' and A.dispstatus_id=10'
//                  +' and A.REC_TAXCODE = ?'
                  +' and (a.title_id is null or a.title_id=a.id)';

          Q.AddStr( aBudgetId);
          Q.AddStr( xUKPP_NOM_PP);
          Q.AddDate( StrToDate( xUKPP_DATE_PP));
          Q.AddStr( aWAYTOGETCASH);
          // exchange #2912
          // счет нужно проверять как IK.LS_UBP
          Q.AddStr( aIK_LS_UBP);//xUK_LS_NUBP);
//          Q.AddStr( xUKPP_INN_PP);
{
          if (LENGTH( xUKPP_KPP_PP) > 0) then begin
            xSQL := xSQL + ' and A.REC_KPP = ?';
            Q.AddStr( xUKPP_KPP_PP);
          end;
}

          xDocXml.LoadXml( Q.Run( xSQL));
          finally
            Q.Free;
        end;

        except
          ExceptionLog( 'Сбой Do_FT_2_2');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~Do_FT_2_4
    поиск ЭД в списке ЭД класса 199 требуется искать документ с полями:
      «Номер», «Дата», равными по значению UKPP.NOM_PP, UKPP.DATA_PP,
        соответственно;
      И полем «Лицевой счет», равным по значению UK. LS_NUBP;
      И с включенным признаком «От ФК»;
      И в статусе «Обработан».}
  procedure Do_FT_2_4;
  var
    Q: TRunQuery;
    xSQL: string;
  begin

      try
        Q:= TRunQuery.Create;
        try
          xSQL :=
                   ' Select A.* From moneytransferorder A'
                  +' Join Document D on A.Document_Id = D.Id'
                  +' Where D.Budget_Id = ? and D.DocumentClass_Id = 199'
                  +' and LTrim(A.Doc_Number) = ? and A.Doc_Date = ?'
                  +' and A.PAY_ACCOUNT=? and A.FK_FLAG=1'
                  +' and A.dispstatus_id=10'
                  +' and (a.title_id is null or a.title_id=a.id)';

          Q.AddStr( aBudgetId);
          Q.AddStr( xUKPP_NOM_PP);
          Q.AddDate( StrToDate( xUKPP_DATE_PP));
          // exchange #2912
          // счет нужно проверять как IK.LS_UBP
          Q.AddStr( aIK_LS_UBP);//xUK_LS_NUBP);


          xDocXml.LoadXml( Q.Run( xSQL));
          finally
            Q.Free;
        end;

        except
          ExceptionLog( 'Сбой Do_FT_2_4');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~Do_FT_2_3
    поиск ЭД в:
      списке ЭД класса 195 требуется искать документ:
        с полями  «Номер документа», «Дата документа», равными по значению
          UKPP.NOM_PP, UKPP.DATA_PP, соответственно;
        И полем «Вид документа в ФК» равным по значению UKPP.NAME_PP;
        И полем «Счет» блока «Плательщик» равным по значению UK. LS_NUBP;
        И в статусе «Обработан».

      Если документ однозначно найден, то ФТ-5.1.
      Если не найдено ни одного документа, то в списке ЭД класса 5
        требуется искать документ с полями:
        «Номер», «Дата», равными по значению UKPP.NOM_PP, UKPP.DATA_PP,
          соответственно;
        И полями «ИНН», «КПП», «Организация» блока «Плательщик»,
          соответствующими организации, официальное название которой равно
          UK. NAME_NUBP; имеющей открытый счет с типом «Лицевой счет в ФК»
          равный значению UK.LS_NUBP;
        И в статусе «Обработка завершена».

      Если документ однозначно найден, то для формирования бюджетной строки
        ЭД класса 199 используется связанный (сквитованный) ЭД класса 195
        или 194 ФТ-5.1.

      Если не найдено ни одного документа, то в списке ЭД класса 24 требуется
        искать документ с полями:
          «Номер», «Дата», равными по значению UKPP.NOM_PP, UKPP.DATA_PP,
            соответственно;
        И полями «ИНН», «КПП»  блока «Получатель из выписки», соответствующими
          значениям  UKPP. INN_PP и UKPP.KPP_PP, соответственно;
        И в статусе «Обработка завершена».

      exchange #2902
      проблема с пустыми строками.
      в результате: если кпп указан пустой строкой ищем: кпп='' или кпп is null.
      так же поправлено, что здесь ищется только 195й, а не 194 или 195й.

      exchange #2959
      исключен из поиска UK.NAME_NUBP при поиске эд 5 класса.
      поиск эд 5го: ИНН и КПП которой равны IK.INN и IK.KPP.}
  procedure Do_FT_2_3;
  var
    Q: TRunQuery;
    xSQL: string;
  begin

      try
        // здесь поиск 195го класса по фт-2.1 должен быть без инн и кпп
        Do_FT_2_1( 'PP', TRUE);
        if (xDocXml.documentElement.childNodes.Length <> 1) then begin
          Q:= TRunQuery.Create;
          try
            // при поиске 195й будет связанным,
            // поэтому parent идет от 5го, а не от связанного 194/195го
            xSQL := ' select m.* from PAYORDER P'
                   +' join DOCUMENT D on D.ID = p.DOCUMENT_ID'
                   +' join AUBUCASHREQUEST M on m.document_id = d.parent_id'
                   +' Where d.Budget_Id = ? and p.DocumentClass_Id = 5'
                   +' and LTrim(p.Doc_Number) = ? and p.Doc_Date = ?'
                   // exchange #2959
//                   +' and p.PAY_NAME=?'
                   // exchange #2902 - поиск только 195й
                   +' and m.documentclass_id in (195)'
                   +' and p.dispstatus_id=10'
                   +' and p.PAY_TAXCODE = ?'
                   // exchange #2902 - либо поле=? либо (...)
                   +' and %_PAY_KPP_%'
                   +' and (m.title_id is null or m.title_id=m.id)'
                   +' and exists(select * from orgaccount a'
                   +'     where a.org_id=p.pay_id'
                   +'          and a.orgacctype_id=5'
                   +'          and a.close_date is null'
                   +'          and a.account_number=?)';

            Q.AddStr( aBudgetId);
            Q.AddStr( xUKPP_NOM_PP);
            Q.AddDate( StrToDate( xUKPP_DATE_PP));
            // exchange #2959
            //Q.AddStr( xUK_NAME_NUBP);

            // exchange #2959
            // поиск эд 5го: ИНН и КПП которой равны IK.INN и IK.KPP.
            // Q.AddStr( xUKPP_INN_PP);
            Q.AddStr( xIK_INN);

            // exchange #2902
            // если кпп указано - ищем через: поле=? и параметр,
            // иначе в запросе ищем "(кпп='' or кпп is null)" без значения
            if (LENGTH( xUKPP_KPP_PP) > 0) then begin
              xSQL := StringReplace( xSQL,
                                     '%_PAY_KPP_%',
                                     'p.PAY_KPP = ?');
              // exchange #2959
              // поиск эд 5го: ИНН и КПП которой равны IK.INN и IK.KPP.
              //Q.AddStr( xUKPP_KPP_PP);
              Q.AddStr( xIK_KPP);
            end else begin
              xSQL := StringReplace( xSQL,
                                     '%_PAY_KPP_%',
                                     '(p.PAY_KPP = '#39#39' or p.PAY_KPP is null)');
            end;

            // exchange #2912
            // счет нужно проверять как IK.LS_UBP
            Q.AddStr( aIK_LS_UBP);//xUK_LS_NUBP);

            xDocXml.LoadXml( Q.Run( xSQL));


            // при поиске 24го, 193й нужно искать в порожденных эд
            // при этом связка parent идет от 193го, а не от 24го.
            if (xDocXml.documentElement.childNodes.Length <> 1) then begin
              xSQL :=  ' select m.*'
                      +' from moneytransferorder m'
                      +' join document d on d.id=m.document_id'
                      +' join payorder p on p.document_id=d.parent_id'
                      +' where d.Budget_Id = ? and p.DocumentClass_Id = 24'
                      +' and ltrim(p.DOC_NUMBER) = ? and p.Doc_Date = ?'
                      +' and p.dispstatus_id=10'
                      +' and m.documentclass_id in (193)'
                      // проверка получателя из выписки!
                      +' and p.ORG_TAXCODE = ?'
                     // exchange #2902 - либо поле=? либо (...)
                     +' and %_ORG_KPP_%'
//                        +' and p.ORG_KPP = ?'
                      +' and (m.title_id is null or m.title_id=m.id)';

              Q.AddStr( aBudgetId);
              Q.AddStr( xUKPP_NOM_PP);
              Q.AddDate( StrToDate( xUKPP_DATE_PP));
              Q.AddStr( xUKPP_INN_PP);

              // exchange #2902
              // если кпп указано - ищем через: поле=? и параметр,
              // иначе в запросе ищем "(кпп='' or кпп is null)" без значения
              if (LENGTH( xUKPP_KPP_PP) > 0) then begin
                xSQL := StringReplace( xSQL,
                                       '%_ORG_KPP_%',
                                       'p.ORG_KPP = ?');
                Q.AddStr( xUKPP_KPP_PP);
              end else begin
                xSQL := StringReplace( xSQL,
                                       '%_ORG_KPP_%',
                                       '(p.ORG_KPP = '#39#39' or p.ORG_KPP is null)');
              end;


              xDocXml.LoadXml( Q.Run( xSQL));

            end;
            finally
              Q.Free;
          end;
        end;

        except
          ExceptionLog( 'Сбой Do_FT_2_3');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~Do_FT_5_1
    ФТ-5.1 - формирование строк по разделу UKPP из строк в найденном эд.}
  procedure Do_FT_5_1;
  var
    xDocLines: Variant;
    xDocLine: Variant;
    xFoundStr: string;
    xDocLineStr: string;
    xDocLineSum: currency;
    xFounds: integer;
    i: integer;
    xDocClass: string;
//    xDocLineUKPP: Variant;
    xXmlDocLineUKPP: Variant;
  begin

      try
        // загружаем только строки эд или эд целиком, если строка одна
        xDocClass := xDoc.GetAttribute( 'DOCUMENTCLASS_ID');
        case xDocClass of
          '199','193':
                xDocLines := QueryXml(
                               ' Select A.* From moneytransferorder A'
                              +' Where a.document_id=?'
                              +' and (a.title_id is null or a.title_id<>a.id)',
                                [ xDoc.GetAttribute( 'DOCUMENT_ID')]);

          '194','195':
                xDocLines := QueryXml(
                               ' Select A.* From aubucashrequest A'
                              +' Where a.document_id=?'
                              +' and (a.title_id is null or a.title_id<>a.id)',
                                [ xDoc.GetAttribute( 'DOCUMENT_ID')]);
        end;//case//

        // разбираем КБК из раздела UKPP
        // это нужно чтобы получить КОСГУ.
        // тип КБК в данном случае не нужен, поэтому отключаем сообщение,
        // если тип КБК не определен (последний параметр =FALSE).
        Do_Check_KBK_( xUKPP_KBK, 'UKPP', xUKPP_LINE_NOM, FALSE);

        // искомая строка по UKPP: КОСГУ+Код субсидии
        xFoundStr := xKBK_KES + GetGrantInvestmentCodeByID(
                                  DoSearch_GRANTINVESTMENT_( xUKPP_ADD_CLASS));

        // перебор строк в найденном ЭД
        xDocLineSum := 0;
        xFounds     := 0;
        for i := 0 to xDocLines.documentElement.childNodes.Length - 1 do begin
          xDocLine := xDocLines.documentElement.childNodes.item[ i];

          xDocLineStr := xDocLine.GetAttribute( 'KESR_CODE')
                          + GetGrantInvestmentCodeByID(
                                xDocLine.GetAttribute( 'GRANTINVESTMENT_ID'));

          if (xDocLineStr = xFoundStr) then begin
            INC( xFounds);
            xDocLineSum := xDocLineSum + StrToCurr(
                                            xDocLine.GetAttribute( 'AMOUNT'));

          end;
        end;//for//


        // если не нашли ни одной строки - считаем что ЭД не найден.
        // дальше нействуем по ФТ-5.2 -> ФТ-5.3
        if (xFounds <= 0) then begin
          // формируем отдельный эд по каждой строке ukpp и нескольким
          // строкам ukpp_n
          Do_FT_5_3( xDocClass);
          {
          if DoCreateED199_ then begin
            Do_FT_5_3( xDocClass);
            if aCfg_CreateMultiDoc
              then DoMakeED199_;
          end;}
          EXIT;
        end;



        // формируем отдельный эд по каждой строке ukpp и нескольким
        // строкам ukpp_n - здеь создаем эд, а в конце передаем на сервер.
        //if NOT DoCreateED199_ then EXIT;

        // разбираем еще раз КБК из раздела UKPP
        // это нужно чтобы отобразить сообщение если тип КБК не определен
        Do_Check_KBK_( xUKPP_KBK, 'UKPP', xUKPP_LINE_NOM);

        // сравниваем суммы
        // сравниваем строки, поскольку при сравнении чисел могут быть
        // неоднозначности из-за вещественной части
        if (FORMAT( '%0.2f', [ xUKPP_SUM_PP]) = FORMAT( '%0.2f', [ xDocLineSum]))
          then begin
            // exchanghe #2907
            // нужно сохранить хотя бы одну строку эд, созданную по блоку UKPP
//            xDocLineUKPP    := NULL;
            xXmlDocLineUKPP := NULL;
            for i := 0 to xDocLines.documentElement.childNodes.Length - 1 do begin
              xDocLine := xDocLines.documentElement.childNodes.item[ i];
  
              xDocLineStr := xDocLine.GetAttribute( 'KESR_CODE')
                              + QueryField( ' select CODE from GRANTINVESTMENT'
                                           +' where ID=?',
                                            [ xDocLine.GetAttribute(
                                                          'GRANTINVESTMENT_ID')],
                                            'CODE');
              if (xDocLineStr = xFoundStr) then begin

                // формируем строку из UKPP
                // сумма - отрицательная сумма из строки ЭД
                Do_Build_Line_From_UKPP_(
                            StrToCurr( xDocLine.GetAttribute( 'AMOUNT')),
                            xDocClass,
                            xDocLine);

                // КОСГУ - из строки ЭД
                xXmlDocLine.SetAttribute(
                            'KESR_CODE',
                            xDocLine.GetAttribute( 'KESR_CODE'));

                // КВФО - из строки ЭД
                xXmlDocLine.SetAttribute(
                            'FSR_ID',
                            xDocLine.GetAttribute( 'FSR_ID'));

                // код субсидий - из строки ЭД
                xXmlDocLine.SetAttribute(
                            'GRANTINVESTMENT_ID',
                            xDocLine.GetAttribute( 'GRANTINVESTMENT_ID'));

                // отраслевой код - из строки ЭД
                xXmlDocLine.SetAttribute(
                            'INDUSTRYCODE_ID',
                            xDocLine.GetAttribute( 'INDUSTRYCODE_ID'));

                // exchange #2907
                // сохраняем первую созданную строку в эд, созданную
                // по блоку UKPP
                if VarIsNULL( xXmlDocLineUKPP) then begin
                  //xDocLineUKPP    := xDocLine;
                  xXmlDocLineUKPP := xXmlDocLine;
                end;
              end;
            end;//for//

            // формирование строк из разделов UKPP_N
            // по текущей строке UKPP.LINE_NOM
            // exchange #2907
            // при создании строки по UKPP_N, оговариваем, что она будет
            // создаваться по строке xDocLineUKPP в найденном эд
            // класса xDocClass, и строка в создаваемом эд 199м классе
            // по первому блоку UKPP будет соответвовать переменной xXmlDocLineUKPP
            Do_Build_Lines_From_UKPP_N_( xDocClass, xXmlDocLineUKPP);

            // завершаем создание эд по каждой строке ukpp
            {if aCfg_CreateMultiDoc
              then DoMakeED199_;}
            EXIT;
          end;


        // случай, если в эд найдены строки, но суммы по этим строкам
        // расходятся с UKPP_SUM_PP
        // формируем строку из UKPP
        // сумма - отрицательная сумма из UKPP
        // exchange #2907
        // в этом случае для эд 199го поле "Направление операции" из строки
        // заполнить не получится (будет пустым).
        Do_Build_Line_From_UKPP_( xUKPP_SUM_PP, xDocClass);

        // КВФО - Если первые два знака значения UK.LS_NUBP равно «21»
        // или «31», то «9», в остальных случаях «8».
        if (COPY( xUK_LS_NUBP, 1, 2) in ['21','31'])
          then xXmlDocLine.SetAttribute( 'FSR_ID', '9')
          else xXmlDocLine.SetAttribute( 'FSR_ID', '8');

        // код субсидий - Значением поля «Код» строки справочника
        // «Коды субсидий» равным значению  UKPP.ADD_KLASS.
        // Если значение не найдено, то заполнять нулевым значением (Не указан).
        // для нулевого кода в АЦК-Ф указывается тип=3
        xXmlDocLine.SetAttribute(
                          'GRANTINVESTMENT_ID',
                          DoSearch_GRANTINVESTMENT_( xUKPP_ADD_CLASS));

        // отраслевой код - нулевой
        xXmlDocLine.SetAttribute(
                          'INDUSTRYCODE_ID',
                          DoSearch_INDUSTRYCODE_System_);


        // формирование строк из разделов UKPP_N
        // по текущей строке UKPP.LINE_NOM
        // exchange #2907
        // создаем строку по блоку UKPP_N для найденного эд класса = xDocClass,
        // что строки в найденном эд по сумме не найдены, и что строка
        // в создаваемом жд 199го класса сформирована по блоку UKPP = xXmlDocLineUKPP
        Do_Build_Lines_From_UKPP_N_( xDocClass, xXmlDocLineUKPP);
        except
          ExceptionLog( 'Сбой Do_FT_5_1');
      end;

  end;
  {---------------------------------------------------------------------------}
  {~Do_FT_5_1_1
    exchange #2912
    в этом случае создаются строки в новом 199м по всем строкам в найденном ЭД
    по ФТ-2.х. в отличие от ФТ-5.1, здесь не анализируются строки с КБК+кодом
    цели, поскольку в этом случае таких строк может не быть в уточняемом эд.
    для найденного 199го есть проблемное место:
      в результате, когда по всем строкам в найденном эд, будут созданы строки
      в новом 199м, в конце будет создана одна строка по UKPP_N. при этом
      поле направление должно заполняться (по #2907) исходя из созданной
      доходной строки. а в случае если строк несколько, то это неоднозначность.
      в этом случае "направление операции" заполняется жестко.}
  procedure Do_FT_5_1_1;
  var
    xDocLines: Variant;
    xDocLine: Variant;
    i: integer;
    xDocClass: string;
    xDocKES: string;
  begin

      try
        // формируем отдельный эд по каждой строке ukpp и нескольким
        // строкам ukpp_n - здеь создаем эд, а в конце передаем на сервер.
        //if NOT DoCreateED199_ then EXIT;

        // загружаем только строки эд или эд целиком, если строка одна
        xDocClass := xDoc.GetAttribute( 'DOCUMENTCLASS_ID');
        case xDocClass of
          '199','193':
                xDocLines := QueryXml(
                               ' Select A.* From moneytransferorder A'
                              +' Where a.document_id=?'
                              +' and (a.title_id is null or a.title_id<>a.id)',
                                [ xDoc.GetAttribute( 'DOCUMENT_ID')]);

          '194','195':
                xDocLines := QueryXml(
                               ' Select A.* From aubucashrequest A'
                              +' Where a.document_id=?'
                              +' and (a.title_id is null or a.title_id<>a.id)',
                                [ xDoc.GetAttribute( 'DOCUMENT_ID')]);
        end;//case//

        // перебираем все строки в найденном эд
        for i := 0 to xDocLines.documentElement.childNodes.Length - 1 do begin
          xDocLine := xDocLines.documentElement.childNodes.item[ i];

          // заполняем КБК и тип КБК из строки в эд
          // это нужно чтобы получить КОСГУ и тип КБК для создаваемой строки,
          // а это нужно чтобы заполнить поле "направление операции".
          // в строке эд заполнено только поле КОСГУ, а для строки КБК нужно
          // еще 17 цифр - добавляем их слева.
          // поскольку в строках могут быть разные КОСГУ, а тип КБК в строке
          // заранее неизвестен, то КОСГУ проверяем (хотя это и не нужно),
          // а тип КБК определяется по коду КОСГУ.
          xDocKES := xDocLine.GetAttribute( 'KESR_CODE');
          Do_Check_KBK_( '00000000000000000' + xDocKES, '', INTTOSTR( i + 1));

          // формируем строку из UKPP
          // сумма - отрицательная сумма из строки ЭД
          Do_Build_Line_From_UKPP_(
                      StrToCurr( xDocLine.GetAttribute( 'AMOUNT')),
                      xDocClass,
                      xDocLine);

          // КОСГУ - из строки ЭД
          xXmlDocLine.SetAttribute( 'KESR_CODE', xDocKES);

          // КВФО - из строки ЭД
          xXmlDocLine.SetAttribute(
                      'FSR_ID',
                      xDocLine.GetAttribute( 'FSR_ID'));

          // код субсидий - из строки ЭД
          xXmlDocLine.SetAttribute(
                      'GRANTINVESTMENT_ID',
                      xDocLine.GetAttribute( 'GRANTINVESTMENT_ID'));

          // отраслевой код - из строки ЭД
          xXmlDocLine.SetAttribute(
                      'INDUSTRYCODE_ID',
                      xDocLine.GetAttribute( 'INDUSTRYCODE_ID'));

        end;//for//

        // формирование строк из разделов UKPP_N
        // если в исходном эд было несколько строк, значит создаваемая строка
        // по UKPP_N не будет связана с создаваемыми строками из найденного эд.
        // это касается заполнения поля "направление операции" из найденного
        // эд 199го класса. для остальных эд это не критично.
        // если строка в найденном эд одна, то она будет переда как созданная
        // по разделу UKPP.
        if ( xDocLines.documentElement.childNodes.Length = 1)
          then Do_Build_Lines_From_UKPP_N_( xDocClass, xXmlDocLine)
          else Do_Build_Lines_From_UKPP_N_( xDocClass);

        // завершаем создание эд по каждой строке ukpp
        if aCfg_CreateMultiDoc
          then DoMakeED199_;
        except
          ExceptionLog( 'Сбой Do_FT_5_1_1');
      end;

  end;
  {---------------------------------------------------------------------------}
  procedure Do_FT_5_2;
  begin

      try
        // exchange #2907
        // строка создается по блоку UKPP_N без учета строк, созданных по UKPP,
        // и уточняемый эд не найден.
        Do_Build_Lines_From_UKPP_N_;
        except
          ExceptionLog( 'Сбой Do_FT_5_2');
      end;

  end;
  {---------------------------------------------------------------------------}
  (*
  {~DoCheck_FT_5_2_
    exchange #2942
    проверяем, если эд по конфигу создается один на все строки,
    то создание уточняющей строки без уточняемой по ФТ-5.2 можно.

    если формируется эд по каждой строке ukpp, то только
    чтобы была строка ukpp и несколько ukpp_n

    результат:
      =true - строка может быть создана
      =false - строка не должна быть создана

    если строка не может быть создана - отображаем сообщение в логе.}
  function DoCheck_FT_5_2_: boolean;
  begin
  
      try
        if NOT aCfg_CreateMultiDoc
          then Result := TRUE
          else begin
            Result := FALSE;
            WriteInfo(
                FORMAT( 'Нельзя создать ЭД %s с GUID=%s.',
                      [ xDocInfo199,
                        xUK_GUID_FK + GetNodeAttributeEx( xNodeUKPP, 'LINE_NOM')]));
          end;
        except
          ExceptionProc( 'DoCheck_FT_5_2_');
      end;
  
  end;
  *)
  {---------------------------------------------------------------------------
                   НАЧАЛО Exch2832_Build199_By_IK_UK
  {---------------------------------------------------------------------------}
begin

    try
      xIK_XML       := aUK_XML.parentNode;
      xUK_DocNum    := VarToStr( aUK_XML.getAttribute('NOM_UK'));
      xUK_DocDate   := VarToStr( aUK_XML.getAttribute('DATE_UK'));
      xUK_GUID_FK   := VarToStr( aUK_XML.GetAttribute('GUID_FK'));
      xUK_NAME_NUBP := VarToStr( aUK_XML.GetAttribute('NAME_NUBP'));
      // поле л/с обязательно = UK.LS_NUBP
      xUK_LS_NUBP := TRIM( VarToStr( aUK_XML.GetAttribute( 'LS_NUBP')));
      xIK_INN := VarToStr( xIK_XML.GetAttribute( 'INN_UBP'));
      xIK_KPP := VarToStr( xIK_XML.GetAttribute( 'KPP_UBP'));


      //------------------------------------------------------------------
      // пока еще ни один 199й не создан
      xXmlDoc199 := NULL;
      // описание искомого ЭД 199го класса
      // это описание отображается в логах как описание ЭД.
      xDocInfo199 := FORMAT( '"Cправка по операциям БУ/АУ" № %s дата %s, '
                            +'номер л/с %s',
                              [ xUK_DocNum,
                                xUK_DocDate,
                                xUK_LS_NUBP]);

      //------------------------------------------------------------------
      // ФТ-4
      // в самом начале проверяем наличие UKPP.
      // если нет ни одного раздела UKPP - такой UK не обрабатываем.
      // если в UKPP есть незаполненные строки, проверяем, есть ли вообще
      // хотя бы одна заполненная. если таких нет - UK не проверяем.
      // поле суммы должно быть заполнено обязательно.
      xUKPP_Found := FALSE;
      for i := 0 to aUK_XML.childNodes.Length - 1 do begin
        xNodeUKPP := aUK_XML.childNodes.Item[i];
        case xNodeUKPP.NodeName of
          'UKPP': begin
              // exchange #2912
              xUKPP_LINE_NOM  := VarToStr( xNodeUKPP.GetAttribute( 'LINE_NOM'));
              xUKPP_KBK       := VarToStr( xNodeUKPP.GetAttribute( 'KBK'));
              xUKPP_ADD_CLASS := VarToStr( xNodeUKPP.GetAttribute( 'ADD_KLASS'));
              xUKPP_NAME_PP   := VarToStr( xNodeUKPP.GetAttribute( 'NAME_PP'));

              // exchange #2912
              // ФТ-4.1 Если поля UKPP.KBK и UKPP.ADD_KLASS строки загружаемого ЭД
              // с номером UKPP.LINE_NOM имеют пустое значение и  поле
              // UKPP.NAME_PP обрабатываемой строки ЭД имеет пустое и значения
              // первых двух знаков IK. LS_UBP равно «30», то ФТ-5.1.1
              // exchange #2924
              // проверка удалена, поскольку требуется проверить поле суммы
              {
              if (    (LENGTH( xUKPP_KBK) <= 0)
                  AND (LENGTH( xUKPP_ADD_CLASS) <= 0)
                  AND (LENGTH( xUKPP_NAME_PP) <= 0)
                  AND (POS( '30', aIK_LS_UBP) = 1)) then begin
                // нашли хотя бы одну не пустую строку UKPP
                xUKPP_Found := TRUE;
                BREAK;
              end;
              }

              // exchange #2912
              // ФТ-4.2 Если поля UKPP.KBK и UKPP.ADD_KLASS строки
              // загружаемого ЭД с номером UKPP.LINE_NOM имеют пустое значение
              // и значения первых двух знаков IK. LS_UBP не равно «30»,
              // то ЭД класса 199 не формировать переходить к следующему
              // документу блока UK.
              // exchange #2924
              // проверка удалена, поскольку для этого случая нужно
              // выполнять ФТ-5.2
              {
              if (    (LENGTH( xUKPP_KBK) <= 0)
                  AND (LENGTH( xUKPP_ADD_CLASS) <= 0)
                  AND (POS( '30', aIK_LS_UBP) <> 1)) then begin
                // то вообще прерываем обработку UK
                WriteInfo( FORMAT( 'ЭД %s по IK.UK не создан - по блоку UKPP.LINE_NOM=%s: поля KBK и ADD_KLASS не заполнены и IK.LS_UBP не начинается на «30».',
                                    [ xDocInfo199,
                                      xUKPP_LINE_NOM]));
                EXIT;
              end;
              }

              // от исходного осталось условие: обязательность заполнения
              // поля суммы
              // exchange #2925
              // здесь не нужно проверять заполнение полей КБК и класса,
              // на заполнение одного из полей. допускается что оба поля
              // могут быть не заполнены.
              if {(    (LENGTH( xUKPP_KBK) > 0) OR (LENGTH( xUKPP_ADD_CLASS) > 0)
                  AND }(LENGTH( VarToStr( xNodeUKPP.GetAttribute( 'SUM_PP'))) > 0)
                then begin
                  // нашли хотя бы одну не пустую строку UKPP
                  xUKPP_Found := TRUE;
                  BREAK;
                end;
            end;
        end;//case//
      end;//for//
      if NOT xUKPP_Found then begin
        WriteInfo( FORMAT( 'ЭД %s по IK.UK не создан - не найдено заполненного блока UKPP.',
                            [ xDocInfo199]));
        EXIT;
      end;

      //------------------------------------------------------------------
      // exchange #2902
      // перебираем все UKPP_N и проверяем, если поле суммы UKPP_N.SUM
      // пустое, то заполняем это поле из связанного по номеру строки
      // раздела UKPP из поля UKPP.SUM_PP

      xUKPP_Found := TRUE;
      for i := 0 to aUK_XML.childNodes.Length - 1 do begin
        xNodeUKPP_N := aUK_XML.childNodes.Item[i];
        if (xNodeUKPP_N.NodeName = 'UKPP_N') then
          if (LENGTH( VarToStr( xNodeUKPP_N.GetAttribute( 'SUM'))) <= 0)
            then begin
              xNodeUKPP := Do_Search_UKPP_(
                              VarToStr(
                                  xNodeUKPP_N.GetAttribute( 'LINE_NOM')));
              if VarIsNull( xNodeUKPP) then begin
                WriteInfo( FORMAT( 'Не найден раздел UKPP_N с номером строки %s',
                                    [ xNodeUKPP_N.GetAttribute( 'LINE_NOM')]));
                xUKPP_Found := FALSE;
              end else begin
                xNodeUKPP_N.SetAttribute(
                              'SUM',
                              VarToStr( xNodeUKPP.GetAttribute( 'SUM_PP')));

              end;
            end;
      end;//for//
      // если хотя бы один раздел UKPP не найден - эд не создаем
      if NOT xUKPP_Found then begin
        WriteInfo( 'Документ не может быть создан.');
        EXIT;
      end;

      //------------------------------------------------------------------
//      WriteInfo( FORMAT( 'ЭД %s.', [ xDocInfo199]), TRUE);

      //------------------------------------------------------------------
      // производим поиск ЭД 199 класса в списке ЭД системы
      // exchange #2942
      // проверка выполняется только если создается один эд на все строки
      if NOT aCfg_CreateMultiDoc then
        if (CheckDouble199_( xUK_GUID_FK) <> 0) then begin
          EXIT;
        end;

      //------------------------------------------------------------------
      WriteLog( 'Определение "Учреждение"...', TRUE);
      { поиск организации, определенной из справочника «Организации»
        по значению UK.NAME_NUBP равному по значению полю «Официальное название».
        Анализируем строки справочника «Организации» только с включенным
        признаком «Актуальная» и с пустой датой закрытия либо большей
        по значению UK.DATE_UK.
        «Счет», равным значению UK.LS_NUBP карточки лицевого счета,
        открытого организации, указанной в блоке «Учреждение»,
        с пустым полем «Заблокирован/закрыт».
        здесь необходимо проверять тип счета = 5 (ЛС ФК)}
      // exchange #2959
      // исключена проверка UK.NAME_NUBP = офиц.названию орг
      // добавлены орг роли орг = 18 и 19 и счет IK.LS_UBP вместо UK.LS_NUBP
      // добавлены в запрос поиска реквизиты: ИНН=UKIK.INN, КПП= IK.KPP
      // чтобы дважды не обновлять код запроса, в начале описываем текст
      // запроса, а при выполнении запроса подставляем раздел select...
      if (LENGTH( xIK_KPP) > 0)
        then xSQL :=   ' from ORG ooo '
                       // exchange #2923
                       // здесь проверяется галка "актуальная" у организации
                      +' Join Contractor C on C.Org_Id = ooo.Id'
                      +' Join Budget Obd on Obd.Org_Id = C.Inspector_Id'
                      +' where Obd.ID=?'// and ooo.DESCRIPTION=?'
                      +' and (ooo.Close_Date is null or ooo.Close_Date>?)'
                      // наличие оргроли орг 18 или 19
                      +' and exists ( select * from orgroles oor where'
                      +'   oor.orgrole_id in (18,19) and oor.org_id=ooo.id)'
                      // параметры орг
                      +' and ooo.TAXCODE=? and ooo.KPP=?'
                      // наличие счета у орг 5го типа
                      +' and exists ( select * from  ORGACCOUNT OA'
                      +'     where OA.ORG_ID = OOO.ID and OA.ACCOUNT_NUMBER=?'
                      +'          and oa.orgacctype_id=5'
                      +'          and OA.CLOSE_DATE is null)'
        else xSQL :=   ' from ORG ooo '
                       // exchange #2923
                       // здесь проверяется галка "актуальная" у организации
                      +' Join Contractor C on C.Org_Id = ooo.Id'
                      +' Join Budget Obd on Obd.Org_Id = C.Inspector_Id'
                      +' where Obd.ID=?'// and ooo.DESCRIPTION=?'
                      +' and (ooo.Close_Date is null or ooo.Close_Date>?)'
                      // наличие оргроли орг 18 или 19
                      +' and exists ( select * from orgroles oor where'
                      +'   oor.orgrole_id in (18,19) and oor.org_id=ooo.id)'
                      // параметры орг
                      +' and ooo.TAXCODE=? and (ooo.KPP = '#39#39' or ooo.KPP is null)'
                      // наличие счета у орг 5го типа
                      +' and exists ( select * from  ORGACCOUNT OA'
                      +'     where OA.ORG_ID = OOO.ID and OA.ACCOUNT_NUMBER=?'
                      +'          and oa.orgacctype_id=5'
                      +'          and OA.CLOSE_DATE is null)';

      if (LENGTH( xIK_KPP) > 0)
        then xOrgFound := QueryField(
                       ' select count( * ) N ' + xSQL,
                       [ aBudgetId,
//                         xUK_NAME_NUBP,
                         StrToDate( xUK_DocDate),
                         xIK_INN,
                         xIK_KPP,
                         aIK_LS_UBP {xUK_LS_NUBP}],
                       'N')
        else xOrgFound := QueryField(
                       ' select count( * ) N ' + xSQL,
                       [ aBudgetId,
//                         xUK_NAME_NUBP,
                         StrToDate( xUK_DocDate),
                         xIK_INN,
                         aIK_LS_UBP {xUK_LS_NUBP}],
                       'N');
      if (xOrgFound <> '1') then begin
        // exchange #2959 - теперь счет ищется по IK.LS_UBP, а не UK.LS_NUBP
        WriteInfo( FORMAT( 'Не удалось определить организацию учреждения'
                          +' со счетом %s.',
                           [  aIK_LS_UBP {xUK_LS_NUBP}]));
        EXIT;
      end;

      // exchange #2959
      // исключена проверка UK.NAME_NUBP = офиц.названию орг
      // добавлены орг роли орг = 18 и 19 и счет IK.aIK_LS_UBP вместо UK.LS_NUBP
      if (LENGTH( xIK_KPP) > 0)
        then xXmlOrg := QueryXml(
                           ' select ooo.* ' + xSQL,
                           [ aBudgetId,
                  //           xUK_NAME_NUBP,
                             StrToDate( xUK_DocDate),
                             xIK_INN,
                             xIK_KPP,
                             aIK_LS_UBP {xUK_LS_NUBP}])
        else xXmlOrg := QueryXml(
                           ' select ooo.* ' + xSQL,
                           [ aBudgetId,
                  //           xUK_NAME_NUBP,
                             StrToDate( xUK_DocDate),
                             xIK_INN,
                             aIK_LS_UBP {xUK_LS_NUBP}]);
      // на всякий случай оставляем проверку количества записей
      if (xXmlOrg.documentElement.childNodes.Length <> 1) then EXIT;
      xXmlOrg := xXmlOrg.documentElement.childNodes.Item[0];

      //------------------------------------------------------------------
      WriteLog('Получение данных организации учредителя', True);
      // учредитель - код вышестоящей организации.
      // если поле не заполнено, ищем организацию с кодом "не указано" (id=0)
      // такая организация (с id=0) должна быть в бд, но для выбора
      // в справочнике организаций она не видна.
      if (VarToStr( xXmlOrg.GetAttribute( 'MANAGER_ID')) = '')
        then xXmlGRBS := QueryXml( ' select * from org where id=?',
                                    [ '0'])
        else xXmlGRBS := QueryXml( ' select * from org where id=?',
                                    [ xXmlOrg.GetAttribute( 'MANAGER_ID')]);
      // доп.проверка результата запроса
      if (xXmlGRBS.documentElement.childNodes.Length <> 1) then begin
        WriteInfo( 'Не удалось прочитать описание организации учредителя в бд.');
        EXIT;
      end;
      xXmlGRBS := xXmlGRBS.documentElement.childNodes.item[0];

      //------------------------------------------------------------------
      (*
      // если создается один эд на все строки, формируем один эд 199й
      // с первой строкой UKPP.
      if NOT aCfg_CreateMultiDoc then begin
        // раздел "платежный документ" заполняется из первой строки UKPP,
        // если такая есть.
        for i := 0 to aUK_XML.childNodes.Length - 1 do begin
          xNodeUKPP := aUK_XML.childNodes.Item[i];
          case xNodeUKPP.NodeName of
            'UKPP': begin
                      // здесь игнорируем проверку создания эд по конфигу,
                      // поскольку уже определили что создаем один эд на
                      // все строки.
                      DoCreateED199_;//( FALSE);
                      BREAK;
                    end;
          end;//case//
        end;//for//
        if VarIsNULL( xXmlDoc199) then begin
          WriteInfo( 'ЭД не создан - не найдено ни одного раздела UKPP.');
          EXIT;
        end;
      end;
      *)

      //------------------------------------------------------------------
      xIndustryCodes := TStringList.Create;
      try
        // в этой переменной будем искать эд в бд
        xDocXml := CreateOleObject('Microsoft.XmlDom');
        try
          // анализируем строки UKPP
          for i := 0 to aUK_XML.childNodes.Length - 1 do begin
            xNodeUKPP := aUK_XML.childNodes.Item[i];
            if (xNodeUKPP.NodeName <> 'UKPP') then CONTINUE;

            // параметры строки
            xUKPP_LINE_NOM  := GetNodeAttributeEx( xNodeUKPP, 'LINE_NOM');
            xUKPP_KBK       := GetNodeAttributeEx( xNodeUKPP, 'KBK');
            xUKPP_ADD_CLASS := GetNodeAttributeEx( xNodeUKPP, 'ADD_KLASS');
            xUKPP_NAME_PP   := GetNodeAttributeEx( xNodeUKPP, 'NAME_PP');
            xUKPP_NOM_PP    := GetNodeAttributeEx( xNodeUKPP, 'NOM_PP');
            xUKPP_DATE_PP   := GetNodeAttributeEx( xNodeUKPP, 'DATE_PP');
            xUKPP_SUM_PP    := StrToCurrDef(
                                    GetNodeAttributeEx( xNodeUKPP, 'SUM_PP'),
                                    0);
            // exchange #2912
            // от исходного осталось условие: обязательность заполнения
            // поля суммы. строки UKPP с пустой суммой игнорируются.
            if (LENGTH( GetNodeAttributeEx( xNodeUKPP, 'SUM_PP')) <= 0)
              then begin

                CONTINUE;
              end;

            // exchange #2942
            // создание эд 199го на группу разделов ukpp
            DoCreateED199_;

            // exchange #2912
            // ФТ-4 Если поля UKPP.KBK и UKPP.ADD_KLASS строки загружаемого ЭД
            // с номером UKPP.LINE_NOM имеют пустое значение и  поле
            // UKPP.NAME_PP обрабатываемой строки ЭД имеет пустое и значения
            // первых двух знаков IK. LS_UBP равно «30», то ФТ-5.1.1
            // exchange #2925
            // ФТ-4: счет больше не проверяется.
            // в результате нужно перейти к ФТ-5.2, а не к ФТ-5.1.1
            if (    (LENGTH( xUKPP_KBK) <= 0)
                AND (LENGTH( xUKPP_ADD_CLASS) <= 0)
                AND (LENGTH( xUKPP_NAME_PP) <= 0)
                {AND (POS( '30', aIK_LS_UBP) = 1)}) then begin

              // если формируется эд по каждой строке ukpp, то только
              // чтобы была строка ukpp и несколько ukpp_n
              Do_FT_5_2;
              CONTINUE;
            end;

            // exchange #2924
            // ФТ-4.2 Если поля UKPP.KBK и UKPP.ADD_KLASS строки
            // загружаемого ЭД с номером UKPP.LINE_NOM имеют пустое значение
            // и значения первых двух знаков IK. LS_UBP не равно «30»,
            // то выполнять ФТ-5.2
            // exchange #2925
            // это условие больше не должно проверяться
(*
            if (    (LENGTH( xUKPP_KBK) <= 0)
                AND (LENGTH( xUKPP_ADD_CLASS) <= 0)
                AND (POS( '30', aIK_LS_UBP) <> 1)) then begin

              Do_FT_5_2;
              CONTINUE;
            end;
*)

            // ФТ-3 если КБК или ADD_KLASS непустое, а поле NAME_PP пустое,
            // то ФТ-5-3
            if ( ((LENGTH( xUKPP_KBK) > 0) OR (LENGTH( xUKPP_ADD_CLASS) > 0))
                 AND (LENGTH( xUKPP_NAME_PP) <= 0)) then begin

              Do_FT_5_3;
              CONTINUE;
            end;

            xUKPP_INN_PP := GetNodeAttributeEx( xNodeUKPP, 'INN_PP');
            xUKPP_KPP_PP := GetNodeAttributeEx( xNodeUKPP, 'KPP_PP');

            // по строкам UKPP ищем ЭД
            case LOWERCASE( xUKPP_NAME_PP) of
              // ФТ-2 пп.1
              'заявка на кассовый расход':
                      Do_FT_2_1( 'ZR');

              'заявка на возврат':
                      Do_FT_2_1( 'ZV');

              // ФТ-2 пп.2
              'заявка на получение наличных денег':
                      Do_FT_2_2( '0');   // чек
              'заявка на получение денежных средств, перечисляемых на карту':
                      Do_FT_2_2( '1');    // карта

              // ФТ-2 пп.3
              'платежное поручение':
                      Do_FT_2_3;

              // ФТ-2 пп.4
              'уведомление об уточнении операций клиента',
              'справка ф. 0504833',
              'справка ф.0504833':
                      Do_FT_2_4;

              else xDocXml.LoadXml( '');
            end;//case//

            // Если документ однозначно найден, то ФТ-5.1, иначе ФТ-5.2.
            // ФТ-5.2 фактически есть создание строк по UKPP_N

            if (xDocXml.documentElement.childNodes.Length <> 1)
              then begin
                // проверяем возможность создания строки по ukpp_n
                Do_FT_5_2;
                {
                if DoCheck_FT_5_2_
                  then Do_FT_5_2;}
              end else begin
                xDoc := xDocXml.documentElement.childNodes.item[0];

                // exchange #2912
                // если значения первых двух знаков IK.LS_UBP равно «30»,
                // то ФТ-5.1.1 иначе ФТ-5.1
                if (POS( '30', aIK_LS_UBP) = 1)
                  then Do_FT_5_1_1
                  else Do_FT_5_1;
              end;
          end;//for//
          finally
            xDocXml.LoadXml( '');
        end;
        finally
          xIndustryCodes.Free;
      end;

      //------------------------------------------------------------------
      // если создан один эд на все строки, передаем его на сервер
{      if NOT aCfg_CreateMultiDoc
        then DoMakeED199_;}
      // exchange #2942
      // если созданный эд еще не отправлен - отправляем его
      DoMakeED199_;
      except
        ExceptionLog( 'Сбой Exch2832_Build199_By_IK_UK');
    end;

end;


begin
end.
