// Модуль описания функций, использующихся только для импорта
{------------------------------------------------------------------------------}
function DoEncodeKBK_KADM( aKBK: string; aCheckKES: boolean = TRUE): string;
begin

    try
      Result := COPY(aKBK, 1, 3);
      except
        ExceptionProc( 'DoEncodeKBK_KADM');
    end;

end;
{------------------------------------------------------------------------------}
function DoEncodeKBK_KES( aKBK: string; aCheckKES: boolean = TRUE): string;
begin

    try
      if aCheckKES
        then Result := COPY( aKBK, LENGTH( aKBK) - 2, 3)  //COPY(aKBK, 18, 3)
        else Result := '';
      except
        ExceptionProc( 'DoEncodeKBK_KES');
    end;

end;
{------------------------------------------------------------------------------}
function DoEncodeKBK_KFSR( aKBK: string; aCheckKES: boolean = TRUE): string;
begin

    try
      Result := COPY(aKBK, 4, 4);
      except
        ExceptionProc( 'DoEncodeKBK_KFSR');
    end;

end;
{------------------------------------------------------------------------------}
function DoEncodeKBK_KSDA( aKBK: string; aCheckKSDA: boolean = TRUE): string;
begin
  try
    Result := COPY(aKBK, 18, 3);
    except
      ExceptionProc( 'DoEncodeKBK_KSDA');
  end;
end;

{------------------------------------------------------------------------------}
function DoEncodeKBK_KCSR( aKBK: string; aCheckKES: boolean = TRUE): string;
begin

    try
      if aCheckKES
        then Result := COPY(aKBK, 8, 7)
        else Result := COPY(aKBK, 8,10);
      except
        ExceptionProc( 'DoEncodeKBK_KCSR');
    end;

end;
{------------------------------------------------------------------------------}
function DoEncodeKBK_KVR( aKBK: string; aCheckKES: boolean = TRUE): string;
begin

    try
      if aCheckKES
        then Result := COPY(aKBK, 15, 3)
        else Result := COPY(aKBK, 18, 3);
      except
        ExceptionProc( 'DoEncodeKBK_KVR');
    end;

end;
{------------------------------------------------------------------------------}
function DoEncodeKBK_KD( aKBK: string; aCheckKES: boolean = TRUE): string;
begin

    try
      if aCheckKES
        then Result := COPY(aKBK, 4, 14)
        else Result := COPY(aKBK, 4, 17);
      except
        ExceptionProc( 'DoEncodeKBK_KD');
    end;

end;
{------------------------------------------------------------------------------}
function DoEncodeKBK_KI( aKBK: string; aCheckKES: boolean = TRUE): string;
begin

    try
      if aCheckKES
        then Result := COPY(aKBK, 4, 14)
        else Result := COPY(aKBK, 4, 17);
      except
        ExceptionProc( 'DoEncodeKBK_KI');
    end;

end;
{------------------------------------------------------------------------------}
{~DoEncodeKBK_Code
  возвращает код КБК (с названием aCode) из строки aKBK.
  aCode - КАДМ/KВД/КД/KВИ/КИ/КФСР/КЦСР/KВР/КР/КОСГУ/КОСГУР/КОСГУД/КОСГУИ
        или KADM/KD/KI/KFSR/KCSR/KVR/KES.
  exchange #3062
  если aCheckKES:
    =true - косгу как раньше 3 последние цифры;
    =false - косгу всегда возвращается как пустая строка. }
function DoEncodeKBK_Code( aKBK, aCode: string;
                           aCheckKES: boolean = TRUE): string;
begin

    try
      case UPPERCASE( aCode) of
        'KАДМ',
        'KADM'        : Result := DoEncodeKBK_KADM( aKBK, aCheckKES);
        'KВД','КД',
        'KD'          : Result := DoEncodeKBK_KD( aKBK, aCheckKES);
        'KВИ','КИ',
        'KI'          : Result := DoEncodeKBK_KI( aKBK, aCheckKES);
        'КФСР','KFSR' : Result := DoEncodeKBK_KFSR( aKBK, aCheckKES);
        'КЦСР','KCSR' : Result := DoEncodeKBK_KCSR( aKBK, aCheckKES);
        'KВР','КР',
        'KVR'         : Result := DoEncodeKBK_KVR( aKBK, aCheckKES);
        'KОСГУ','KES',
        'KОСГУД',
        'KОСГУИ',
        'КОСГУР'      : Result := DoEncodeKBK_KES( aKBK, aCheckKES);

      end;//case//
      except
        ExceptionProc( 'DoEncodeKBK_Code');
    end;

end;
{------------------------------------------------------------------------------}
{~DoEncodeKBK_R
  разделение кода КБК (расходы) на составляющие.
  вынесено отдельно для унификации кода во всех скриптах импорта.
  exchange #3062
  если aCheckKES:
    =true - косгу как раньше 3 последние цифры;
    =false - косгу не выделяется из кбк }
procedure DoEncodeKBK_R( aKBK: string;
                         var aKADMR, aKFSR, aKCSR, aKVR, aKESR: string;
                         aCheckKES: boolean = TRUE);
begin

    try
      aKADMR := DoEncodeKBK_KADM( aKBK, aCheckKES);
      aKFSR := DoEncodeKBK_KFSR( aKBK, aCheckKES);
      aKCSR := DoEncodeKBK_KCSR( aKBK, aCheckKES);
      aKVR := DoEncodeKBK_KVR( aKBK, aCheckKES);
      aKESR := DoEncodeKBK_KES( aKBK, aCheckKES);
      except
        ExceptionProc( 'DoEncodeKBK_R');
    end;

end;
{------------------------------------------------------------------------------}
{~DoEncodeKBK_D
  разделение кода КБК (доходы) на составляющие.
  вынесено отдельно для унификации кода во всех скриптах импорта.
  exchange #3062
  если aCheckKES:
    =true - косгу как раньше 3 последние цифры;
    =false - косгу не выделяется из кбк }
procedure DoEncodeKBK_D( aKBK: string;
                         var aKADMD, aKD, aKESD: string;
                         aCheckKES: boolean = TRUE);
begin

    try
      aKADMD := DoEncodeKBK_KADM( aKBK, aCheckKES);
      aKD := DoEncodeKBK_KD( aKBK, aCheckKES);
      aKESD := DoEncodeKBK_KES( aKBK, aCheckKES);
      except
        ExceptionProc( 'DoEncodeKBK_D');
    end;

end;
{------------------------------------------------------------------------------}
{~DoEncodeKBK_D
  разделение кода КБК (источники) на составляющие.
  вынесено отдельно для унификации кода во всех скриптах импорта.
  exchange #3062
  если aCheckKES:
    =true - косгу как раньше 3 последние цифры;
    =false - косгу не выделяется из кбк }
procedure DoEncodeKBK_I( aKBK: string;
                         var aKADMI, aKI, aKESI: string;
                         aCheckKES: boolean = TRUE);
begin

    try
      aKADMI := DoEncodeKBK_KADM( aKBK, aCheckKES);
      aKI := DoEncodeKBK_KI( aKBK, aCheckKES);
      aKESI := DoEncodeKBK_KES( aKBK, aCheckKES);
      except
        ExceptionProc( 'DoEncodeKBK_I');
    end;

end;
{------------------------------------------------------------------------------}
{~DoEncodeKBK_R_ToXml
  разделение кода КБК (расходы) на составляющие с заполнением отдельных
  полей в узле xml aXmlNode.
  вынесено отдельно для унификации кода во всех скриптах импорта.
  exchange #3062
  если aCheckKES:
    =true - косгу как раньше 3 последние цифры;
    =false - косгу не выделяется из кбк
  параметр aMode - вариант названия атрибутов полей кбк в узле xml:
    =0 - KADMR_CODE,KFSR_CODE,KCSR_CODE,KVR_CODE,KESR_CODE}
procedure DoEncodeKBK_R_ToXml( aKBK: string;
                               aXmlNode: Variant;
                               aCheckKES: boolean = TRUE;
                               aMode: integer = 0);
begin

    try
      aXmlNode.setAttribute( 'KADMR_CODE', DoEncodeKBK_KADM( aKBK, aCheckKES));
      aXmlNode.setAttribute( 'KFSR_CODE', DoEncodeKBK_KFSR( aKBK, aCheckKES));
      aXmlNode.setAttribute( 'KCSR_CODE', DoEncodeKBK_KCSR( aKBK, aCheckKES));
      aXmlNode.setAttribute( 'KVR_CODE', DoEncodeKBK_KVR( aKBK, aCheckKES));
      if aCheckKES
        then aXmlNode.setAttribute( 'KESR_CODE', DoEncodeKBK_KES( aKBK, aCheckKES));
      except
        ExceptionProc( 'DoEncodeKBK_R_ToXml');
    end;

end;
{------------------------------------------------------------------------------}
{~DoEncodeKBK_D_ToXml
  разделение кода КБК (доходы) на составляющие с заполнением отдельных
  полей в узле xml aXmlNode.
  вынесено отдельно для унификации кода во всех скриптах импорта.
  exchange #3062
  если aCheckKES:
    =true - косгу как раньше 3 последние цифры;
    =false - косгу не выделяется из кбк
  параметр aMode - вариант названия атрибутов полей кбк в узле xml:
    =0 - KADMD_CODE,KD_CODE,KESD_CODE}
procedure DoEncodeKBK_D_ToXml( aKBK: string;
                               aXmlNode: Variant;
                               aCheckKES: boolean = TRUE;
                               aMode: integer = 0);
begin

    try
      aXmlNode.setAttribute( 'KADMD_CODE', DoEncodeKBK_KADM( aKBK, aCheckKES));
      aXmlNode.setAttribute( 'KD_CODE', DoEncodeKBK_KD( aKBK, aCheckKES));
      if aCheckKES
        then aXmlNode.setAttribute( 'KESD_CODE', DoEncodeKBK_KES( aKBK, aCheckKES));
      except
        ExceptionProc( 'DoEncodeKBK_D_ToXml');
    end;

end;
{------------------------------------------------------------------------------}
{~DoEncodeKBK_I_ToXml
  разделение кода КБК (источники) на составляющие с заполнением отдельных
  полей в узле xml aXmlNode.
  вынесено отдельно для унификации кода во всех скриптах импорта.
  exchange #3062
  если aCheckKES:
    =true - косгу как раньше 3 последние цифры;
    =false - косгу не выделяется из кбк
  параметр aMode - вариант названия атрибутов полей кбк в узле xml:
    =0 - KADMI_CODE,KI_CODE,KESI_CODE}
procedure DoEncodeKBK_I_ToXml( aKBK: string;
                               aXmlNode: Variant;
                               aCheckKES: boolean = TRUE;
                               aMode: integer = 0);
begin

    try
      aXmlNode.setAttribute( 'KADMI_CODE', DoEncodeKBK_KADM( aKBK, aCheckKES));
      aXmlNode.setAttribute( 'KI_CODE', DoEncodeKBK_KI( aKBK, aCheckKES));
      if aCheckKES
        then aXmlNode.setAttribute( 'KESI_CODE', DoEncodeKBK_KES( aKBK, aCheckKES));
      except
        ExceptionProc( 'DoEncodeKBK_I_ToXml');
    end;

end;

(*
{------------------------------------------------------------------------------}
{~GetCodeKbkDef
  возвращает подицию и длину кода aCodeDef в строке КБК (20символьной).
  aCodeDef - КАДМ/KВД/КД/KВИ/КИ/КФСР/КЦСР/KВР/КР/КОСГУ/КОСГУР/КОСГУД/КОСГУИ.
  exchange #3062
  отменен КОСГУ, поэтому, если aCheckKES=false, для кода КОСГУ
   возвращается длина кода =0, а КИ/КД/КВР расширены на 3 символа.
  если aCheckKES=true - описание кодов как раньше.
  вынесено в отдельную подпрограмму, что позволяет убрать лишние дубли кода.}
procedure GetCodeKbkDef( aCodeDef: String;
                         var aPos, aLength: integer;
                         aCheckKES: boolean = TRUE);
begin

    try
      case aCodeDef of
        'KАДМ'        : begin aPos := 1; aLength := 3; end;
        'KВД','КД'    : begin
                          aPos := 4;
                          if aCheckKES
                            then aLength := 14
                            else aLength := 17;
                        end;
        'KВИ','КИ'    : begin
                          aPos := 4;
                          if aCheckKES
                            then aLength := 14
                            else aLength := 17;
                        end;
        'КФСР'        : begin aPos := 4; aLength := 4; end;
        'КЦСР'        : begin aPos := 8; aLength := 7; end;
        'KВР','КР'    : begin
                          aPos := 15;
                          if aCheckKES
                            then aLength := 3
                            else aLength := 6;
                        end;
        'KОСГУ',
        'KОСГУД',
        'KОСГУИ',
        'КОСГУР'      : begin
                          aPos := 18;
                          if aCheckKES
                            then aLength := 3
                            else aLength := 0;
                        end;
        else
          raise( format( 'Код %s не распознан.', [ aCodeDef]));
      end;//case//

      except
        ExceptionProc( 'GetCodeKbkDef');
    end;

end;
{------------------------------------------------------------------------------}
{~GetCodeFromKBKAndCodeDef
  возвращает код из строки КБК и описание кода: поз и длину подстроки }
function GetCodeFromKBKAndCodeDef( aKbk, aCodeDef: String;
                                   var aPos, aLength: integer;
                                   aCheckKES: boolean = TRUE): string;
begin

    try
      GetCodeKbkDef( aCodeDef, aPos, aLength, aCheckKES);
      if (aLength <= 0)
        then Result := ''
        else Result := COPY( aKBK, aPos, aLength);

      except
        ExceptionProc( 'GetCodeFromKBKAndCodeDef');
    end;

end;
{------------------------------------------------------------------------------}
{~GetCodeFromKBK
  возвращает код БК aCodeDef из КБК aKbk для типа кода aTypeKBK.
  aTypeKBK - тип КБК: 10,20,31/32. прочие коды не допускаются.
  aCodeDef - КАДМ/KВД/КД/KВИ/КИ/КФСР/КЦСР/KВР/КР.
  если по aCodeDef не будет распознан - возбуждается сбой.
  здесь aKbk не расшиняется до 20 символов!

  exchange #3062
  отменен КОСГУ, КД/КИ/КР расширены на длину поля косгу.}
function GetCodeFromKBK( aKbk, aCodeDef: String;
                         aCheckKES: boolean = TRUE): string;
var
  xPos, xLength: integer;
begin

    try
      Result := GetCodeFromKBKAndCodeDef( aKbk, aCodeDef, xPos, xLength, aCheckKES);
      except
        ExceptionProc( 'GetCodeFromKBK');
    end;

end;
*)
{------------------------------------------------------------------------------}
{~DoCheckCodeBC_
  exchange #3062
  оптимизация и унификация кода.
  проверка кода aCode с названием aCodeName (для коментария)
  в бд в таблице aDict.
  если код не найден, в журнале info будет сообщение:
    код=значение не найден в бд.

  параметры:
    aDict - название таблицы (регистронезависимо):
            kes, kd, ki и др.
   aCodeName - название кода (для сообщения в журнале).

  результат:
    =true - код найден
    =false - код не найден и заменен на 0.
              в этом случае aCode содержит нулевой код.    }
function DoCheckCodeBC_( var aCode: string;
                         aBudgetID, aDict, aCodeName: string;
                         aResetBadKbk: boolean;
                         aCheckKES: boolean): boolean;
begin

    try
      if (LENGTH( aCode) <= 0)
        then Result := TRUE
        else
      if KbkExists( aBudgetId, aCode, aDict)
        then Result := TRUE
        else begin
          WriteInfo( Format( 'Значение %s=%s не найдено в справочнике '
                            +'классификаторов бюджета.',
                             [ aCodeName,
                               aCode]));
          if aResetBadKbk then begin
            aCode := FormatKbkEx( aDict, '0', aBudgetID, aCheckKES);
            WriteInfo( FORMAT( 'Замена значения кода %s на системный 0 (%s).',
                                [ aCodeName,
                                  aCode]));
          end;
          Result := FALSE;
        end;
      except
        ExceptionProc( 'DoCheckCodeBC_');
    end;

end;
{------------------------------------------------------------------------------}
{~ValidateKBK_KES
  проверка КОСГУ aCode, если aCheckKES =true.
  если aCheckKES =false, то результат всегда =true, а код aCode всегда
  возвращается пустым.}
function ValidateKBK_KES( var aCode: string;
                          aBudgetId: String;
                          aResetBadKbk: boolean;
                          aCheckKES: boolean = TRUE): boolean;
begin

    try
      if aCheckKES
        then Result := DoCheckCodeBC_( aCode,
                                       aBudgetId,
                                       'kes',
                                       'КОСГУ',
                                       aResetBadKbk,
                                       aCheckKES)
        else begin
          aCode := '';
          Result := TRUE;
        end;
      except
        ExceptionProc( 'ValidateKBK_KES');
    end;

end;
{------------------------------------------------------------------------------}
{~ValidateKBK_KVR
  проверка КВР aCode}
function ValidateKBK_KVR( var aCode: string;
                          aBudgetId: String;
                          aResetBadKbk: boolean): boolean;
begin

    try
      Result := DoCheckCodeBC_( aCode,
                                 aBudgetId,
                                 'kvr',
                                 'КВР',
                                 aResetBadKbk,
                                 TRUE);
      except
        ExceptionProc( 'ValidateKBK_KVR');
    end;

end;
{------------------------------------------------------------------------------}
{~ValidateKBK_KFSR
  проверка КФСР aCode}
function ValidateKBK_KFSR( var aCode: string;
                          aBudgetId: String;
                          aResetBadKbk: boolean): boolean;
begin

    try
      Result := DoCheckCodeBC_( aCode,
                                 aBudgetId,
                                 'kfsr',
                                 'КФСР',
                                 aResetBadKbk,
                                 TRUE);
      except
        ExceptionProc( 'ValidateKBK_KFSR');
    end;

end;
{------------------------------------------------------------------------------}
{~ValidateKBK_KSDA
  проверка Ан.группы aCode}
function ValidateKBK_KSDA( var aCode: string;
                          aBudgetId: String;
                          aResetBadKbk: boolean): boolean;
begin

    try
      Result := DoCheckCodeBC_( aCode,
                                 aBudgetId,
                                 'ksda',
                                 'Ан. группа',
                                 aResetBadKbk,
                                 TRUE);
      except
        ExceptionProc( 'ValidateKBK_KSDA');
    end;

end;
{------------------------------------------------------------------------------}
{~ValidateKbkEx
  Проверяет значения КБК на нахождение в справочниках АЦК.
  Особенностью является то, что проверяется строка КБК, а не документ XML.
  aKbk - строка КБК. в начале выполняется дополнение этой строки до 20-ти
         символов, а потом проверяются коды, в зависимости от типа КБК.
         при отсутствии кодов КБК в БД, если параметр aResetBadKbk=true,
         то значения кодов, в этом параметре, будут заменены на символ "0".
  aTypeKBK - тип КБК: 10,20,31/32. прочие коды не допускаются.
         если обнаружен несовметимый тип КБК, выполняется запись в журнал,
         подпрограммой writeInfo и возвращается TRUE. строка КБК в этом
         случае не проверяется и не меняется!
  aBudgetId - код бюджета для проверки КБК;
  aFilter - фильтр проверки. если строка пустая - проверяются все коды КБК.
         для выборочной проверки нужно указывать названия кодов через запятую.
         регистр названий роли не играет.
         значения названий кодов в фильтре:
           КОСГУ,КАДМ,КФСР,КИ,КД,КЦСР,КВР
 aResetBadKbk - если =true: сбрасывать коды КБК которые отстутсвуют в бд.
         при этом те части кода, которые нужно сбросить в aKbk заменяются
         символом "0".

 в aChangesToZero возвращается:
    =true - были замены кодов на 0.
    =false - замен кодов нет.
    переменная инициализируется в этой подпрограмме.

 exchange #2591
   при переходе на символьные КБК: 0-й код проставляется по маске кода.
   это делается при замене кода на 0, если не найден в справочнике.

 exchange #3062
 отменен КОСГУ, КД/КИ/КР расширены на длину поля косгу.

 возвращает:
   =true, если есть ошибка в каком либо коде БК.
   =false - ошибок нет.
            ошибки нет, если код заменен на 0}
function ValidateKbkEx( var aKbk: string;
                        aTypeKBK, aBudgetId: String;
                        aResetBadKbk: boolean;
                        var aChangesToZero: boolean;
                        aFilter: string = '';
                        aCheckKES: boolean = TRUE): boolean;
var
  xKbkResult: string;
  {----------------------------------------------------------------------------}
  {~ValidateCode_
    проверка кода БК с названием aCodeDef/aCodeDef2.
    aCodeName - название кода в справ. кодов.
    aCodeDef2 - дополнительное название кода в фильтре.
    если в фильтре встретится aCodeDef - берется это название, если aCodeDef2,
    то берется aCodeDef2.
    если фильтр пустой, берется aCodeDef.
    возвращает:
       =true, если есть ошибка в каком либо коде БК.
       =false - ошибок нет.
                ошибки нет, если код заменен на 0}
  function ValidateCode_( aCodeDef, aCodeTable: string;
                          aCodeDef2: string = ''): boolean;
  var
    xCode: string;
    xCodeDef: string;
  begin

      try
        Result := FALSE;

        // выделяем код из строки
        xCode := DoEncodeKBK_Code( aKBK, aCodeTable, aCheckKES);
        if (LENGTH( xCode) <= 0) then begin

          EXIT;
        end;

        // проверяем, нужно ли проверять код КБК по фильтру проверки?
        // или фильтр не указан (пустой) то требуется проверка кода.
        // если фильтр не пустой, ищем, есть ли в фильтре описание
        // кода aCodeDef или aCodeDef2 или aCodeTable.
        // если описания кода нет в фильтре - код не проверяется.
        if (LENGTH( aFilter) <= 0)
          then xCodeDef := aCodeDef
          else begin
            xCodeDef := '';
            if (POS( ',' + aCodeDef + ',', aFilter) > 0)
              then xCodeDef := aCodeDef
              else
                if (LENGTH( aCodeDef2) > 0) then
                  if (POS( ',' + aCodeDef2 + ',', aFilter) > 0)
                    then xCodeDef := aCodeDef2;
            if (LENGTH( xCodeDef) <= 0) then
              if (POS( ',' + UPPERCASE( aCodeTable) + ',', aFilter) > 0)
                then xCodeDef := UPPERCASE( aCodeTable);
          end;

        if (LENGTH( xCodeDef) <= 0) then begin
          xKbkResult := xKbkResult + xCode;

          EXIT;
        end;

        if DoCheckCodeBC_( xCode,
                           aBudgetId,
                           aCodeTable,
                           xCodeDef,
                           aResetBadKbk,
                           aCheckKES)
          then begin
            xKbkResult := xKbkResult + xCode;
            EXIT;
          end;

        if aResetBadKbk
          then aChangesToZero := TRUE;

        xKbkResult := xKbkResult + xCode;

        // ошибки нет, если код заменен на 0
        Result := NOT( aResetBadKbk);
        except
          ExceptionProc( 'ValidateCode_');
      end;

  end;
  {----------------------------------------------------------------------------}
begin

    try
      Result := False;
      aChangesToZero:= FALSE;

      // дополняем КБК до 20-ти символов нулями слева
      aKbk := KBKExtend( aKbk);
      xKbkResult := '';

      // форматируем фильтр
      if (LENGTH( aFilter) > 0)
        then aFilter := ',' + UPPERCASE( aFilter) + ',';


      case aTypeKBK of
        // Доходы KADMD_CODE + KD_CODE + KESD_CODE
        '20': begin
                // здесь нельзя использовать комбинацию: операция OR операция,
                // поскольку куполняется до первой операции, которая возвращает
                // TRUE. последующие операции не будут выполняться.
                if ValidateCode_( 'КАДМ', 'Kadm') then Result := TRUE;
                if ValidateCode_( 'КВД', 'Kd', 'КД') then Result := TRUE;
                if ValidateCode_( 'КОСГУ', 'KES') then Result := TRUE;
              end;

        // Источники KADMI_CODE + KI_CODE + KESI_CODE
        '31','32': begin
                if ValidateCode_( 'КАДМ', 'Kadm') then Result := TRUE;
                if ValidateCode_( 'КВИ', 'Ki', 'КИ') then Result := TRUE;
                if ValidateCode_( 'КОСГУ', 'KES') then Result := TRUE;
              end;


        // Расходы KADMR_CODE, KFSR_CODE, KCSR_CODE, KVR_CODE, KESR_CODE
        '10': begin
                if ValidateCode_( 'КАДМ', 'Kadm') then Result := TRUE;
                if ValidateCode_( 'КФСР', 'Kfsr') then Result := TRUE;
                if ValidateCode_( 'КЦСР', 'Kcsr') then Result := TRUE;
                if ValidateCode_( 'КВР', 'Kvr', 'КР') then Result := TRUE;
                if ValidateCode_( 'КОСГУ', 'KES') then Result := TRUE;
              end;

        else begin
              WriteInfo(
                  FORMAT( 'Не распознан тип КБК (%s). КБК не проверяется.',
                          [ aTypeKBK]));
              Result := True;
              EXIT;
            end;
      end;//case//

      aKbk := xKbkResult;
      except
        ExceptionLog( 'Сбой QueryKbk.ValidateKbkEx');
    end;

end;
{------------------------------------------------------------------------------}
{~ValidateKbk
  Проверяет значения КБК на нахождение в справочниках АЦК.
  Особенностью является то, что проверяется строка КБК, а не документ XML.
  aKbk - строка КБК. в начале выполняется дополнение этой строки до 20-ти
         символов, а потом проверяются коды, в зависимости от типа КБК.
         при отсутствии кодов КБК в БД, если параметр aResetBadKbk=true,
         то значения кодов, в этом параметре, будут заменены на символ "0".
  aTypeKBK - тип КБК: 10,20,31/32. прочие коды не допускаются.
         если обнаружен несовметимый тип КБК, выполняется запись в журнал,
         подпрограммой writeInfo и возвращается TRUE. строка КБК в этом
         случае не проверяется и не меняется!
  aBudgetId - код бюджета для проверки КБК;
  aFilter - фильтр проверки. если строка пустая - проверяются все коды КБК.
         для выборочной проверки нужно указывать названия кодов через запятую.
         регистр названий роли не играет.
         значения названий кодов в фильтре:
           КОСГУ,КАДМ,КФСР,КИ,КД,КЦСР,КВР
 aResetBadKbk - если =true: сбрасывать коды КБК которые отстутсвуют в бд.
         при этом те части кода, которые нужно сбросить в aKbk заменяются
         символом "0".

 exchange #2591
   при переходе на символьные КБК: 0-й код проставляется по маске кода.
   это делается при замене кода на 0, если не найден в справочнике.

 exchange #3062
 отменен КОСГУ, КД/КИ/КР расширены на длину поля косгу.

 возвращает:
   =true, если есть ошибка в каком либо коде БК.
   =false - ошибок нет.
            ошибки нет, если код заменен на 0}
function ValidateKbk( var aKbk: string; aTypeKBK, aBudgetId: String;
                      aResetBadKbk: boolean = TRUE;
                      aFilter: string = '';
                      aCheckKES: boolean = TRUE): Boolean;
var
  xChangesToZero: boolean;
begin

    try
      Result := ValidateKbkEx( aKbk,
                               aTypeKBK,
                               aBudgetId,
                               aResetBadKbk,
                               xChangesToZero,
                               aFilter,
                               aCheckKES);
      except
        ExceptionProc( 'ValidateKbk');
    end;

end;

{~CheckKbkLineED_Ex
  проверка кбк с указанными типом кбк и заполнением полей кодов в строке
  формируемого ЭД: aLineED.
  если код не будет найден в бд - код заменяется на 0, если параметр
  aChangeKBKset0 =true. если aChangeKBKset0=false, то коды сохраняются
  в строке как есть.
  в отличие от CheckKBK ничего лишнее в строку не добавляется.
  парамтры:
    aChangeKBKset0 - замена кбк на 0 (=true) или без замены (false).
  результат:
    =false - все коды есть в бд.
            ошибки нет, если код заменен на 0.
    =true - каких то кодов нет в бд.
  exchange #3062
  выборка кодов кбк из строки - приведение к единообразию кода.

  результат:
    aChangesToZero:
        =true, если коды заменялись на 0.
        =false - замен на 0 небыло.}
function CheckKbkLineED_Ex( aBudgetID, aTypeKBK, aKbk: String;
                         aLineED: Variant;
                         aChangeKBKset0: boolean;
                         var aChangesToZero: boolean;
                         aCheckKES: boolean = TRUE): Boolean;
var
  xCodeKES: string;
begin

    try
      { выполняем проверку КБК с заменой отсутствующих кодов,
        а потом вытаскиваем из строки КБК коды и сохраняем их в строке aLineED.}
      Result := ValidateKbkEx( aKBK,
                               aTypeKBK,
                               aBudgetID,
                               aChangeKBKset0,
                               aChangesToZero,
                               '',
                               aCheckKES);

      // выделяем код косгу из строки кбк
      // а сохранять его будем в разных атрибутах, и только если значение
      // кода будет отлично от пустой строки!
      xCodeKES := DoEncodeKBK_KES( aKBK, aCheckKES);
      case aTypeKBK of
        // Доходы KADMD_CODE + KD_CODE + KESD_CODE
        '20': begin
                aLineED.SetAttribute( 'KADMD_CODE',
                                      DoEncodeKBK_KADM( aKBK, aCheckKES));
                aLineED.SetAttribute( 'KD_CODE',
                                      DoEncodeKBK_KD( aKBK, aCheckKES));
                if (LENGTH( xCodeKES) > 0)
                  then aLineED.SetAttribute( 'KESD_CODE', xCodeKES);
            end;

        // Источники KADMI_CODE + KI_CODE + KESI_CODE
        '31', '32': begin
                aLineED.SetAttribute( 'KADMI_CODE',
                                      DoEncodeKBK_KADM( aKBK, aCheckKES));
                aLineED.SetAttribute( 'KI_CODE',
                                      DoEncodeKBK_KI( aKBK, aCheckKES));
                if (LENGTH( xCodeKES) > 0)
                  then aLineED.SetAttribute( 'KESI_CODE', xCodeKES);
            end;

        // Расходы KADMR_CODE, KFSR_CODE, KCSR_CODE, KVR_CODE, KESR_CODE
        '10': begin
                //KADMR_CODE
                aLineED.SetAttribute( 'KADMR_CODE',
                                      DoEncodeKBK_KADM( aKBK, aCheckKES));
              //KFSR_CODE
                aLineED.SetAttribute( 'KFSR_CODE',
                                      DoEncodeKBK_KFSR( aKBK, aCheckKES));
              //KCSR_CODE
                aLineED.SetAttribute( 'KCSR_CODE',
                                      DoEncodeKBK_KCSR( aKBK, aCheckKES));
              //KVR_CODE
                aLineED.SetAttribute( 'KVR_CODE',
                                      DoEncodeKBK_KVR( aKBK, aCheckKES));
              //KESR_CODE
                if (LENGTH( xCodeKES) > 0)
                  then aLineED.SetAttribute( 'KESR_CODE', xCodeKES);
            end;
      end;//case//
      except
        ExceptionProc( 'CheckKbkLineED_Ex');
    end;

end;


{~CheckKbkLineED
  проверка кбк с указанными типом кбк и заполнением полей кодов в строке
  формируемого ЭД: aLineED.
  если код не будет найден в бд - код заменяется на 0, если параметр
  aChangeKBKset0 =true. если aChangeKBKset0=false, то коды сохраняются
  в строке как есть.
  в отличие от CheckKBK ничего лишнее в строку не добавляется.
  параметры:
    aChangeKBKset0 - замена кбк на 0 (=true) или без замены (false).
  результат:
    =false - все коды есть в бд
            или коды заменялись на 0 и если флаг замены на 0 =true.
    =true - каких то кодов нет в бд
            или коды заменялись на 0 и если флаг замены на 0 =false.}
function CheckKbkLineED( aBudgetID, aTypeKBK, aKbk: String;
                         aLineED: Variant;
                         aChangeKBKset0: boolean;
                         aCheckKES: boolean = TRUE): Boolean;
var
  xChangedToZero: boolean;
begin

    try
      Result := CheckKbkLineED_Ex( aBudgetID,
                                   aTypeKBK,
                                   aKbk,
                                   aLineED,
                                   TRUE,       // замена на 0 всегда
                                   xChangedToZero,
                                   aCheckKES);

      if NOT Result then
        if xChangedToZero then
          if NOT aChangeKBKset0
            then Result := TRUE;
      except
        ExceptionProc( 'CheckKbkLineED');
    end;

end;



{~CheckKbk
  Проверяет значения КБК на нахождение в справочниках АЦК
  Функция заменяет на ноль атрибуты не найденные в справочниках
  здесь учитывается глобальная переменная param_changeKBK:
    замена кбк на 0 (=true) или без замены (false).

  результат:
    =True - значит нашли хотябы один код в файле, которого нет в справочнике.
    =false - все коды есть в бд или код заменан на 0, если замена разрешена.

  На входе:
//Head - шапка документа
//Line - строка с КБК из файла
//fieldKbk - поле содержащее КБК, который надо проверить
//paramDopKbk - параметр схем получения доп КБК. По умолчанию выключен.
  aWithoutPurposefulgrant - определяет, проверять ли атрибут ADD_CLASS
                            как код цели.
                            если =true, то поле проверяется как код цели
                            с заполнением атрибута PURPOSEFULGRANT_ID.
                            если параметр =FALSE, то атрибут ADD_CLASS
                            не проверяется на код цели.
                            по умолчанию значение параметра =FALSE.

  exchange #2591
  при переходе на символьные КБК, при замене кодов на 0, код указывается
  не =0, а с форматированием по маске.
  так же форматируются нулевые доп.коды и прочие нулевые коды.

  exchange #3062
  отмена проверки косгу для эд.
  причем проверка остается выборочной: для ау/бу нужно оставить как есть,
  а для прочих эд - не проверять.
  для этого добавлен параметр aCheckKES:
    =false - проверка без кода КОСГУ;
    =true - проверка как раньше с полем косгу.}
function CheckKbk( Line: Variant;
                    fieldKbk, BudgetId: String;
                    paramDopKbk: String = '';
                    aWithoutPurposefulgrant: boolean = FALSE;
                    aCheckKES: boolean = TRUE{;
                    aChangeKBKset0: boolean = TRUE}): Boolean;

function getBudgetLine(BudgetId, xKBK_Type, xKBK: String): Variant;
var
  XML: Variant;
  sql: String;
begin
  XML := CreateOleObject('Microsoft.XmlDom');
  if (LENGTH(xKBK) = 20) then
  begin
    try
      sql := '';
      case xKBK_Type of

        '20':
        begin
          sql := ' select inc.* ' +
          '   from incbudget inc ' +
          '   where 0 = 0 ' +
          '         and inc.budget_id = ? ' +
          '         and inc.kadmd_code = ? ' +
          '         and inc.kd_code = ? ' +
          '         and inc.fsd_id = ? ';
          XML := QueryXml(sql, [BudgetId,
                       GetNodeAttributeEx( Line, 'KADMD_CODE'), GetNodeAttributeEx( Line, 'KD_CODE'),
                       GetNodeAttributeEx( Line, 'FSD_ID')]);
        end;

        '31', '32':
        begin
          sql := ' select src.* ' +
          '   from srcbudget src ' +
          '   where 0 = 0 ' +
          '         and src.budget_id = ? ' +
          '         and src.kadmi_code = ? ' +
          '         and src.ki_code = ? ' +
          '         and src.fsi_id = ? ';
          XML := QueryXml(sql, [BudgetId,
                       GetNodeAttributeEx( Line, 'KADMI_CODE'), GetNodeAttributeEx( Line, 'KI_CODE'),
                       GetNodeAttributeEx( Line, 'FSI_ID')]);
        end;

        '10':
        begin
          sql := ' select exp.* ' +
          '   from expbudget exp ' +
          '   where 0 = 0 ' +
          '         and exp.budget_id = ? ' +
          '         and exp.estimate_id = ? ' +
          '         and exp.recipient_id = ? ' +
          '         and exp.kadmr_code = ? ' +
          '         and exp.kfsr_code = ? ' +
          '         and exp.kcsr_code = ? ' +
          '         and exp.kvr_code = ? ' +
          '         and exp.fsr_id = ? ';
          XML := QueryXml(sql, [BudgetId, GetNodeAttributeEx( Line, 'ESTIMATE_ID'), GetNodeAttributeEx( Line, 'RECIPIENT_ID'),
                       GetNodeAttributeEx( Line, 'KADMR_CODE'), GetNodeAttributeEx( Line, 'KFSR_CODE'),
                       GetNodeAttributeEx( Line, 'KCSR_CODE'), GetNodeAttributeEx( Line, 'KVR_CODE'),
                       GetNodeAttributeEx( Line, 'FSR_ID')]);
        end;
      end;

    except
      Raise('Ошибка при выполнении запроса для поиска бюджетной строки: ' + LastException.Message);
    end;
  end;
  Result := XML;
end;

var
//  CodeAzk: String;
  xKBK: string;
  xKBK_Type: string;
  xChangesToZero: boolean;
  XmlBudgetLine, XmlElem: Variant;
begin

    try
      Result := False;

      xKBK := GetNodeAttributeEx( Line, fieldKbk);
      xKBK_Type := GetNodeAttributeEx( Line, 'TYPE_KBK');

      if (xKBK_Type = '') or (xKBK = '') then begin
        Line.setAttribute('F_EMPTYKBK', 'True');
        Exit;
      end;

      Line.setAttribute('F_PRIMARYKBK', xKBK);

      { проверяем строку кбк с заменой кодов на 0.
        результат записываем в строку Line.}
      Result := CheckKbkLineED( BudgetId,
                                xKBK_Type,
                                xKBK,
                                Line,
                                param_changeKBK,    // замена на 0
                                aCheckKES);
      if Result then begin
        // какой либо код не найден и заменен на 0
        Line.setAttribute('F_CHANGEDKBK', 'True');
        Line.setAttribute('F_CREATE_DOP', 'False');
      end;

      XmlElem := null;
      if (GetNodeAttributeEx( Line, '__CALC_DOP_CODES') = '1') then
      begin
        XmlBudgetLine := getBudgetLine(BudgetId, xKBK_Type, xKBK);
        if (XmlBudgetLine.getElementsByTagName('ROW').Length <> 0) then
        begin
          if (XmlBudgetLine.getElementsByTagName('ROW').Length = 1) then
          begin
            XmlElem := XmlBudgetLine.getElementsByTagName('ROW').Item[0];
            aWithoutPurposefulgrant := true; //Сами заполняем из строки бюджета
          end
        end;
      end;

//      WriteLog( 'Проверка КБК...');
      case xKBK_Type of
      // Доходы KADMD_CODE + KD_CODE + KESD_CODE
        '20':
        begin
          Line.setAttribute('FSD_ID', 1);
          Line.setAttribute('ANAL_KIND', 1);

          if Copy( GetNodeAttributeEx( Line, 'KD_CODE'), 1, 1) = '3'
          then Line.setAttribute('FSD_ID', 2);

          if NOT VarIsNull( XmlElem) then
          begin
            Line.setAttribute('KDD_CODE', XmlElem.getAttribute('KDD_CODE'));
            Line.setAttribute('KESD_CODE', XmlElem.getAttribute('KESD_CODE'));
            Line.setAttribute('PURPOSEFULGRANTD_ID', XmlElem.getAttribute('PURPOSEFULGRANTD_ID'));
          end else
          begin
            //доп классификацию доходов заполняем нулями
            //(т.к. не приходит в файлах)
            Line.setAttribute('KDD_CODE', FormatKbk( 'KDD', '0', BudgetId));
          end;

        end;


      // Источники KADMI_CODE + KI_CODE + KESI_CODE
        '31', '32':
        begin
          Line.setAttribute('ANAL_KIND', 2);
          Line.setAttribute('FSI_ID', 1);

          if NOT VarIsNull( XmlElem) then
          begin
            Line.setAttribute('KDI_CODE', XmlElem.getAttribute('KDI_CODE'));
            Line.setAttribute('KESI_CODE', XmlElem.getAttribute('KESI_CODE'));
            Line.setAttribute('PURPOSEFULGRANTI_ID', XmlElem.getAttribute('PURPOSEFULGRANTI_ID'));
          end else
          begin
            //доп классификацию источнников заполняем нулями
            // (т.к. не приходит в файлах)
            Line.setAttribute('KDI_CODE', FormatKbk( 'KDI', '0', BudgetId));
          end;
        end;

      // Расходы KADMR_CODE, KFSR_CODE, KCSR_CODE, KVR_CODE, KESR_CODE
        '10':
        begin
          Line.setAttribute('ANAL_KIND', 0);
          Line.setAttribute('FSR_ID', 1);

          if NOT VarIsNull( XmlElem) then
          begin
            Line.setAttribute('KDF_CODE', XmlElem.getAttribute('KDF_CODE'));
            Line.setAttribute('KDE_CODE', XmlElem.getAttribute('KDE_CODE'));
            Line.setAttribute('KDR_CODE', XmlElem.getAttribute('KDR_CODE'));
            Line.setAttribute('KESR_CODE', XmlElem.getAttribute('KESR_CODE'));
            Line.setAttribute('PURPOSEFULGRANT_ID', XmlElem.getAttribute('PURPOSEFULGRANT_ID'));
          end
          else begin
            //доп классификацию расходов заполняем нулями
            // (т.к. не приходит в файлах)
            Line.setAttribute('KDF_CODE', FormatKbk( 'KDF', '0', BudgetId));
            Line.setAttribute('KDE_CODE', FormatKbk( 'KDE', '0', BudgetId));
            Line.setAttribute('KDR_CODE', FormatKbk( 'KDR', '0', BudgetId));
          end;
        end;

      end;//case//

      // если нужно проверять кц - выполняем проверку поля add_klass
      // как кц или доп.коды.
      if NOT aWithoutPurposefulgrant then
        if Check_DopKD_or_Purposefulgrant( Line,
                                           fieldKbk,
                                           BudgetId,
                                           paramDopKbk)
          then Result := TRUE;
      except
        ExceptionProc( 'Un_Import.CheckKbk');
    end;

end;


{~Check_DopKD_or_Purposefulgrant
  определение по полю ADD_KLASS/ADD_CLASS доп кодов или КЦ.
  результат:
    =true - есть ошибки (коды не найдены в бд);
    =false - все норм.

   Код субсидии
   exchange #2410 - поле кода субсидии PURPOSEFULGRANT_ID будем менять
   в зависимости от типа КБК на PURPOSEFULGRANTD_ID и PURPOSEFULGRANTI_ID.
   делать эт будем в секции finally.
   exchange #2977
   для разделов ZN поле кода цели описывается в ADD_CLASS, а не ADD_KLASS.
   поэтому проверяем, если есть поле ADD_CLASS - берем значение оттуда,
   иначе из ADD_KLASS.

  exchange #3062 - поправлены ошибки.}
function Check_DopKD_or_Purposefulgrant( Line: Variant;
                                         fieldKbk, aBudgetId: String;
                                          aParamDopKbk: String): boolean;
var
  xPurposefulgrantAttrName: string;
  xED_PURPOSEFULGRANT_ID: string;
  xPurposefulgrantValue: string;

begin

    try
//      try
        Result := FALSE;

        // exchange #2985
        // исправлена путаница с заполнением поля кода цели в строке эд
        // в начале по полю типа кбк определяем название поля кода цели:
        // PURPOSEFULGRANT_ID/PURPOSEFULGRANTD_ID/PURPOSEFULGRANTI_ID
        // а потом заполняем это поле.
        // если поле типа КБК не указано - будем заполнять поле PURPOSEFULGRANT_ID.
        // раньше заполнялось поле PURPOSEFULGRANT_ID, а потом определялось
        // название поля для эд по типу кбк и значение переносилось
        // из PURPOSEFULGRANT_ID в нужное поле, а исходное PURPOSEFULGRANT_ID
        // заполнялось нулем. для расходной строки, поле PURPOSEFULGRANT_ID
        // заполнялось сразу.
        // при таком заполнении возможны проблемы в дальнейшей обработки эд
        // и формировании дочерних эд по основным и приводило к ошибке,
        // но не всегда.
        // доработка #2410, с заменой значения, не всегда работала коректно.
        xED_PURPOSEFULGRANT_ID := Exch2410_FieldName_PURPOSEFULGRANT_ID(
                                         GetNodeAttributeEx( Line, 'TYPE_KBK'));


        // предварительно заполняем атрибут пустым значением, чтобы атрибут
        // в узле уже был. это дает то, что потом не обязательно проверять
        // значение на наличие поля (т.е. можно не использовать VarToStr).
        Line.setAttribute( xED_PURPOSEFULGRANT_ID, '');

        // exchange #2977
        // определяем, какое поле есть в строке: ADD_KLASS или ADD_CLASS.
        if NOT VarIsNull( Line.getAttribute('ADD_KLASS'))
          then xPurposefulgrantAttrName := 'ADD_KLASS'
          else
            if NOT VarIsNull( Line.getAttribute('ADD_CLASS'))
              then xPurposefulgrantAttrName := 'ADD_CLASS'
              else begin

                EXIT;
              end;

        // получаем значение поля. сохраняем и используем, вместо постоянного
        // обращения к атрибуту
        xPurposefulgrantValue := TRIM( GetNodeAttributeEx(
                                                  Line,
                                                  xPurposefulgrantAttrName));


        // пустое значение - ничего не делаем.
        // кц не заменяем на 0, доп коды не определяем
        if (LENGTH( xPurposefulgrantValue) <= 0) then EXIT;

          //получает доп КБК из кода субсидии по правилу в настройке paramDopKbk.
          // если доп кбк определены, значит поле xPurposefulgrantAttrName
          // задействовано под доп кбк. если не определены - значит в этом
          // поле определен КЦ.
          // exchange #3062 - исправлена ошибка внесенная ранее по #2389.
          // если поле не доп код, тогда это кц, а раньше определялся в начале
          // доп код, а потом кц.

          if not GetDopKbk( Line,
                            aBudgetId,
                            aParamDopKbk,
                            xPurposefulgrantAttrName,
                            xED_PURPOSEFULGRANT_ID) then begin
            // если Доп коды не распознаны, пробуем найти поле ADD_KLASS в БД
            // как код цели

            if KbkExists( aBudgetId,
                          xPurposefulgrantValue,
                          'Purposefulgrant') then begin
                // exchange #2513
                // требуется (по желанию ДЭ) добавить что то в лог,
                // чтобы "подсветить" БЛ.
                // по БЛ (доработка #2198) пустой КЦ может быть, если в
                // справочнике КЦ заблокирован. в этом случае поиск КЦ в справочнике
                // дает истину, а функция GetPurposeFulgrantId вернет пустое
                // значение.
                Line.setAttribute( xED_PURPOSEFULGRANT_ID,
                                   GetPurposeFulgrantId(
                                                      aBudgetId,
                                                      xPurposefulgrantValue));
                // здесь проверяем, если поле КЦ не заполнено
                // в логе пишем дополнительную информацию.
                if (LENGTH( Line.GetAttribute( xED_PURPOSEFULGRANT_ID)) <= 0)
                  then WriteInfo(
                          FORMAT( 'Код цели %s не подставляется в ЭД (строках ЭД),'
                                 +' поскольку, возможно, он заблокирован.',
                                  [ xPurposefulgrantValue]));
            end else begin
              // если код цели не найден в БД - код цели не заполняется,
              // но в этом случае доп.ЭД все равно создавать не нужно
              // exchange #2985 - заполняем нулем
              // это актуально для доп.эд в которых код цели заполняется
              // в зависимости от указания значения во входном файле:
              // если поле заполнено - берется из файла, если нет
              // ищется по #2389 (по set файлам и РЧБ/ДЧБ).
              Line.setAttribute(
                  xED_PURPOSEFULGRANT_ID,
                  Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget( aBudgetId));
  
              // выставляем флаги, что доп.ЭД в этом случае создавать не нужно
              Line.setAttribute('F_CHANGEDKBK', 'True');
              Line.setAttribute('F_CREATE_DOP', 'False');
              // exchange #2389 - переименование "код субсидии" на "код цели"
              WriteInfo(
                  Format( 'Значение Кода цели=%s не найдено в справочнике '
                         +'классификаторов бюджета - заменено на 0;',
                          [ xPurposefulgrantValue]));
              Result := True;
            end;
          end;
  //      finally
        {
          // exchange #2410
          Exch2410_ExchangeField_PURPOSEFULGRANT_ID(
                        Line,
                        Exch2410_FieldName_PURPOSEFULGRANT_ID(
                                       VarToStr( Line.getAttribute('TYPE_KBK'))),
    //                                                  'PURPOSEFULGRANT_ID'),
                        Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget( BudgetId));
                        }
    //  end;

      except
        ExceptionProc( 'Check_DopKD_or_Purposefulgrant');
    end;

end;


{~GetDopKbk
  Функция получает доп кбк по правилу, если удалось получить доп коды, то
  возвращает true.
  exchange #3062 - переработка кода
  добавлен параметр aAttrName - название поля в Line.
    принимает значение: ADD_KLASS или ADD_CLASS.
  добавлен параметр aPURPOSEFULGRANT_Name - название поля КЦ
    принимает значение:
      PURPOSEFULGRANT_ID, PURPOSEFULGRANTD_ID, PURPOSEFULGRANTI_ID }
function GetDopKbk( Line: Variant;
                    aBudgetId,
                    aParamDopKbk,
                    aAttrName,
                    aPURPOSEFULGRANT_Name: String): Boolean;
var
  DopFkLen, DopEkLen, DopKrLen: Integer;
  DopFk, DopEk, DopKr, Sub: String;
  xAttrValue: string;
  xLine_TYPE_KBK: string;
begin

    try
      Result := False;
      if aParamDopKbk = '' then Exit;

      // exchange #3062 - оптимизация кода
      // получаем значение атрибута и в дальнейшем используем его,
      // вместо выполнения каждый раз через Line.getAttribute.
      xAttrValue := GetNodeAttributeEx( Line, aAttrName);
      xLine_TYPE_KBK := GetNodeAttributeEx( Line, 'TYPE_KBK');

      case LOWERCASE( aParamDopKbk) of
        //Exchange #1833
        'камчатская обл.': begin
              if ((Length( xAttrValue) <> 17)
                  or (xLine_TYPE_KBK <> '10')) then begin
                WriteInfo(
                    FORMAT( 'Длина поля %s не равна 17 '
                           +'или тип КБК отличен от расходного.'
                           +' Значение поля %s: %s',
                            [ aAttrName,
                              aAttrName,
                              xAttrValue]));
                Exit;
              end;

              //Вычисляем длину доп кодов.
              DopFkLen := GetLengthCode('KDF', aBudgetId);
              DopEkLen := GetLengthCode('KDE', aBudgetId);
              DopKrLen := GetLengthCode('KDR', aBudgetId);
              if (DopFkLen + DopEkLen + DopKrLen) <> 17 then Exit;
              //вырезаем доп коды из кода субсидии.
              DopFk := Copy( xAttrValue, 1, DopFkLen);
              DopEk := Copy( xAttrValue, DopFkLen + 1, DopEkLen);
              DopKr := Copy( xAttrValue, DopFkLen +  DopEkLen + 1, DopKrLen);
              //проверяем наличие доп кодов в базе
              if (KbkExists( aBudgetId, DopFk, 'Kdf'))
                  and (KbkExists( aBudgetId, DopEk, 'Kde'))
                  and (KbkExists( aBudgetId, DopKr, 'Kdr'))
                then begin
                  Line.setAttribute('KDF_CODE', DopFk);
                  Line.setAttribute('KDE_CODE', DopEk);
                  Line.setAttribute('KDR_CODE', DopKr);
                  Result := True;
                end else
                  WriteInfo('Доп код(ы) не найден(ы) в базе АЦК. ДопФК=' + DopFk + '; ДопЭК=' +
                    DopEk + '; ДопКР=' + DopKr + '.');
            end;

        //Exchange #1970
        'томск': begin
              if ((Length( xAttrValue) < 15) or (Length( xAttrValue) > 20)
                  or (xLine_TYPE_KBK <> '10')) then begin
                WriteInfo(
                    FORMAT( 'Длина поля %s меньше 15 или'
                           +' тип КБК отличен от расходного.'
                           +' Значение поля %s: %s',
                            [ aAttrName,
                              aAttrName,
                              xAttrValue]));
                Exit;
              end;
              //получаем доп коды и код субсидии по маске
              // ххх.ххх.ххх.хххххх.00000
              Sub   := Copy( xAttrValue, 1, 3);
              DopKr := Copy( xAttrValue, 4, 3);
              DopEk := Copy( xAttrValue, 7, 3);
              DopFk := Copy( xAttrValue,10, 6);

              // exchange #2389
              // если первые 3 символа есть в бд - код цели берем по этим символам
              if KbkExists( aBudgetId, Sub, 'Purposefulgrant')
                then Line.setAttribute( aPURPOSEFULGRANT_Name, //'PURPOSEFULGRANT_ID',
                                        GetPurposeFulgrantId( aBudgetId, Sub))
                else
                  if Sub = '000'
                    // exchange #2389
                    // получаем код 0-го значения кода субсидии из БД.
                    // это дает то, что в начале код субсидии ищется как =000
                    // а если не найден - как =0.
                    then Line.setAttribute(
                                aPURPOSEFULGRANT_Name, //'PURPOSEFULGRANT_ID',
                                Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget(
                                                                     aBudgetId))
                    else Exit;

              //проверяем наличие доп кодов и кода субсидии в базе
              if (    (KbkExists( aBudgetId, DopFk, 'Kdf'))
                  and (KbkExists( aBudgetId, DopEk, 'Kde'))
                  and (KbkExists( aBudgetId, DopKr, 'Kdr')))
                then begin
                  Line.setAttribute('KDF_CODE', DopFk);
                  Line.setAttribute('KDE_CODE', DopEk);
                  Line.setAttribute('KDR_CODE', DopKr);
                  Result := True;
                end else
                  WriteInfo('Доп код(ы) или код субсидии не найден(ы) в базе АЦК. ДопФК=' +
                    DopFk + '; ДопЭК=' + DopEk + '; ДопКР=' + DopKr + '.');
            end;
      end;//case//
      except
        ExceptionLog( 'Сбой Un_Import.GetDopKbk');
    end;

end;


{------------------------------------------------------------------------------}
{~CheckChangesKBKtoZeroEx
  проверка изменения кбк на 0 (aChangeKbkToZero)
  и допустимость этого по конфигу - глобальный параметр .

  результат:
    =true - все в норме.
    =false - замена была но по конфигу это запрещено.

  если значение атрибута aXmlLine.F_CHANGEDKBK <> true, то замены кодов кбк
  на 0 не выполнялось.}
function CheckChangesKBKtoZeroEx( aChangeKbkToZero: boolean): boolean;
begin

    try
      if NOT aChangeKbkToZero then begin
        WriteInfo('Пройдена успешно.');
        Result := TRUE;
      end else
        if param_changeKBK then begin
          WriteInfo('Разрешено заменять отсутствующие КБК на 0.');
          Result := TRUE;
        end else begin
          WriteInfo('Запрещено заменять отсутствующие КБК на 0.');
//            WriteInfo('Найдены КБК в файле, которые отсутствуют в базе АЦК.');
          Result := FALSE;
        end;
      except
        ExceptionProc( 'CheckChangesKBKtoZeroEx');
    end;

end;
{------------------------------------------------------------------------------}
{~CheckChangesKBKtoZero
  проверка изменения кбк в строке aXmlLine на 0 и допустимость этого по конфигу.
  результат:
    =true - все в норме.
    =false - замена была но по конфигу это запрещено.

  если значение атрибута aXmlLine.F_CHANGEDKBK <> true, то замены кодов кбк
  на 0 не выполнялось.}
function CheckChangesKBKtoZero( aXmlLine: Variant): boolean;
begin

    try
      Result := CheckChangesKBKtoZeroEx(
                                    AttrToBoolean( aXmlLine, 'F_CHANGEDKBK'));
      except
        ExceptionProc( 'CheckChangesKBKtoZero');
    end;

end;
{------------------------------------------------------------------------------}
{~DoEncodeKBK_KES_or_KVR
  exchange #3081
  берем последние 3 символа из строки кбк aKBK: это может быть либо косгу,
  либо квр.}
function DoEncodeKBK_KES_or_KVR( aKBK: string): string;
begin

    try
      if (LENGTH( aKBK) > 2)
        then Result := COPY( aKBK, LENGTH( aKBK) - 2, 3)
        else Result := '';
      except
        ExceptionProc( 'DoEncodeKBK_KES_or_KVR');
    end;

end;
{------------------------------------------------------------------------------}
{~CheckKES_for_Line_Created_ED_Ex
  exchange #2952
  проверка КОСГУ (название указывается в параметре aFieldKES)
  в строке ЭД aXmlLine, если aCheckKES=true, с заменой отсутствующего кода
  в бд на 0.
  если aCheckKES=false, проверка кода не осуществляется.
  в результате, если код заменяется на 0, то aCheckKES сбрасывается в FALSE.

  значение aFieldKES:
    KESR_CODE - для КОСГУ расходов
    KESD_CODE или KESI_CODE

  exchange #3062
  логическая ошибка: флаг ошибки нужно вынести отдельно от aCheckKES.
  это потому, что если код приходит 000 его проверять не имеет смысла,
  и при этом флаг aCheckKES=false, а на него потом завязки на ошибки.
  здесь будет использоваться глоб.переменная - заменять кбк на 0.
  поэтому переделано в функцию.
  результат:
    =true - все норм.
    =false - есть ошибки}
function CheckKES_for_Line_Created_ED_Ex( aBudgetId: string;
                                           aXmlLine: variant;
                                           aFieldKES: string;
                                           aCheckKES: boolean): boolean;
var
  xKES: string;
begin

    try
      Result := TRUE;
      if aCheckKES then begin
        xKES := GetNodeAttributeEx( aXmlLine, aFieldKES);
        if (LENGTH( xKES) > 0) then begin
          // если КОСГУ нет в БД, проверяем флаг замены кбк на 0.
          // если флаг =true - заменяем КОСГУ на код 0 (по маске)
          // иначе эд не создаем.
          {
          if not KbkExists( aBudgetId, xKES, 'Kes') then begin
            aXmlLine.SetAttribute( aFieldKES,
                                   FormatKbk( 'KES', '0', aBudgetId));}
          // exchange #3062
          // если параметр замены кбк на 0 = нет, то это фатальная ошибка
          if NOT ValidateKBK_KES( xKES, aBudgetId, TRUE, aCheckKES)
            then Result := CheckChangesKBKtoZeroEx( TRUE);
          // сохраняем проверенный код в любом случае
          aXmlLine.SetAttribute( aFieldKES, xKES);
        end;
      end;
      except
        ExceptionProc( 'CheckKES_for_Line_Created_ED_Ex');
    end;

end;
{------------------------------------------------------------------------------}
{~CheckKVR_for_Line_Created_ED_Ex
  exchange #3081
  проверка КВР (название поля указывается в параметре aFieldName)
  в строке ЭД aXmlLine, с заменой отсутствующего кода в бд на 0.
  проверка работает только если aCheckCode=true.

  параметры:
    aFieldName - название кода в строке эд.
                  обычно это KVR_CODE, но для некоторых эд это может быть
                  другое название.

  результат:
    =true - все норм или aCheckCode=false.
    =false - есть ошибки}
function CheckKVR_for_Line_Created_ED_Ex( aBudgetId: string;
                                           aXmlLine: variant;
                                           aFieldName: string;
                                           aCheckCode: boolean): boolean;
var
  xCode: string;
begin

    try
      Result := TRUE;
      if aCheckCode then begin
        xCode := GetNodeAttributeEx( aXmlLine, aFieldName);
        if (LENGTH( xCode) > 0) then begin
          // если КОСГУ нет в БД, проверяем флаг замены кбк на 0.
          // если флаг =true - заменяем КОСГУ на код 0 (по маске)
          // иначе эд не создаем.
          // exchange #3062
          // если параметр замены кбк на 0 = нет, то это фатальная ошибка
          if NOT DoCheckCodeBC_( xCode, aBudgetId, 'kvr', 'КВР', TRUE, FALSE)
            then Result := CheckChangesKBKtoZeroEx( TRUE);
          // сохраняем проверенный код в любом случае
          aXmlLine.SetAttribute( aFieldName, xCode);
        end;
      end;
      except
        ExceptionProc( 'CheckKVR_for_Line_Created_ED_Ex');
    end;

end;
{~CheckKVR_for_Line_Created_ED_Ex
  проверка Аналитической групы (название поля указывается в параметре aFieldName)
  в строке ЭД aXmlLine, с заменой отсутствующего кода в бд на 0.
  проверка работает только если aCheckCode=true.

  параметры:
    aFieldName - название кода в строке эд.
                  обычно это KSDA_CODE, но для некоторых эд это может быть
                  другое название.

  результат:
    =true - все норм или aCheckCode=false.
    =false - есть ошибки}
function CheckKSDA_for_Line_Created_ED_Ex( aBudgetId: string;
                                           aXmlLine: variant;
                                           aFieldName: string;
                                           aCheckCode: boolean): boolean;
var
  xCode: string;
begin

    try
      Result := TRUE;
      if aCheckCode then begin
        xCode := GetNodeAttributeEx( aXmlLine, aFieldName);
        if (LENGTH( xCode) > 0) then begin
          // если Ан.группы нет в БД, проверяем флаг замены кбк на 0.
          // если флаг =true - заменяем КОСГУ на код 0 (по маске)
          // иначе эд не создаем.
          // exchange #3062
          // если параметр замены кбк на 0 = нет, то это фатальная ошибка
          if NOT DoCheckCodeBC_( xCode, aBudgetId, 'ksda', 'Ан.группа', TRUE, FALSE)
            then Result := CheckChangesKBKtoZeroEx( TRUE);
          // сохраняем проверенный код в любом случае
          aXmlLine.SetAttribute( aFieldName, xCode);
        end;
      end;
      except
        ExceptionProc( 'CheckKSDA_for_Line_Created_ED_Ex');
    end;

end;
{------------------------------------------------------------------------------}
{~CheckKES_for_Line_Created_ED
  exchange #2955
  проверка КОСГУ (название указывается в параметре aFieldKES)
  в строке ЭД aXmlLine, если aCheckKES=true с заменой отсутствующего кода
  в бд, если aChangeKBKto0=true.

  результат:
    =true - код проверен успешно (заменен на 0) или код не проверяется;
    =false - кода нет в бд.

  если aCheckKES=false, проверка кода не осуществляется и всегда возвращается
  результат =true.

  если кода нет в бд, то,
    если aChangeKBKto0=true, то код заменяется на 0, и результат =true.
    если aChangeKBKto0=false, то результат =false, и код в стрке не меняется.

  если код в строке имеет пустое значение или отсутствует в стрке,
  то в бд он не проверяется и результат =true.

  значение aFieldKES:
    KESR_CODE - для КОСГУ расходов
    KESD_CODE или KESI_CODE

  если результат подпрограммы =false, то:
    если указано значение параметра aMsg, то в журнале info отображается
    сообщение:
      format( aMsg, [ 'Не найден код КОСГУ ... в БД.']).
    поэтому строка aMsg ожидается в формате: 'ЭД ... не создан. %s' }
function CheckKES_for_Line_Created_ED( aBudgetId: string;
                                       aXmlLine: variant;
                                       aFieldKES: string;
                                       aCheckKES: boolean;
                                       aChangeKBKto0: boolean;
                                       aMsg: string = ''): boolean;
var
  xKES: string;
begin

    try
      Result := TRUE;
      if aCheckKES then begin
        xKES := VarToStr( aXmlLine.GetAttribute( aFieldKES));
        if (LENGTH( xKES) > 0) then
          // если КОСГУ нет в БД, проверяем флаг замены кбк на 0.
          // если флаг =true - заменяем КОСГУ на код 0 (по маске)
          // иначе эд не создаем.
          if not KbkExists( aBudgetId, xKES, 'Kes') then
            if aChangeKBKto0
              then aXmlLine.SetAttribute( aFieldKES,
                                          FormatKbk( 'KES', '0', aBudgetId))
              else begin
                if (LENGTH( aMsg) > 0)
                  then WriteInfo(
                          FORMAT( aMsg,
                                  [ FORMAT( 'Не найден код КОСГУ %s в БД.',
                                            [ xKES])]));
                Result := FALSE;
              end;
      end;

      except
        ExceptionProc( 'CheckKES_for_Line_Created_ED');
    end;

end;
{------------------------------------------------------------------------------}
{~FillKES_for_Line_Created_ED
  exchange #2965
  проверка КОСГУ aValueKES (название указывается в параметре aFieldKES).

  если aValueKES = пусто, заполняется 0м кодом, а результат = FALSE.
  при этом в журнале info ничего не отображается.

  иначе проверяется aValueKES в бд:
    если в бд не найдено, заполняется 0м кодом, а результат = FALSE.
    при этом, если указано сообщение aInfo (шаблон типа "косгу %s не найдено в БД")
    то:
      если значение в параметре aCfg_CreateParamAUBU = нет
        то: отображается сообщение aInfo в журнале info
        иначе: отображается сообщение aInfo + строка "- заменен на 0"
               в журнале info.
    если строка aInfo пустая, сообщение в журнале вообще не отображается.

    если в бд найдено, заполняется значением aValueKES, а результат = TRUE.

  заполнение выполняется в стрке aXmlLine в поле aFieldKES.

  значение aFieldKES:
    KESR_CODE - для КОСГУ расходов
    KESD_CODE или KESI_CODE}
function FillKES_for_Line_Created_ED( aBudgetId: string;
                                           aXmlLine: variant;
                                           aFieldKES: string;
                                           aValueKES: string;
                                           aCfg_CreateParamAUBU: string;
                                           aInfo: string): boolean;
var
  xKES: string;
begin

    try
      if (LENGTH( aValueKES) <= 0) then begin
        aXmlLine.SetAttribute( aFieldKES, FormatKbk( 'KES', '0', aBudgetId));
        Result := FALSE;
      end else
        if KbkExists( aBudgetId, aValueKES, 'Kes') then begin
          aXmlLine.SetAttribute( aFieldKES, aValueKES);
          Result := TRUE;
        end else begin
          aXmlLine.SetAttribute( aFieldKES, FormatKbk( 'KES', '0', aBudgetId));
          Result := FALSE;

          if (LENGTH( aInfo) > 0) then
            if ( LOWERCASE( aCfg_CreateParamAUBU) = 'нет')
              then WriteInfo( FORMAT( aInfo, [ aValueKES]))
              else WriteInfo( FORMAT( aInfo, [ aValueKES]) + ' - заменен на 0');
        end;

      except
        ExceptionProc( 'FillKES_for_Line_Created_ED');
    end;

end;
{------------------------------------------------------------------------------}
{~InsertKES_for_Line_Created_ED
  exchange #2955
  вставка КОСГУ (название указывается в параметре aFieldKES)
  в строке ЭД aXmlLine, с определением проверки кода КОСГУ в переменной.
  реализовано через подпрограмму для упрощения кода.

  например:
  вставка косгу без проверки кода в бд:
  InsertKES_for_Line_Created_ED( строка, 'KESR_CODE', '000', false, xCheckKES);

  вставка косгу с проверкой кода в бд:
  InsertKES_for_Line_Created_ED( строка, 'KESR_CODE', '999', true, xCheckKES);

  проверка осуществляется подпрограммой CheckKES_for_Line_Created_ED.

  в параметре aCheckKES передается значение флага проверки кода косгу в строке,
  а в результате, значение возвращается в параметре aOutCheckKES.
  таким образом при доработке кода, вызов этой подпрограммы заменяет
  две строки кода + логирование для отладки.}
procedure InsertKES_for_Line_Created_ED( aXmlLine: variant;
                                         aFieldKES: string;
                                         aValueKES: string;
                                         aCheckKES: boolean;
                                         var aOutCheckKES: boolean);
begin

    try
      if NOT VarIsNULL( aXmlLine)
        then aXmlLine.SetAttribute( aFieldKES, aValueKES);
      aOutCheckKES := aCheckKES;

      except
        ExceptionProc( 'InsertKES_for_Line_Created_ED');
    end;

end;

begin
end.
