const
  // получение название кода и оффициальное Наименование организации по ID
  cnSQL_OrgNameAndOfficialDescr =
            'select NAME,DESCRIPTION,TAXCODE,KPP from Org where ID=?';



{~FillXmlOrgNameAndOfficialDescr
  Заполнение название кода и оффициальное Наименование Финансового органа
  организации aOrgID в узле XML: aXmlNode.
  заполнение в атрибутах, соотвтетственно:
    aAttrOrgName - название атрибута кода организации;
    aAttrOrgDescription - название атрибута описания организации.
  допускается указание пустого значения aOrgID. в этом случае указанные
    поля aAttrOrgName и aAttrOrgDescription в XML aXmlNode не заполняются.
  например:
    название кода организации = код клиента - 'KOD_UBP';
    название описания организации = наименование клиента - 'NAME_UBP'.}
procedure FillXmlOrgNameAndOfficialDescr( aXmlNode: Variant;
                                          aAttrOrgName: string;
                                          aAttrOrgDescription: string;
                                          aOrgID: string);
begin

    try
      if (LENGTH( aOrgID) > 0) then begin
        aXmlNode.SetAttribute( aAttrOrgName,
                               QueryField( cnSQL_OrgNameAndOfficialDescr,
                                           [ aOrgID],
                                           'NAME'));
        aXmlNode.SetAttribute( aAttrOrgDescription,
                               QueryField( cnSQL_OrgNameAndOfficialDescr,
                                           [ aOrgID],
                                           'DESCRIPTION'));
      end;

      except
        ExceptionLog( 'Сбой Un_Org.FillXmlOrgNameAndOfficialDescr');
    end;

end;


{~GetOrgOfficialName
  возвращает официальное название организации по ид. организации aOrgID.
  ~результат~
    строка оффициального названия органиации.}
function GetOrgOfficialName( aOrgID: string): string;
begin

    try
      if (LENGTH( TRIM( aOrgID)) <= 0)
        then Result := ''
        else Result := QueryField( cnSQL_OrgNameAndOfficialDescr,
                                    [ aOrgID],
                                    'DESCRIPTION');
      except
        ExceptionLog( 'Сбой Un_Org.GetOrgOfficialName');
    end;

end;


{~GetOrgCode
  возвращает код организации по ид. организации aOrgID.
  ~результат~
    строка: код органиации.}
function GetOrgCode( aOrgID: string): string;
begin

    try
      if (LENGTH( TRIM( aOrgID)) <= 0)
        then Result := ''
        else Result := QueryField( cnSQL_OrgNameAndOfficialDescr,
                                    [ aOrgID],
                                    'NAME');
      except
        ExceptionLog( 'Сбой Un_Org.GetOrgCode');
    end;

end;


{~GetOrgKPP
  возвращает КПП по ид. организации aOrgID.
  ~результат~
    строка: код органиации.}
function GetOrgKPP( aOrgID: string): string;
begin

    try
      if (LENGTH( TRIM( aOrgID)) <= 0)
        then Result := ''
        else Result := QueryField( cnSQL_OrgNameAndOfficialDescr,
                                    [ aOrgID],
                                    'KPP');
      except
        ExceptionLog( 'Сбой Un_Org.GetOrgKPP');
    end;

end;


{~GetOrgTaxCode
  возвращает ИНН по ид. организации aOrgID.
  ~результат~
    строка: код органиации.}
function GetOrgTaxCode( aOrgID: string): string;
begin

    try
      if (LENGTH( TRIM( aOrgID)) <= 0)
        then Result := ''
        else Result := QueryField( cnSQL_OrgNameAndOfficialDescr,
                                    [ aOrgID],
                                    'TAXCODE');
      except
        ExceptionLog( 'Сбой Un_Org.GetOrgTaxCode');
    end;

end;


{~GetOrgManagerID
  возвращает организацию «вышестоящего ГРБС» (manager_id) организации aOrgID.
  ~результат~
    строка: ид.органиации «вышестоящего ГРБС».}
function GetOrgManagerID( aOrgID: string): string;
begin

    try
      if (LENGTH( TRIM( aOrgID)) <= 0)
        then Result := ''
        else Result := QueryField( 'select MANAGER_ID from org where id=?',
                                    [ aOrgID],
                                    'MANAGER_ID');
      except
        ExceptionLog( 'Сбой Un_Org.GetOrgManagerID');
    end;

end;


{~ExistsRolesForOrg
 проверка ролей xRoles для организации xOrgID.
  возвращает:
    =true - если организация имеет хотя бы одну из указанных ролей
    =false - организация не имеет ни одной из указанных ролей, либо не
              указана ни организация ни роли. }
function ExistsRolesForOrg( aOrgID, aRoles: string): boolean;
var
  xSql: string;
  xXml: variant;
begin

    try
      Result := FALSE;
      if ( TRIM( aOrgID) = '') then EXIT;
      if ( TRIM( aRoles) = '') then EXIT;
      xSql := FORMAT( 'select * from OrgRoles r where r.org_id=? '
                     +'and r.orgrole_id in (%s)',
                     [ TRIM( aRoles)]);
      xXml := QueryXml( xSql, [ TRIM( aOrgID)]);
      if VarIsNull( xXml) then EXIT;
      Result := ( xXml.documentElement.childNodes.Length > 0);

      except
        ExceptionLog( 'Сбой: Un_Org.ExistsRolesForOrg');
    end;

end;


{~ExistsCredentialsFlagForOrgAccount
 проверяет признак Переданные полномочия ПБС для счета организации.
  возвращает:
    =true - признак для счета организации aOrgAccountID установлен.
    =false - признак не установлен или не указан счет организации}
function ExistsCredentialsFlagForOrgAccount( aOrgAccountID: string): boolean;
var
  xSql: string;
  xXml: variant;
begin

    try
      Result := FALSE;


      if NOT CompareVersionAZK_Finance( 'признак Переданные полномочия ПБС', '2.28', FALSE, FALSE)
        then EXIT;

      if ( TRIM( aOrgAccountID) = '') then EXIT;

      xSql := 'select * from OrgAccount where id=? and credentials_flag=1';
      xXml := QueryXml( xSql, [ TRIM( aOrgAccountID)]);
      if VarIsNull( xXml) then EXIT;
      Result := ( xXml.documentElement.childNodes.Length > 0);

      except
        ExceptionLog( 'Сбой: Un_Org.ExistsCredentialsFlagForOrgAccount');
    end;

end;


{~GetOrgID_By_EstimateID
  получение кода организации по идентификатору владельца бланка расходов.
  параметры
    aEstimateID - идентификатору владельца бланка расходов.
    aOrgID - выходной параметр - код организации.
  ~результат~
    =TRUE - в результирующем параметре aOrgID возвращается строка
            идентификатора организации или пустая строка.
    =FALSE - организация не распознана.}
function GetOrgID_By_EstimateID( aEstimateID: string;
                                 var aOrgID: string): boolean;
begin

    try

      if ( LENGTH( TRIM( aEstimateID)) > 0)
        then Result := ExistsSingleFieldFromSQLEx(
                              'select ORG_ID from ESTIMATE where ID=?',
                              [ aEstimateID],
                              aOrgID)
        else begin
          aOrgID := '';
          Result := FALSE;
        end;

      except
        ExceptionLog( 'Сбой Un_Org.GetOrgID_By_EstimateID');
    end;

end;


{~ExistsRolesAndCredentialsOrgEx
  выполняет проверку ролей (АУ/БУ) для организации aOrgID и признака
  "Переданные полномочия ПБС" для счета организации aOrgAccID.
  возвращает:
    =true - если счет Организации имеет роль Бюджетное или
      Автономное учреждение и Счет финансирования имеет признак "Переданные
      полномочия ПБС". }
function ExistsRolesAndCredentialsOrgEx( aOrgID, aOrgAccID: string): boolean;
begin

    try

      if NOT ExistsCredentialsFlagForOrgAccount( aOrgAccID)
        then Result := FALSE
        else Result := ExistsRolesForOrg( aOrgID, '18,19');

      except
        ExceptionLog( 'Сбой: Un_Org.ExistsRolesAndCredentialsOrgEx');
    end;

end;


{~ExistsRolesAndCredentialsOrg
  выполняет загрузку ид. организации владельца бланка расходов aEstimateID
  с последующей проверкой ролей для этой организации и признака
  "Переданные полномочия ПБС" для счета организации aOrgAccID,
  владельца банка расходов.
  возвращает:
    =true - если Организация-владелец бланка расходов  имеет роль Бюджетное или
      Автономное учреждение и Счет финансирования имеет признак "Переданные
      полномочия ПБС". }
function ExistsRolesAndCredentialsOrg( aEstimateID, aOrgAccID: string): boolean;
var
  xOrgID: string;
begin

    try

      // получаем ID организации владельца бланка расходов
      if NOT GetOrgID_By_EstimateID( aEstimateID, xOrgID)
        then Result := FALSE   // организация не найдена
        else Result := ExistsRolesAndCredentialsOrgEx( xOrgID, aOrgAccID);

      except
        ExceptionLog( 'Сбой: Un_Org.ExistsRolesAndCredentialsOrg');
    end;

end;


{~GetOrgByTaxCodeAndKPPEx
  xPrefixFields = PAY или REC}
function GetOrgByTaxCodeAndKPPEx( var aOrgID: string;
                                   aTaxcode, aKpp: String;
                                   aDocClass: String = '';
                                   aRecPay: String = '';
                                   aUseOrgRef: Boolean = False;
                                   aDoc: Variant = null): Variant;
var
  xXmlOrg: Variant;
  xRecCount: integer;
begin
  try

    try
      Result := NULL;
      aOrgID := '';
      
      try
        if (aTaxCode = '') then begin
          
          EXIT;
        end;
        if (aKPP = '') then begin
          
          EXIT;
        end;
        xXmlOrg := GetOrgByTaxKpp( aTaxCode, aKPP, aDocClass, aRecPay, aUseOrgRef, aDoc);
        // поскольку по ИНН и КПП нельзя четко определить организацию, то на выходе
        // получаем: 0 - вообще не найдено, 1 - найдена одна организация и
        // >1 - найдено несколько организаций.
        xRecCount := xXmlOrg.documentElement.childNodes.Length;
        
        if (xRecCount = 1) then begin
          xXmlOrg := xXmlOrg.documentElement.childNodes.item[0];
          aOrgID  := xXmlOrg.getAttribute('ID');
          
          Result := xXmlOrg;
          EXIT;
        end;
        
        finally
          
      end;
      except
        ExceptionLog( 'Сбой Un_Org.GetOrgByTaxCodeAndKPPEx');
    end;
    finally
      
  end;
end;

{~GetOrgByTaxCodeAndKPP_IntoXml
  }
function GetOrgByTaxCodeAndKPP_IntoXml( xDoc: Variant;
                                        xPrefixFields: string;
                                        var xOrgID: string): Variant;
begin

    try
      Result := GetOrgByTaxCodeAndKPPEx(
                   xOrgID,
                   Trim(xDoc.getAttribute( xPrefixFields + '_TAXCODE')),
                   Trim(xDoc.getAttribute( xPrefixFields + '_KPP')),
                   '5',
                   xPrefixFields);

      except
        ExceptionLog( 'Сбой Un_Org.GetOrgByTaxCodeAndKPP_IntoXml');
    end;

end;

{~GetOrgAccountByOrgID_IntoXml
  }
function GetOrgAccountByOrgID_IntoXml( var xDoc: Variant;
                                       xOrgID: string;
                                       xPrefixFields: string{,
                                       xUpdateDoc: boolean}): Variant;
var
  xLS: string;
  xXmlAcc: Variant;
  xRecCount: integer;
begin
  try
    
    try
      Result := NULL;
      xLS   := xDoc.getAttribute( xPrefixFields + '_ACCOUNT');
      
      try
        xXmlAcc   := GetAccAllRekByOrgIdAccNum( xOrgID, xLS);
        xRecCount := xXmlAcc.documentElement.childNodes.Length;
        
        if (xRecCount = 1) then begin
          Result := xXmlAcc.documentElement.childNodes.item[0];
          
          EXIT;
        end;
        
        finally
          
      end;
      except
        ExceptionLog( 'Сбой Un_Org.UpdateOrgAccountByOrgID_IntoXml');
    end;
    finally
      
  end;
end;

{~GetRespersonByOrgID_IntoXML
  }
function GetRespersonByOrgID_IntoXML( aOrgID: string;
                                      aFilter: string = ''): Variant;
begin
  try
    
    
    
    Result := QueryXml( 'Select Rp.*, a.caption APP_NAME_ '
                       +'from RespPerson Rp '
                       +'join appointment a on rp.appointment_id=a.id '
                       +'Where Rp.Org_Id = ? ' + aFilter,
                       [ aOrgID]);
    
    
    except
      ExceptionLog( 'Сбой Un_Org.GetRespersonByOrgID_IntoXML');
  end;
end;


{~UpdateRespersonM1ByOrgID_IntoXML
  По организации – плательщику заполнять  с ее закладки "Ответственные лица"
  - ФИО и должность руководителя  с кодом -2,
  ФИО и должность гл. бухгалтера - ФИО и должность с кодом -3.

  exchange #2831
  Если у организации не выбраны ответственные лица –
  то из системного параметра «Ответственные лица»
  (Сервис – Системные параметры – Заполнение полей в документах).
  Одноименные поля строки параметра с соответствующими значениями
  полей бюджет и организация, если не найдена строка,
  то брать из строки с соответствующим бюджетом и видом «Все».
  Если определить реквизиты не удалось, то оставлять пустыми.

  параметр aSysParamType:
    '-2' - генеральный
    '-3' - бух

  если параметр aSysParamType и aBudgetID будут =пустые строки (оба параметра),
  то в системных настройках по #2831 поиск не выполняется.}
function UpdateRespersonM1ByOrgID_IntoXML( var xDoc: Variant;
                                            xPrefixFields: string;
                                            aOrgID: string;
                                            aFilter: string = '';
                                            aBudgetID: string = '';
                                            aClass: string = '';
                                            aSysParamType: string = ''): boolean;
var
  xXmlResperson: Variant;
  xNode: Variant;
  i: integer;
  xFound: integer;
  xIndex: integer;
begin

    try
      xXmlResperson := GetRespersonByOrgID_IntoXML( aOrgID, aFilter);
      if (xXmlResperson.documentElement.childNodes.Length = 1) then begin
        xNode := xXmlResperson.documentElement.childNodes.Item[0];
        xDoc.setAttribute( xPrefixFields + 'ID'  , xNode.getAttribute('ID'));
        xDoc.setAttribute( xPrefixFields + 'FIO' , xNode.getAttribute('FIO'));
        xDoc.setAttribute( xPrefixFields + 'POST', xNode.getAttribute('APP_NAME_'));

        Result := TRUE;
      end else begin
        Result := FALSE;
        // exchange #2831 - поиск по системным настройкам, если указан
        // бюджет и тип параметра (класс не обязателен)
        if ((LENGTH( aBudgetID) > 0) AND (LENGTH( aSysParamType) > 0)) then begin
          // получаем данные из сиспараметра
          xXmlResperson := SysParam_RESPONSIBLEPERSONS;

          // в начале ищем с учетом бюджета и организации
          xFound := 0;
          xIndex := -1;

          for i := 0 to xXmlResperson.documentElement.childNodes.Length - 1 do begin
            xNode := xXmlResperson.documentElement.childNodes.Item[i];
            if (LENGTH( aBudgetID) > 0) then
              if (xNode.GetAttribute( 'BudgetID') <> aBudgetID)
                then CONTINUE;
            if (xNode.GetAttribute( 'OrgID') <> aOrgID)
              then CONTINUE;
            if (LENGTH( aClass) > 0) then
              if (xNode.GetAttribute( 'Class') <> aClass)
                then CONTINUE;
            // вид документа – «Электронный документ»
            if (xNode.GetAttribute( 'VidID') <> '0')
              then CONTINUE;
            xIndex := i;
            INC( xFound);
          end;//for//

          // если не нашли или найдено несколько - пробуем найти по параметру
          // бюджета с вид документа = "все"
          if (xFound <> 1) then begin

            xFound := 0;
            for i := 0 to xXmlResperson.documentElement.childNodes.Length - 1 do begin
              xNode := xXmlResperson.documentElement.childNodes.Item[i];
              if (LENGTH( aBudgetID) > 0) then
                if (xNode.GetAttribute( 'BudgetID') <> aBudgetID)
                  then CONTINUE;
              if (xNode.GetAttribute( 'VidID') <> '3')
                then CONTINUE;
              xIndex := i;
              INC( xFound);
            end;//for//
          end;


          // если нашли однозначно - берем эти параметры
          if (xFound = 1) then begin
            xNode := xXmlResperson.documentElement.childNodes.Item[ xIndex];
            case aSysParamType of
              '-2': begin
                    // руководитель
                    xDoc.setAttribute( xPrefixFields + 'ID'  ,
                                       xNode.getAttribute( 'HEAD_ID'));
                    xDoc.setAttribute( xPrefixFields + 'FIO' ,
                                       xNode.getAttribute( 'HEAD_FIO'));
                    xDoc.setAttribute( xPrefixFields + 'POST',
                                       xNode.getAttribute( 'HEAD_POST'));
                  end;
              '-3': begin
                    // бух
                    xDoc.setAttribute( xPrefixFields + 'ID'  ,
                                       xNode.getAttribute( 'BOOKKEEPER_ID'));
                    xDoc.setAttribute( xPrefixFields + 'FIO' ,
                                       xNode.getAttribute( 'BOOKKEEPER_FIO'));
                    xDoc.setAttribute( xPrefixFields + 'POST',
                                       xNode.getAttribute( 'BOOKKEEPER_POST'));
                  end;
            end;//case//
            Result := TRUE;
          end;
        end;
      end;
      except
        ExceptionLog( 'Сбой Un_Org.UpdateRespersonM1ByOrgID_IntoXML');
    end;

end;


{~GetBudgetByOrgCodeEx
  Exchange 2353. постановка пп.1.1.1-1.1.3.
  возвращает ид. бюджета для кода организации aOrgCode и фин.года aFinYear.
  если бюджет не будет найден, будет возвращена пустая строка.}
function GetBudgetByOrgCodeEx( aOrgCode, aFinYear: String): String;
var
  sql: String;
  Xml: Variant;
begin

    try
      { "Код организации" должен быть равен aOrgCode (смотреть сначала на закладке
        "Дополнительно", если там найден и только у одной организации среди
        незакрытых, "Бюджет" брать, указанный для этого кода, продолжая загрузку)}
      Xml := QueryXml(
                 ' Select distinct Obd.Budget_Id'
                +' From OrgBudgetDet Obd'
                +' Join Budget B on Obd.Budget_Id = B.Id '
                // не нужна связь: бюджет-финансовый орган!
                // требует отдельного исправления!
                //+' and Obd.Org_Id = B.Org_Id'
                +' join Org O on O.id = Obd.Org_Id and O.close_date is null'
                +' Where Obd.Code_Fk = ? and B.FinYear = ?',
                [ aOrgCode, aFinYear]);
      if (Xml.documentElement.childNodes.Length <> 1) then begin
        { Если "Код организации" не найден на закладке "Дополнительно",
          то ищем код в поле "Код организации" на закладке "Общая информация".
          Если найдена только одна организация с таким кодом среди незакрытых,
          актуальная только для одного ФО (ФО определяется через связь
          INSPECTOR_ID ), который указан только в одном бюджете текущего
          финансового года в карточке бюджета в поле "Финансовый орган",
          то берем этот Бюджет и продолжаем загрузку,}
        Xml := QueryXml(
                   ' Select distinct B.ID BUDGET_ID'
                  +' From Budget B'
  //-- берем все ФО связанные с бюджетом через inspector_id
                  +' join Org CO on CO.id=B.org_id'
                  +' join contractor C on C.inspector_id=CO.id'
  //-- проверяем, чтобы организация фо имеет признак "фин орган"
                  +' join orgroles COR on COR.org_id=CO.id and COR.orgrole_id=4'
  //-- проверяем организацию, связанную с ФО с по коду
                  +' join Org O on O.Id = C.org_id and O.name = ?'
                  +' and O.close_date is null'
  //-- берем бюджет для этого ФО
                  +' Where B.FinYear = ?',
                 [ aOrgCode, aFinYear]);
        if (Xml.documentElement.childNodes.Length <> 1) then begin
          { Если предыдущее условие не выполняется (организация актуальна для
            нескольких бюджетов), то Бюджет определяется следующим образом:
            ищем код в поле "Код организации" на закладке "Общая информация".
            Если найдена только одна организация с таким кодом среди незакрытых,
            то смотрим в ее карточке ссылку на ППО в поле "Публично-правовое
            образование". Затем, находим в справочнике бюджетов Бюджет текущего
            финансового года, "Публично-правовое образование" которого равно ППО
            организации, берем этот бюджет и продолжаем загрузку.}
          Xml := QueryXml(
                    ' Select O.id From org O'
                   +' Where O.name = ? and O.close_date is null',
                   [ aOrgCode]);
          if (Xml.documentElement.childNodes.Length <> 1) then begin
  
            Result := '';
            EXIT;
          end;
          Xml := QueryXml(
                    ' Select distinct B.id BUDGET_ID'
                   +' From org O'
                   +' Join Budget B on B.ppo_id=O.ppo_id and B.FinYear = ?'
                   +' Where O.name = ? and O.close_date is null',
                   [ aFinYear, aOrgCode]);
          if (Xml.documentElement.childNodes.Length <> 1) then begin
            
            Result := '';
            EXIT;
          end;
        end;
      end;
      Result := Xml.documentElement.childNodes.Item[0].getAttribute('BUDGET_ID');
      except
        ExceptionProc( 'GetBudgetByOrgCodeEx');
    end;

end;

(*
procedure CopyDescriptionRecipientXML( var xTo: Variant;
                                       var xFrom: Variant;
                                       xParams: string = '');
begin
  try
    
    xTo.setAttribute( 'ORG_ID'      , GetNodeAttributeValue( xFrom, '//*/@ID'));
    xTo.setAttribute( 'ORG_TAXCODE' , GetNodeAttributeValue( xFrom, '//*/@TAXCODE'));
    xTo.setAttribute( 'ORG_KPP'     , GetNodeAttributeValue( xFrom, '//*/@KPP'));
    xTo.setAttribute( 'ORG_NAME'    , GetNodeAttributeValue( xFrom, '//*/@CAPTION'));
    
    except
      ExceptionLog( 'Сбой Un_Org.CopyDescriptionRecipientXML');
  end;
end;
*)

{~SearchRecipientID
  }
function SearchRecipientID( aBudgetID{, aKOD_UBP}: string;
                            aZR_INN_UBP, aZR_KPP_UBP, aLS: string;
                            aParamAccount, aParamRoles: string;
 //                           var aDoc: Variant;
                            var aRecipientID: string;
                            aParamsExt: string = ''): boolean;
var
  xXmlOrg: Variant;
begin
  try
    
    Result := SearchRecipientID_ToXML( aBudgetID,
                                       aZR_INN_UBP,
                                       aZR_KPP_UBP,
                                       aLS,
                                       aParamAccount,
                                       aParamRoles,
                                       aRecipientID,
                                       xXmlOrg,
                                       aParamsExt);
    
    except
      ExceptionLog( 'Сбой Un_Org.SearchRecipientID');
  end;
end;

{~SearchRecipientID_ToXML
}
function SearchRecipientID_ToXML( aBudgetID{, aKOD_UBP}: string;
                                  aZR_INN_UBP, aZR_KPP_UBP, aLS: string;
                                  aParamAccount, aParamRoles: string;
                                  var aRecipientID: string;
                                  var aTo: Variant;
                                  aParamsExt: string = ''): boolean;
var
  n: integer;
begin
  try
    
    // счет при поиске организации необязателен

    n := SearchOrgRecipient(
            aBudgetID,
            aZR_INN_UBP,
            aZR_KPP_UBP,
            IfThenElseStr(
                      ( aParamAccount <> ''),
                      #13#10 + FORMAT('%s=%s', [ aParamAccount, aLS]),
                      '')
           +IfThenElseStr(
                      ( aParamRoles <> ''),
                      #13#10'roles=' + aParamRoles,
                      '')
           +aParamsExt,
            aTo);
    Result := (n = 1);

    if Result then aRecipientID := GetNodeAttributeValue( aTo, '//*/@ID');
    
    except
      ExceptionLog( 'Сбой Un_Org.SearchRecipientID_ToXML');
  end;
end;

function SearchOrgRecipient( aBudgetID: string;
                          aZR_INN_UBP, aZR_KPP_UBP: string;
                          aParamsExt: string;
                          var xTo: Variant): integer;
begin
  try
    
    
    
    if (aZR_INN_UBP = '') {OR (xZR_KPP_UBP = '')) }then begin
      
      Result := 0;
      Exit;
    end;
    xTo := SearchOrg(
                 #13#10'active=1'
                +#13#10'budget_id='+aBudgetID
                +#13#10'taxcode='+aZR_INN_UBP
                +#13#10'kpp='+aZR_KPP_UBP
                +aParamsExt);
    Result := xTo.documentElement.childNodes.Length;// = 1);
    
    except
      ExceptionLog( 'Сбой Un_Org.SearchRecipientID');
  end;
end;


{~RecepientDescription
  получение описания (поле DESCRIPTION) бюджетополучателя aRecepientID. }
function RecepientDescription( aRecepientID: string): string;
begin
  try
    
    try
      
      if ( LENGTH( TRIM( aRecepientID)) > 0)
        then Result := QueryField(sqlOrgId, [ aRecepientID], 'DESCRIPTION')
        else Result := '';
      
      except
        ExceptionLog( 'Сбой Un_Org.RecepientDescription');
    end;
    finally
      
  end;
end;



function SearchOrg( aParams: string): Variant;
var
  xSQL: String;
  xWhere: string;
  xFields: string;
  SL: TStringList;
  xParamQuery: Variant;

  procedure AddParamQuery( xVar: Variant; aCount: integer = 1);
  var i: integer;
  begin
    
    
    for i := 1 to aCount do begin
      if VarIsNull( xParamQuery)
        then begin
          
          xParamQuery := VarArrayCreate([0,0], varVariant);
        end else begin
          
          SetLength(xParamQuery, Length( xParamQuery) + 1);
        end;

      xParamQuery[ Length( xParamQuery) - 1] := xVar;
    end;//for//
    
  end;

  procedure AddSQL_( aSubStr: string);
  begin
    if (aSubStr <> '')
      then xSQL := xSQL + ' ' + aSubStr;
  end;

  procedure AddWhere_( aWhereDict, aWhereItem: string);
  begin
    if (xWhere = '')
      then xWhere := aWhereItem
      else xWhere := xWhere + FORMAT( ' %s %s', [ aWhereDict ,aWhereItem]);
  end;

  procedure AddFields_( aWhereItem: string);
  begin
    if (aWhereItem <> '')
      then xFields := ',' + aWhereItem;
  end;

  procedure AddFieldsParam_( aParamName, aParamFields: string);
  begin
    if (SL.IndexOfName( aParamName) <> -1)
      then AddFields_( SL.Values[ aParamFields]);
  end;

  procedure AddFieldsStr_( aParamName, aFields: string);
  begin
    if (SL.IndexOfName( aParamName) <> -1)
      then AddFields_( aFields);
  end;

  procedure AddSQLParam_( aParamName, aTemplate: string);
  begin
    if (SL.IndexOfName( aParamName) <> -1)
      then AddSQL_( aTemplate);
  end;

  procedure AddWhereParamStr_( aWhereDict, aParamName, aTemplate: string;
                               aTemplateForEmptyValue: string = '');
  var
    xParamValue: string;
  begin
    if (SL.IndexOfName( aParamName) <> -1) then begin
      xParamValue := SL.Values[ aParamName];
      if ((xParamValue = '') AND (aTemplateForEmptyValue <> ''))
        then AddWhere_( aWhereDict, FORMAT( aTemplateForEmptyValue, [ xParamValue]))
        else AddWhere_( aWhereDict, FORMAT( aTemplate, [ xParamValue]))
    end;
  end;

  procedure AddWhereParamStrEx_( aWhereDict, aParamName, aTemplate, aTemplValueName: string);
  begin
    if (SL.IndexOfName( aParamName) <> -1)
      then AddWhere_( aWhereDict,
                      StringReplace( aTemplate, aTemplValueName, SL.Values[ aParamName]));
  end;

  procedure AddWhereParam_( aWhereDict, aParamName,
                            aTemplate: string;
                            aTempParamCount: integer;
                            aTemplateForEmptyValue: string = '';
                            aTempForEmptyParamCount: integer = 0);
  var
    xParamValue: string;
  begin
    if (SL.IndexOfName( aParamName) <> -1) then begin
      xParamValue := SL.Values[ aParamName];
      if ((xParamValue = '') AND (aTemplateForEmptyValue <> ''))
        then begin
          AddWhere_( aWhereDict, aTemplateForEmptyValue);
          AddParamQuery( xParamValue, aTempForEmptyParamCount);
        end else begin
          AddWhere_( aWhereDict, aTemplate);
          AddParamQuery( xParamValue, aTempParamCount);
        end;
    end;
  end;

  procedure AddWhereParamEx_( aWhereDict, aParamName, aTemplate: string;
                              aParamCount: integer);
  begin
    if (SL.IndexOfName( aParamName) <> -1) then begin
      AddWhere_( aWhereDict, aTemplate);
      AddParamQuery( SL.Values[ aParamName], aParamCount);
    end;
  end;

begin
  try
    
    
    Result      := NULL;
    xParamQuery := NULL;
    SL:= TStringList.Create;
    try
      SL.Text := aParams;     // разбор параметров

      xSQL   := 'Select distinct O.*%s From Org O';
      xFields:= '';
      xWhere := '';
      
      AddFields_( SL.Values[ 'fields']);

      AddWhereParam_( 'and', 'active', 'O.Close_Date is null', 0);
      AddWhereParam_( 'and', 'closed', 'O.Close_Date is not null', 0);

      AddSQLParam_(
          'budget_id',
          ' Join Contractor C on C.Org_Id = O.Id'
         +' Left Join Budget B on B.Org_Id = C.Inspector_Id');
      AddWhereParam_( 'and', 'budget_id', 'B.Id = ?', 1);

      AddSQLParam_( 'roles', 'Left Join OrgRoles Ors on O.Id = Ors.Org_Id');
      AddWhereParamStr_( 'and', 'roles', 'Ors.OrgRole_Id in (%s)');

      AddSQLParam_( 'not_roles', 'Left Join OrgRoles Ors on O.Id = Ors.Org_Id');
      AddWhereParamStr_( 'and', 'not_roles', 'Ors.OrgRole_Id not in (%s)');

      AddSQLParam_( 'code_fk', 'Join OrgBudgetDet Obd on Obd.Org_Id = O.Id');
      AddWhereParam_( 'and', 'code_fk', 'Obd.Code_Fk = ?', 1);

      AddWhereParam_( 'and', 'taxcode', 'O.Taxcode = ?', 1);
      AddWhereParam_( 'and', 'name', 'O.Name = ?', 1);
      AddWhereParam_( 'and', 'kpp', 'O.Kpp = ?', 1, 'O.Kpp is null');
      AddWhereParam_( 'and', 'id', 'O.ID = ?', 1);

      AddWhereParamEx_(
          'and',
          'account1',
          'exists ('
         +'  select oa1.id from orgaccount oa1'
         +'  where oa1.org_id=o.id'
         +'    and oa1.orgacctype_id in (5)'
         +'    and oa1.DISTRIBUTIVE_FLAG = 0'
         +'    and oa1.ACCOUNT_NUMBER=?'
         +' union'
         +'  select oa2.id from orgaccount oa2'
         +'    join orgaccount oac2 on oac2.id=oa2.KEEPERACC_ID'
         +'  where oa2.org_id=o.id'
         +'     and oa2.orgacctype_id in (1)'
         +'     and oa2.DISTRIBUTIVE_FLAG = 0'
         +'     and oac2.account_number=?)',
          2);

      AddWhereParamEx_(
          'and',
          'account2',
          'exists ('
         +'  select oa1.id from orgaccount oa1'
         +'  where oa1.org_id=o.id'
         +'    and oa1.orgacctype_id in (7)'
         +'    and oa1.DISTRIBUTIVE_FLAG = 0'
         +'    and oa1.ACCOUNT_NUMBER=?'
         +' union'
         +'  select oa2.id from orgaccount oa2'
         +'  join orgaccount oac2 on oac2.id=oa2.KEEPERACC_ID'
         +'  where oa2.org_id=o.id'
         +'     and oa2.orgacctype_id in (6)'
         +'     and oa2.DISTRIBUTIVE_FLAG = 0'
         +'     and oac2.account_number=?)',
          2);

      AddSQLParam_(
          'receiver',
          'Join RECEIVER res on res.Org_Id = O.Id and res.ISDEFAULT = 1'
            // завязка RECEIVER с бюджетом!
            +IfThen( (SL.IndexOfName( 'budget_id') <> -1),
                     ' and res.budget_id=B.id'));
                     
      AddWhereParamEx_(
          'and',
          'account3',
          'exists ('
         +'  select * from orgaccount oa1'
         +'  where oa1.org_id=o.id'
         +'    and ((oa1.orgacctype_id in (8)'
         +'          and oa1.DISTRIBUTIVE_FLAG = 0'
         +'          and oa1.ACCOUNT_NUMBER=?)'
         +'      or (oa1.orgacctype_id in (9)'
         +'          and oa1.ACCOUNT_NUMBER=?)))',
          2);

      AddFieldsParam_( 'manager', 'manager');
      AddSQLParam_( 'manager', 'left join Org omanag on omanag.Id = O.MANAGER_ID');

      xSQL := FORMAT( xSQL, [ xFields]);
      if VarIsNull( xWhere)
        then Result := QueryXml( xSQL, xParamQuery{NULL})
        else Result := QueryXml( xSQL + ' where ' + xWhere, xParamQuery{NULL});
      finally
        SL.Free;
    end;
    
    except
      ExceptionLog( 'Сбой Un_Org.SearchOrg');
  end;
end;


{~GetOrgXML_By_EstimateID
  получение описания организации-оператора (в виде полного описания организации
  в XML) по идентификатору владельца бланка расходов aEstimateID из справочника
  бланка расходов.
  в описании организации так же доступны поля вышестоящего ГРБС:
      MANAGER_NAME - код ГРБС;
      MANAGER_DESCRIPTION - оффициальное название организации.
  ~параметры~
    aEstimateID: ~~(вх,обяз)~~ - ид.владельца бланка расходов;
    aOrgXML: (вых,обяз) - описание организации.
              если результат функции =FALSE, в результате возвращается NULL.
  ~результат~
    =TRUE - организация определена и загружена в параметр aOrgXML;
    =FALSE - организация не распознана.}
function GetOrgXML_By_EstimateID( aEstimateID: string;
                                  var aOrgXML: Variant): boolean;
var
  xOrgID: string;
begin

    Result := FALSE;
    aOrgXML:= NULL;
    try
      // получаем ID организации владельца бланка расходов
      if NOT GetOrgID_By_EstimateID( aEstimateID, xOrgID)
        then begin
          WriteLog('Не удалось определить организацию владельца бланка расходов.');
          EXIT;
        end;

      // описание организации
      aOrgXML := SearchOrg(
                    FORMAT( #13#10'id=%s'
                           +#13#10'manager=omanag.name MANAGER_NAME,'
                                 +'omanag.description MANAGER_DESCRIPTION',
                           [ xOrgID]));
      Result  := NOT( VarIsNull( aOrgXML));
      except
        ExceptionLog( 'Сбой Un_Org.GetOrgXML_By_EstimateID');
    end;

end;


{~SearchOrgByID
  поиск орг по ID.
  дополнительно допускаются дополнительные параметры (в aParams).
  перечисление параметров через #13#10 в виде: параметр=значение.
  результат
    null - если найдено неск или ни одной орг
    узел с описанием орг.}
function SearchOrgByID( aID: string; aParams: string = ''): Variant;
var
  xXml: Variant;
begin

    try
      xXml := SearchOrg( TRIM( 'id=' + aID + #13#10 + aParams));
      if VarIsNULL( xXml)
        then Result := NULL
        else
          if (xXml.DocumentElement.childNodes.Length = 1)
            then Result := xXml.DocumentElement.childNodes.Item[ 0]
            else Result := NULL;

      except
        ExceptionProc( 'SearchOrgByID');
    end;

end;



{~Exch2436_SearchBudgetID
  поиск организации, л/с, бюджета по записи: exchange #2436.

 параметры
   aOrgCode - КПП организации
   aFinYear - финансовый год (например: 2013)
   aLS - номер л/с
   aLS_ID - (вых) - ид.л/с или пустая строка
   aOrg_ID - (вых) - ид.организации или пустая строка
   aCfg_IncludeClosedLS:
           =true - учитывать тольно незакрытые л/с
           =false - проверка закрыточти л/с не учитывается.
 результат
   ид.бюджета или пустая строка


  exchange #2436
2.1.	Искать в справочнике организаций:
2.1.1.	«Код организации» должен быть равен TO.KOD_UBP (смотреть сначала на
закладке Дополнительно в поле «Код УФК», если там найден и только у одной
организации среди незакрытых, «Бюджет» брать, указанный для этого кода,
продолжая загрузку),
2.1.2.	Если Код организации не найден на закладке Дополнительно,
то ищем код в поле «Код организации» на закладке «Общая информация».
Если найдена только одна организация с таким кодом среди незакрытых,
актуальная только для одного ФО (ФО определяется через связь INSPECTOR_ID ),
который указан только в одном бюджете текущего финансового года в карточке
бюджета в поле «Финансовый орган», то берем этот Бюджет и продолжаем загрузку,
2.1.3.	Если условие 2.1.2 не выполняется (организация актуальна для нескольких
бюджетов), то Бюджет определяется следующим образом:
ищем код в поле «Код организации» на закладке «Общая информация».
Если найдена только одна организация с таким кодом среди незакрытых,
то смотрим в ее карточке ссылку на ППО в поле «Публично-правовое образование».
Затем, находим в справочнике бюджетов Бюджет текущего финансового года,
«Публично-правовое образование» которого равно ППО организации,
берем этот бюджет и продолжаем загрузку.
2.1.4.	Дополнительно к предыдущим пунктам в данном бюджете организация должна
иметь роль «Бюджетное учреждение» или «Автономное учреждение», л/с 5 типа
без признака «Распорядительный» и «Бюджетный» без даты закрытия с номером,
равным SP.LS.

 фин.год и тут учитываем стандартно

 помимо этого определяется идентификатор л/с (результат в параметре aLS_ID).
 если определить ид. л/с найти не получится, будет возвращена пустая строка.

 exchange #2516
 пп.2.1.4 - изменения:
   не учитывать значение даты закрытия л/с, рассматривать все л/с,
подходящие по другим параметрам поиска с любой датой закрытия.

 exchange #2516
    добавлен параметр aCfg_IncludeClosedLS:
    если параметр aCfg_IncludeClosedLS=TRUE, то при проверке л/с не проверяется
дата закрытия л/с.
    если параметр aCfg_IncludeClosedLS=FALSE, выполняется проверка л/с, чтобы
дата закрытия л/с не была указана (=null) (по старому - пп.2.1.4 exchange #2436).

  exchange #2600
  алгоритм поиска организации для IK.ZR и др. аналагичен как и для SP.
  поэтому функция используется одна и так же.
  код функции перенесен из модуля Imp_FK_SP сюда по записи exchange #2600.}
function Exch2436_SearchBudgetID( aOrgCode, aFinYear, aLS: String;
                                  var aLS_ID: string;
                                  var aOrg_ID: string;
                                  aCfg_IncludeClosedLS: boolean): String;
var
  xXml: Variant;
  xSQL_Org: string;
  xIndex: integer;
  xSQLCheckClosedLS: string;
begin

    try
      Result := '';
      aLS_ID := '';
      aOrg_ID:= '';

      // exchange #2516
      // если параметр aCfg_IncludeClosedLS=TRUE, исключаем из запросов
      // проверку закрытия л/с.
      if aCfg_IncludeClosedLS
        then xSQLCheckClosedLS := ''
        else xSQLCheckClosedLS := 'and OA.CLOSE_DATE is null ';

      // пп.2.1.4 - подзапрос получения организации по счету 5го типа
      // он используется во всех запросах поиска бюджета и организации.
      // сюда передается только номер счета (OA.ACCOUNT_NUMBER=л/с)
      // а возвращает ID всех организаций, в которых есть указанный л/с
      xSQL_Org := ' select ooo.id from ORG ooo '
                  +' left join ORGACCOUNT OA on OA.ORG_ID = ooo.ID '
                  +' and OA.ORGACCTYPE_ID = 5 and OA.DISTRIBUTIVE_FLAG = 0 '
                  +' and OA.BUDGET_FLAG = 0 ' //and OA.CLOSE_DATE is null '
                  + xSQLCheckClosedLS
                  +' where ooo.ID in (select OROLE.org_id from ORGROLES OROLE '
                  +' where OROLE.ORGROLE_ID in (18, 19)) '
                  +' and OA.ACCOUNT_NUMBER=?';

      // вместо блоков if then else используем набор запросов по порядку.
      // как только номер будет вне диапазона - прерываем цикл.
      // идея в том, что перебираем все возможные алгоритмы по порядку
      // (порядок определяется номером в переменной xIndex), начиная с нулевого
      // номера.
      xIndex := 0;
      repeat

        case xIndex of
          // пп.2.1.1
          0: xXml := QueryXml(
                        FORMAT( ' Select distinct Obd.Budget_Id BUDGET_ID'
                                +' From OrgBudgetDet Obd'
                                +' join org o on o.id=obd.org_id and o.close_date is null '
                                // проверку организации как фин.орган в бюджете
                                // не выполнять!
                                +' Join Budget B on Obd.Budget_Id = B.Id '//and Obd.Org_Id = B.Org_Id'
                                +' Where Obd.Code_Fk = ? and B.FinYear = ?'
                                +' and o.id in (%s)',
                                 [ xSQL_Org]),
                        [ aOrgCode, aFinYear, aLS]);

          // пп.2.1.2
          1: xXml := QueryXml(
                        FORMAT( ' Select distinct b.Id BUDGET_ID From Budget b '
                                +' left join contractor c on c.inspector_id = b.org_id '
                                +' left join org o on o.id=c.org_id and o.close_date is null '
                                +' Where O.Name = ? and B.FinYear = ?'
                                +' and o.id in (%s)',
                                 [ xSQL_Org]),
                        [ aOrgCode, aFinYear, aLS]);

          // пп.2.1.3
          2: xXml := QueryXml(
                        FORMAT( ' Select distinct b.Id BUDGET_ID From Budget b '
                                +' left join org o on o.ppo_id=b.ppo_id and o.close_date is null '
                                +' Where O.Name = ? and B.FinYear = ?'
                                +' and o.id in (%s)',
                                 [ xSQL_Org]),
                        [ aOrgCode, aFinYear, aLS]);

          // все запросы исчерпаны
          else BREAK;
        end;//case//
        // если результат запроса удовлетворяет условию - получаем результат
        // и прерываем цикл
        if (xXml.documentElement.childNodes.Length = 1) then begin
          Result := xXml.documentElement.childNodes.Item[0].getAttribute('BUDGET_ID');
          { теперь нужно определить идентификатор л/с
            в начале определяем организацию.
            в запросах поиска организации будет все тот же подзапрос поиска
            организаций по л/с (xSQL_Org), а в качестве b.id подставляется
            найденный идентификатор бюджета из переменной Result (эта же
            переменная есть результат функции).
            идея в том, что поиск организации должен соотвтетствовать запросу
            поиска бюджета. по сути это тот же запрос поиска бюджета, только
            как результат возвращается не идентификатор бюджета, а организация,
            которая находится в этом бюджете, и в которой есть указанный л/с.}
          case xIndex of
            // 2.1.1
            0: xXml := QueryXml(
                          FORMAT( ' Select distinct o.ID'
                                  +' From OrgBudgetDet Obd'
                                  +' join org o on o.id=obd.org_id and o.close_date is null '
                                  // проверку организации как фин.орган в бюджете
                                  // не выполнять!
                                  +' Join Budget B on Obd.Budget_Id = B.Id '//and Obd.Org_Id = B.Org_Id'
                                  +' Where Obd.Code_Fk = ? and B.FinYear = ? and b.id = ?'
                                  +' and o.id in (%s)',
                                   [ xSQL_Org]),
                          [ aOrgCode, aFinYear, Result, aLS]);

            // 2.1.2
            1: xXml := QueryXml(
                          FORMAT( ' Select distinct o.ID From Budget b '
                                  +' left join contractor c on c.inspector_id = b.org_id '
                                  +' left join org o on o.id=c.org_id and o.close_date is null '
                                  +' Where O.Name = ? and B.FinYear = ? and b.id = ?'
                                  +' and o.id in (%s)',
                                   [ xSQL_Org]),
                          [ aOrgCode, aFinYear, Result, aLS]);

            // 2.1.3
            2: xXml := QueryXml(
                          FORMAT( ' Select distinct o.ID From Budget b '
                                  +' left join org o on o.ppo_id=b.ppo_id and o.close_date is null '
                                  +' Where O.Name = ? and B.FinYear = ? and b.id = ?'
                                  +' and o.id in (%s)',
                                   [ xSQL_Org]),
                          [ aOrgCode, aFinYear, Result, aLS]);
          end;//case//
          if (xXml.documentElement.childNodes.Length = 1) then begin
            aOrg_ID := xXml.documentElement.childNodes.Item[0].getAttribute('ID');

            // теперь определяем ид.л/с по найденной организации
            xXml := QueryXml( ' select OA.id from ORG ooo '
                              +' left join ORGACCOUNT OA on OA.ORG_ID = ooo.ID '
                              +' and OA.ORGACCTYPE_ID = 5 and OA.DISTRIBUTIVE_FLAG = 0 '
                              +' and OA.BUDGET_FLAG = 0 ' //and OA.CLOSE_DATE is null '
                              + xSQLCheckClosedLS
                              +' where ooo.ID in (select OROLE.org_id from ORGROLES OROLE '
                              +' where OROLE.ORGROLE_ID in (18, 19)) '
                              +' and OA.ACCOUNT_NUMBER=? and ooo.ID=?',
                              [ aLS, aOrg_ID]);
            if (xXml.documentElement.childNodes.Length = 1)
              then aLS_ID := xXml.documentElement.childNodes.Item[0].getAttribute('ID');
          end;
          BREAK;
        end;
        // следующий подзапрос
        INC( xIndex);
      until FALSE;
      except
        ExceptionLog( 'Сбой Un_Org.Exch2436_SearchBudgetID');
    end;

end;


begin
end.
