{ Exchange #2389}
var
  vExch2389_paramDopKbk: string;
  vExch2389_KIF_SET: TStringList;
  vExch2389_KDTYPE_SET: TStringList;


{~Initialize_Exch2389
  инициализация модуля.
  здесь нужно указать параметры:
    aDir - базовый каталог (с концевым символом "\") в котором лежат файлы
           настроек: Settings\KIF_KBK.set.
    aExch2389_paramDopKbk - "Схема загрузки доп кодов из поля ADD_KLASS"
                             из конфигурации.}
procedure Initialize_Exch2389( aDir: string;
                               aExch2389_paramDopKbk: string);
begin

    try
      vExch2389_paramDopKbk := aExch2389_paramDopKbk;
      // создаем список настроек KIF_SET и загружаем их из файла
      if FileExists( aDir + 'Settings\KIF_KBK.set')
        then vExch2389_KIF_SET := LoadSettings( aDir + 'Settings\KIF_KBK.set')
        else vExch2389_KIF_SET := TStringList.Create;
      // создаем список настроек KDTYPE_SET и загружаем их из файла
      if FileExists( aDir + 'Settings\KDTYPE_KBK.set')
        then vExch2389_KDTYPE_SET := LoadSettings( aDir + 'Settings\KDTYPE_KBK.set')
        else vExch2389_KDTYPE_SET:= TStringList.Create;

      except
        ExceptionLog( 'Сбой Un_Exch2389.Initialize_Exch2389');
    end;

end;


{~Finalize_Exch2389
  завершение работы с модулем.
  освобождает выделенные ресурсы.}
procedure Finalize_Exch2389;
begin

    try
      if (vExch2389_KIF_SET <> NIL) then begin
        vExch2389_KIF_SET.Free;
        vExch2389_KIF_SET := NIL;
      end;
      if (vExch2389_KDTYPE_SET <> NIL) then begin
        vExch2389_KDTYPE_SET.Free;
        vExch2389_KDTYPE_SET := NIL;
      end;
      except
        ExceptionLog( 'Сбой Un_Exch2389.Finalize_Exch2389;');
    end;

end;



{~Exch2389_GetPurposegrantID_R
  возвратит список ид.кодов целей расходов (из справочника "расходная часть
  бюджета" из таблицы ExpBudget) искомых по бланку расходов
  и КБК (КФСР, КЦСР, КВР, КОСГУ, КВСР) с учетом бюджета (необязательно)
  и КИФ (КВФО) (необязательно) для которых не активизированы параметры
  «Контроль в ФК» и «Заблокировано».
  ид.кодов разделяются символами ";" (если их несколько).

  праметры
   aEstimateID - бланку расходов
   aKFSR - КФСР
   aKCSR - КЦСР
   aKVR - КВР
   aKOSGU - КОСГУ
   aKVCR - КВСР
   aKVFO - КВФО (КИФ) (необязательное) если указано значение, дополнительно
          выполняется поиск с учетом поля FSR_ID. если строка пустая, то
          в запросе КВФО не участвует.
   aBudgetID - бюджет (необязательное) допускается указыват пустую строку.

  результат:
    строка с ид.кодов или одним кодом или пустая строка.}
function Exch2389_GetPurposegrantID_R( aEstimateID: string;
                                       aKFSR: string;
                                       aKCSR: string;
                                       aKVR: string;
                                       aKOSGU: string;
                                       aKVCR: string;
                                       aKVFO: string;
                                       aBudgetID: string): string;
var
  xSQL: string;
  xParams: Variant;
  xXml: Variant;
  i: integer;
  xNode: Variant;
begin

    try
      Result := '';

      // exchange #3062 - убран косгу
      xSQL :=  ' select distinct eb.purposefulgrant_id from expbudget eb'
              +' where eb.estimate_id=? and eb.kfsr_code=?'
              +'      and eb.kcsr_code=? and eb.kvr_code=?'
              +'      and eb.kadmr_code=?';
//              +'      and eb.kesr_code=? and eb.kadmr_code=?';
      xParams := [ aEstimateID, aKFSR, aKCSR, aKVR, {aKOSGU, }aKVCR];

      if (LENGTH( aBudgetID) > 0) then begin

        xSQL := xSQL + ' and eb.budget_id=?';
        VarArrayUnion( xParams, aBudgetID);
      end;

      if (LENGTH( aKVFO) > 0) then begin

        xSQL := xSQL + ' and eb.fsr_id=?';
        VarArrayUnion( xParams, aKVFO);
      end;

      // exchange #2695
      // здесь необходимо проверять флаг "контроль ФК".
      // ищем без учета флага "заблокировано" и c флагом НЕ "контроль ФК"
      xXml := QueryXml(  FORMAT( ' select pg.ID ID from purposefulgrant pg'
                                +' where id in (%s) and pg.CONTROLFK_FLAG=0',
                                 [ xSQL]),
                         xParams);
      // exchange #2695
      // если найден больше одного кода
      // ищем код цели с фильтром НЕ "блокировка" и c флагом НЕ "контроль ФК"
      if (xXml.documentElement.childNodes.Length > 1)
        then xXml := QueryXml(  FORMAT( ' select pg.ID ID from purposefulgrant pg'
                                       +' where id in (%s) and pg.block_flag=0'
                                       +' and pg.CONTROLFK_FLAG=0',
                                        [ xSQL]),
                                xParams);

      for i := 0 to xXml.documentElement.childNodes.Length - 1 do begin
        if (i > 0) then Result := Result + ';';
        xNode := xXml.documentElement.childNodes.Item[ i];
        Result := Result + xNode.GetAttribute('ID');
      end;

      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2389_GetPurposegrantID_R');
    end;

end;


{~Exch2389_GetPurposegrantID_D
  возвратит список ид.кодов целей доходов (из справочника "доходная часть
  бюджета" из таблицы IncBudget) искомых по получателю
  и КБК (Гл.администратор+КВД+КОСГУ) с учетом бюджета (необязательно)
  и КИФ (КВФО) (необязательно) и Доп.КД (необязательно) для которых не
  активизированы параметры «Контроль в ФК» и «Заблокировано».
  ид.кодов разделяются символами ";" (если их несколько).
  exchange #2453
  из-за того, что по коду получателя можно найти неоднозначное число записей
  (больше одной), то пробуем искать добавляя плательщика и территорию.
  изменения по #2453:
  убрана проверка флага "Контроль ФК".
  проверка "блокировка" используется при неоднозначном результате:
  т.е. в начале выполняется поиск без этого флага, а том с этим флагом.
  если однозначно будет найдено без этого флага, то значение флага "блокировка"
  игнорируется. если будет найдено несколько записей, то приоритет отдается
  незаблокированным!
  exchange #2577
  возвращена проверка флага "Контроль ФК" (and pg.CONTROLFK_FLAG=0).
  при этом удален поиск только с четом флага НЕ "контроль в ФК".
  теперь выполняется поиск один раз с фильтром НЕ "блокировка" и НЕ "Контроль ФК"!

  exchange #2494
  выполняется поиск:
    exchange #3013
    в п.1 получатель нужно исключить !
    это не исправлено по #2494 (или ошибка мержа)
    1) КБК+КИФ+доп.КД+тип дохода
        если код будет найден однозначно - возвращается этот код.
        если не будет найдено ни одного кода - результат будет пустым.
    2) если найдено несколько КЦ по получатель+КБК+КИФ+доп.КД+тип дохода, то:
      2.1) если указан плательщик (<>0 и не пустое) и территория (<>0 и не пустое)
            то выполняется поиск:
                получатель+КБК+плательщик+территория+КИФ+доп.КД+тип дохода
            если результат однозначен - возвращаем его.
            если результат пустой или неоднозначен - возвращается результат
            исходного запроса:
              получатель+КБК+КИФ+доп.КД+тип дохода
      2.2) если указан только плательщик (<>0 и не пустое)
            то выполняется поиск:
                получатель+КБК+плательщик+КИФ+доп.КД+тип дохода
            если результат однозначен - возвращаем его.
            если результат пустой или неоднозначен - возвращается результат
            исходного запроса:
              получатель+КБК+КИФ+доп.КД+тип дохода
      2.3) если указана только территория (<>0 и не пустое)
            то выполняется поиск:
              получатель+КБК+территория+КИФ+доп.КД+тип дохода
            если результат однозначен - возвращаем его.
            если результат пустой или неоднозначен - возвращается результат
            исходного запроса:
              получатель+КБК+КИФ+доп.КД+тип дохода.

  праметры
   aReceiverID - получатель
   aADM - Гл.администратор
   aKD - КВД
   aKOSGU - КОСГУ
   aDD - Доп.КД
   aKVFO - КВФО (КИФ) (необязательное) если указано значение, дополнительно
          выполняется поиск с учетом поля FSR_ID. если строка пустая, то
          в запросе КВФО не участвует.
   aBudgetID - бюджет (необязательное) допускается указыват пустую строку.
   aPayID - плательщик
   aTerritoryID - территория
   aKDT_Code - тип дохода
  результат:
    строка с ид.кодов или одним кодом или пустая строка или перечисление
          кодов через ";".
    aResultOneValue (out)
          =true если в результате найден код цели однозначно.}
function Exch2389_GetPurposegrantID_D( aReceiverID: string;
                                       aADM: string;
                                       aKD: string;
                                       aKOSGU: string;
                                       aKVFO: string;
                                       aDD: string;
                                       aBudgetID: string;
                                       aPayID: string;
                                       aTerritoryID: string;
                                       aKDT_Code: string;
                                       var aResultOneValue: boolean): string;

var
  xSQL: string;
  xParams: Variant;
  xSQL_copy: string;
  xParams_copy: Variant;
  n: integer;
  xPayID: string;
  xTerritoryID: string;

  function DoQuery_( var aResultCodes: string): integer;
  var
    i: integer;
    xXml: Variant;
    xNode: Variant;
  begin
    
        try
          // exchange #2577
          // ищем код цели с фильтром НЕ "блокировка" и НЕ "Контроль ФК"

          xXml := QueryXml(  FORMAT( ' select pg.ID ID from purposefulgrant pg'
                                    +' where pg.id in (%s) and pg.block_flag=0'
                                    +' and pg.CONTROLFK_FLAG=0',
                                     [ xSQL]),
                             xParams);
          xXml := xXml.documentElement;

          aResultCodes := '';
          for i := 0 to xXml.childNodes.Length - 1 do begin
            xNode := xXml.childNodes.Item[ i];
            if (i > 0) then aResultCodes := aResultCodes + ';';
            aResultCodes := aResultCodes + xNode.GetAttribute('ID');
          end;

          Result := xXml.childNodes.Length;

          except
            ExceptionLog( 'Сбой Un_Exch2389.Exch2389_GetPurposegrantID_D.DoQuery_');
        end;
    
  end;

  {~DoAppendReceiver_
    добавление в запрос получателя с параметром }
  procedure DoAppendReceiver_;
  begin
  
      try
        xSQL := xSQL + ' and eb.receiver_id=?';
        VarArrayUnion( xParams, aReceiverID);
        except
          ExceptionProc( 'DoAppendReceiver_');
      end;
  
  end;

begin

    try
//      Result := '';

      // в начале пробуем найти по получателю + КБК
      // если будет найден один код цели - он и будет результатом.
      // получатель + КБК

{
      xSQL :=  ' select distinct eb.purposefulgrantd_id from incbudget eb'
              +' where eb.receiver_id=? and eb.kadmd_code=?'
              +'      and eb.kd_code=? and eb.kesd_code=?';
      xParams := [ aReceiverID, aADM, aKD, aKOSGU];
}
      // exchange #3013
      // после доработки #2494 получатель остался. по #3013 убран.
      // exchange #3062 - убран косгу
      xSQL :=  ' select distinct eb.purposefulgrantd_id from incbudget eb'
              +' where eb.kadmd_code=? and eb.kd_code=?';
//              +' where eb.kadmd_code=? and eb.kd_code=? and eb.kesd_code=?';
      xParams := [ aADM, aKD{, aKOSGU}];

      if (LENGTH( aBudgetID) > 0) then begin

        xSQL := xSQL + ' and eb.budget_id=?';
        VarArrayUnion( xParams, aBudgetID);
      end;

      if (LENGTH( aKVFO) > 0) then begin

        xSQL := xSQL + ' and eb.fsd_id=?';
        VarArrayUnion( xParams, aKVFO);
      end;

      if (LENGTH( aDD) > 0) then begin

        xSQL := xSQL + ' and eb.kdd_code=?';
        VarArrayUnion( xParams, aDD);
      end;

      if (LENGTH( aKDT_Code) > 0) then begin

        xSQL := xSQL + ' and eb.kdt_code=?';
        VarArrayUnion( xParams, aKDT_Code);
      end;

//      DoQueryPrepare_;
      n := DoQuery_( Result);
      // при однозначном результате - код цели найден
      if (n = 1) then begin
        aResultOneValue := TRUE;
        EXIT;
      end;
      // если в запросе не найдено ничего - добавлять дополнительные критерии
      // к поиску безсмысленно, поэтому результатом будет пустая строка.
      // при этом результат тоже считается однозначным.
      if (n = 0) then begin
        aResultOneValue := TRUE;
        EXIT;
      end;

      // exchange #3013
      // теперь учитываем получателя

      // сохраняем запрос с параметрами без получателя
      xSQL_copy := xSQL;
      xParams_copy := xParams;
      DoAppendReceiver_;    // добавляем получателя
      n := DoQuery_( Result);
      // при однозначном результате - код цели найден
      if (n = 1) then begin
        aResultOneValue := TRUE;
        EXIT;
      end;
      // если в запросе не найдено ничего - добавлять дополнительные критерии
      // к поиску безсмысленно, поэтому результатом будет пустая строка.
      // при этом результат тоже считается однозначным.
      if (n = 0) then begin
        aResultOneValue := TRUE;
        EXIT;
      end;

      // при неоднозначном результате:
      // проверяем указан плательщик или территория
      // если ни один из этих параметров не указан - больше ничего найти не сможем.
      if (aPayID = '0')
        then xPayID := ''
        else xPayID := aPayID;
      if (aTerritoryID = '0')
        then xTerritoryID := ''
        else xTerritoryID := aTerritoryID;


      if ((xTerritoryID = '') AND (xPayID = '')) then begin
        aResultOneValue := FALSE;
        EXIT;
      end;

      // exchange #3013
      // восстанавливаем запрос без получателя
      xSQL := xSQL_copy;
      xParams := xParams_copy;

      // exchange #2494
      // добавляем плательщика + территорию, причем делаем так:
      // если указан и плательщик и территория - подставляем сразу оба параметра
      // и по оттдельности не ищем.
      // если в результате возвращена одна запись или ничего не найдено,
      // результат тоже считается однозначным.
      if ((xTerritoryID <> '') AND (xPayID <> '')) then begin

//        DoQueryPrepare_;
        xSQL := xSQL + ' and eb.territory_id=? and eb.payer_id=?';
        VarArrayUnion( xParams, xTerritoryID);
        VarArrayUnion( xParams, xPayID);
        if (DoQuery_( Result) <= 1)
          then aResultOneValue := TRUE
          else begin
            // exchange #3013
            // при множественном варианте добавляем получателя
            DoAppendReceiver_;
            if (DoQuery_( Result) > 1)
              then aResultOneValue := FALSE
              else aResultOneValue := TRUE;
          end;
{
            // если неоднозначность или ничего - возвращаемся к первоначальному
            // запросу
            DoQueryPrepare_;
            if (DoQuery_( Result) = 1)
              then aResultOneValue := TRUE
              else aResultOneValue := FALSE;
          end;}
        EXIT;
      end;

      // если указана территория - ищем что указано
      // если в результате возвращена одна запись или ничего не найдено,
      // результат тоже считается однозначным.
      if (xTerritoryID <> '') then begin

        xSQL := xSQL + ' and eb.territory_id=?';
        VarArrayUnion( xParams, xTerritoryID);
        if (DoQuery_( Result) <= 1)
          then aResultOneValue := TRUE
          else begin
            // exchange #3013
            // при множественном варианте добавляем получателя
            DoAppendReceiver_;
            if (DoQuery_( Result) > 1)
              then aResultOneValue := FALSE
              else aResultOneValue := TRUE;
          end;
{        if (DoQuery_( Result) > 1)
          then aResultOneValue := FALSE
          else aResultOneValue := TRUE;}
          {
            // если неоднозначность или ничего - возвращаемся к первоначальному
            // запросу
            DoQueryPrepare_;
            if (DoQuery_( Result) = 1)
              then aResultOneValue := TRUE
              else aResultOneValue := FALSE;
          end;
          }
        EXIT;
      end;

      // если указан плательщик - ищем что указано
      if (aPayID <> '') then begin

        xSQL := xSQL + ' and eb.payer_id=?';
        VarArrayUnion( xParams, xPayID);
        if (DoQuery_( Result) <= 1)
          then aResultOneValue := TRUE
          else begin
            // exchange #3013
            // при множественном варианте добавляем получателя
            DoAppendReceiver_;
            if (DoQuery_( Result) > 1)
              then aResultOneValue := FALSE
              else aResultOneValue := TRUE;
          end;
        {
        if (DoQuery_( Result) > 1)
          then aResultOneValue := FALSE
          else aResultOneValue := TRUE;}
          {
            // если неоднозначность или ничего - возвращаемся к первоначальному
            // запросу
            DoQueryPrepare_;
            if (DoQuery_( Result) = 1)
              then aResultOneValue := TRUE
              else aResultOneValue := FALSE;
          end;
          }
        EXIT;
      end;


      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2389_GetPurposegrantID_D');
    end;

end;


{~Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget
  получение идентификатора нулевого кода цели для указанного бюджета.
  в результате возвращается ид. кода цели =0 или пустая строка.

  пустая строка возвращается, если в БД не завели значение 0 для бюджета.
  проверку на такой случай не делаем.

  чтобы определить, что во входном файле, код цели небыл указан и был указан
  как код цели =0, надо чтобы, в случае, если поле "код цели" отсутствует во
  входном файле, атрибут "код цели" в строке ЭД заполняется пустой строкой.
  а если во входном классе указан код цели, определенный в бд как =0 (не
  указан), то атрибут "код цели" в строке ЭД заполняется заполняется ID
  описания кода цели =0 в БД.}
function Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget( aBudgetID: string): string;
begin

    try
      Result := GetPurposeFulgrantId( aBudgetID, '0');
      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget');
    end;

end;


{~Exch2389_GetAttribute_PURPOSEFULGRANT_ID
  получает значение атрибута "код цели" (по имени aAttributeName)
  из строки ЭД aDopDocLine.
  результат:
    =true - указанный атрибут кода цели присутствует в строке ЭД и значение
            атрибута не пустая строка.
    =false - указанный атрибут кода цели отсутствует в строке ЭД или значение
            атрибута = пустая строка.
  параметры:
    aDopDocLine - строка ЭД, где будем определять код цели;
    aAttributeName - название атрибута кода цели в строке ЭД;
    aPURPOSEFULGRANT_ID (out) - значение параметра кода цели в строке ЭД
                          по имени aAttributeName;
    aComment - комментарий для кода цели (доходный,расходный,источники).}
function Exch2389_GetAttribute_PURPOSEFULGRANT_ID( var aDopDocLine: Variant;
                                                   aAttributeName: string;
                                                   var aPURPOSEFULGRANT_ID: string;
                                                   aComment: string): boolean;
begin

    try
      { если атрибут кода цели не указан - принимаем его как "не указан".
        если указан - по идентификатору кода цели получаем код цели и
        проверяем его на "не указан" (=0).}
      if NOT XmlAttrExists( aDopDocLine, aAttributeName)
        then aPURPOSEFULGRANT_ID := ''
        else aPURPOSEFULGRANT_ID := TRIM( aDopDocLine.GetAttribute( aAttributeName));

      // если код цели небыл указан - ничего не делаем
      if (LENGTH( aPURPOSEFULGRANT_ID) > 0)
        then Result := TRUE
        else Result := FALSE;

      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2389_GetAttribute_PURPOSEFULGRANT_ID');
    end;

end;


{~Exch2389_KIF_Search
  Получение кода КИФ (КВФО) из строка доп.ЭД и указанного бюджета.

   в начале параметр "КИФ" берется из строки доп.ЭД из атрибута aKIF_Attribute:
     1.1) если такого атрибута нет или атрибут пустой см. пп.1.3.
     1.2) если атрибут FSR_ID не пустой, проверяем наличие его значения
          в справочнике "справочник\бухгалтерия\виды финансовго обеспечения".
          если значение в справочнике есть - используем указанный КИФ.
          если такого значения нет - см.пп.1.3.
     1.3) если КИФ не определен, то пробуем найти подходящий КИФ в наборе
          KIF_KBK.set. если там подходящего нет - КИФ не будет использоваться
          для поиска кода цели.

  Параметры:
    aDopDocLine - расходная строка доп.документ (строка нового доп.ЭД):
    aBudgetID - бюджет доп.ЭД.
    aKIF_Attribute - атрибут поля КИФ в строке доп.ЭД. это может быть
                или FSR_ID или FSD_ID.
    aKBK - КБК строки доп.ЭД.
  результат
    КИФ (КВФО) или пустая строка.}
function Exch2389_KIF_Search( var aDopDocLine: Variant;
                              aKIF_Attribute: string;
                              aBudgetID: string;
                              aKBK: string): string;
begin

    try

      if NOT XmlAttrExists( aDopDocLine, aKIF_Attribute)
        then Result := ''
        else Result := TRIM( aDopDocLine.GetAttribute( aKIF_Attribute));



      if (LENGTH( Result) > 0) then begin
        // проверяем наличие КВФО в справочнике
        Result := QueryField(  ' Select ID From FINSOURCE Where ID = ? '
                              +' and IS_ACTIVE=1',
                              [ Result],
                              'ID');
      end;
      // если КИФ (КВФО) так и не определен, берем из KIF_SET
      if (LENGTH( Result) <= 0)
        then Result := MatchesIntoSettings( aKBK, vExch2389_KIF_SET, 0);

      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2389_KIF_Search');
    end;

end;


{~Exch2389_GetResult_PURPOSEFULGRANT_ID
  возвращает в результате код цели: либо из указанного в aResult_ID, либо
  нулевой код цели для указанного бюджета.
  параметры:
    aResult_ID - список кодов целей (через ";"), например строки: '1;234;234'
                  или '123' или ''.
                  разделитель ";" не должен указываться ни последним, ни первым
                  символом в строке!
    aBudgetID - бюджет, для определения нулевого кода цели.
  результат:
    строка идентификатора кода цели.

  если в строке aResult_ID указан только один идентификатор, возвращается
  именно этот идентификатор. если в строке указано несколько кодов,
  возвращается нулевой код цели, определяемый по бюджету.
  если строка aResult_ID пустая, так же возвращается нулевой код цели.}
function Exch2389_GetResult_PURPOSEFULGRANT_ID( aResultID: string;
                                                aBudgetID: string): string;
begin

    try
      // если коды распознаны и если код только один - это и будет
      // искомый код цели
      if (LENGTH( aResultID) <= 0)
        then Result := Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget( aBudgetID)
        else
          if (POS( ';', aResultID) <= 0)
            then Result := aResultID
            else Result := Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget( aBudgetID);

      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2389_GetResult_PURPOSEFULGRANT_ID');
    end;

end;



{~Exch2389_GetPURPOSEFULGRANT_ID_R
  Получение кода цели в расходной строке (КБК=10) доп.ЭД, порожденном от 24-го
  или 5-го классов.

    для доп.ЭД 25/19/79 классов код цели определяется так:
    1) если в наборе KDTYPE_SET есть шаблон 1:маска, то ищем в разрезе
        «Бланк расходов» + «КБК» + «КИФ». если в файле нет 1:маска, или
        по маске не найдено, то поиск кода цели осуществляется в разрезе
        «Бланк расходов» + «КБК».
       где: КИФ (КВФО) определяется подпрограммой Exch2389_KIF_Search.
    2) если в наборе KDTYPE_SET нет шаблона 1:маска или подходящего по маске,
        то поиск кода цели осуществляется в разрезе: «Бланк расходов» + «КБК».

    для доп.ЭД отличных от 25/19/79 классов код цели определяется в разрезе:
  «Бланк расходов» + «КБК».

  exchange #2494
  теперь для всех ЭД ищется в разрезе: «Бланк расходов» + «КБК» + «КИФ».
  для КБК: КФСР+КЦСР+КВР+КОСГУ+КВСР


  Параметры:
    aDopDocLine - расходная строка доп.документ (строка нового доп.ЭД):
    aBudgetID - бюджет доп.ЭД.
    aEstimateID - бланк расходов - обязательный!
    aDopDocClass - класс доп.документа - обязательный!
  результат
    ид. кода цели для строки расходного доп.ЭД по КБК, или пустая строка.}
function Exch2389_GetPURPOSEFULGRANT_ID_R( var aDopDocLine: Variant;
                                            aBudgetID: string;
                                            aEstimateID: string;
                                            aDopDocClass: string): string;
var
  xCodes: string;
  xKFSR_CODE: string;
  xKCSR_CODE: string;
  xKVR_CODE: string;
  xKESR_CODE: string;
  xKADMR_CODE: string;
  xKVFO: string;
  xKBK: string;
begin

    try
      xKFSR_CODE := GetNodeAttributeEx( aDopDocLine, 'KFSR_CODE');
      xKCSR_CODE := GetNodeAttributeEx( aDopDocLine, 'KCSR_CODE');
      xKVR_CODE  := GetNodeAttributeEx( aDopDocLine, 'KVR_CODE');
      xKESR_CODE := GetNodeAttributeEx( aDopDocLine, 'KESR_CODE');
      xKADMR_CODE:= GetNodeAttributeEx( aDopDocLine, 'KADMR_CODE');
      // exchange #3062 - отмена косгу
      // унификация кода: собираем строку кбк без форматирования кода !
      // исправлена ошибка связанная с неверным формированием строки кбк
      xKBK       := GetRuleKBKLine_R( xKADMR_CODE,
                                      xKFSR_CODE,
                                      xKCSR_CODE,
                                      xKVR_CODE,
                                      xKESR_CODE,
                                      '',        // бюджет
                                      FALSE,   // без косгу
                                      '',
                                      FALSE);
//      xKBK       := xKFSR_CODE+xKCSR_CODE+xKVR_CODE+xKESR_CODE+xKADMR_CODE;

      { exchange #2494
        «Бланк расходов» + «КБК» + «КИФ».}
      // определяем КИФ (КВФО)
      xKVFO := Exch2389_KIF_Search(
                    aDopDocLine,
                    'FSR_ID',
                    aBudgetID,
                    xKBK);

      xCodes := Exch2389_GetPurposegrantID_R(
                    aEstimateID,
                    xKFSR_CODE,
                    xKCSR_CODE,
                    xKVR_CODE,
                    xKESR_CODE,
                    xKADMR_CODE,
                    xKVFO,
                    aBudgetID);   // бюджет

      // если коды распознаны и если код только один - это и будет
      // искомый код цели
      Result := Exch2389_GetResult_PURPOSEFULGRANT_ID( xCodes, aBudgetID);

      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2389_GetPURPOSEFULGRANT_ID_R');
    end;

end;


{~Exch2494_KDD_Search
  exchange #3013
  исправлена ошибка в параметрах: не в том месте стоял параметр aReceiverID.}
function Exch2494_KDD_Search(  aADM: string;
                               aKD: string;
                               aKOSGU: string;
                               aKVFO: string;
                               aBudgetID: string;
                               aReceiverID: string;
                               aPayID: string;
                               aTerritoryID: string;
                               aKDT_Code: string): string;
var
  xSQL: string;
  xParams: Variant;
  xSQL_copy: string;
  xParams_copy: Variant;
  xPayID: string;
  xTerritoryID: string;

  function DoQuery_( var aResultCodes: string): boolean;
  var
    n: string;
  begin

      try
        // если найден однозначно - возвращаем этот доп.код как результат
        n := QueryField( ' select count(distinct eb.kdd_code) RES ' + xSQL,
                         xParams,
                         'RES');
        if (n = '1') then begin
          aResultCodes := QueryField(
                                ' select distinct eb.kdd_code KDD' + xSQL,
                                xParams,
                                'KDD');
          Result := TRUE;     // завершаем поиск доп.кода
          EXIT;
        end;

        // неоднозначный результат: доп.код не найден.
        // поэтому возвращаем пустой доп.код
        aResultCodes := '';

        // если ничего не надено - добавляеть дополнительные критерии выборки
        // бесполезно.
        if (n = '0')
          then Result := TRUE     // завершаем поиск доп.кода
          else Result := FALSE;   // продолжаем поиск доп.кода
        except
          ExceptionLog( 'Сбой Un_Exch2389.Exch2494_KDD_Search.DoQuery_');
      end;

  end;

  {~DoAppendReceiver_
    добавление в запрос получателя с параметром }
  procedure DoAppendReceiver_;
  begin
  
      try
        xSQL := xSQL + ' and eb.receiver_id=?';
        VarArrayUnion( xParams, aReceiverID);
        except
          ExceptionProc( 'DoAppendReceiver_');
      end;
  
  end;

begin

    try
      // формируем запрос колучения доп.кода из ДЧБ по:
      // получатель, кбк, бюджет
      // exchange #3013
      // в начале без получателя

      // exchange #3062 - убран косгу
      xSQL := ' from incbudget eb'
             +' where eb.kadmd_code=? and eb.kd_code=?';
//             +' where eb.kadmd_code=? and eb.kd_code=? and eb.kesd_code=?';
      xParams := [ aADM, aKD{, aKOSGU}];

      if (LENGTH( aBudgetID) > 0) then begin

        xSQL := xSQL + ' and eb.budget_id=?';
        VarArrayUnion( xParams, aBudgetID);
      end;

      if (LENGTH( aKVFO) > 0) then begin

        xSQL := xSQL + ' and eb.fsd_id=?';
        VarArrayUnion( xParams, aKVFO);
      end;

      if (LENGTH( aKDT_Code) > 0) then begin

        xSQL := xSQL + ' and eb.kdt_code=?';
        VarArrayUnion( xParams, aKDT_Code);
      end;

      // если найден однозначно - возвращаем этот доп.код как результат
      // если ничего не надено - добавляеть дополнительные критерии выборки
      // бесполезно, поэтому возвращаем пустой доп.код
      if DoQuery_( Result) then EXIT;

      // exchange #3013
      // теперь учитываем получателя

      // сохраняем запрос с параметрами без получателя
      xSQL_copy := xSQL;
      xParams_copy := xParams;
      DoAppendReceiver_;    // добавляем получателя
      if DoQuery_( Result) then EXIT;

      // при неоднозначном результате:
      // проверяем указан плательщик или территория
      // если ни один из этих параметров не указан - больше ничего найти не сможем.
      if (aPayID = '0')
        then xPayID := ''
        else xPayID := aPayID;
      if (aTerritoryID = '0')
        then xTerritoryID := ''
        else xTerritoryID := aTerritoryID;

      if ((xTerritoryID = '') AND (xPayID = '')) then EXIT;

      // exchange #3013
      // восстанавливаем запрос без получателя
      xSQL := xSQL_copy;
      xParams := xParams_copy;

      // exchange #2494
      // добавляем плательщика + территорию, причем делаем так:
      // если указан и плательщик и территория - подставляем сразу оба параметра
      // и по оттдельности не ищем
      if ((xTerritoryID <> '') AND (xPayID <> '')) then begin

//        DoQueryPrepare_;
        xSQL := xSQL + ' and eb.territory_id=? and eb.payer_id=?';
        VarArrayUnion( xParams, xTerritoryID);
        VarArrayUnion( xParams, xPayID);
        // exchange #3013
        // при множественном варианте добавляем получателя
        if NOT DoQuery_( Result) then begin
          DoAppendReceiver_;    // добавляем получателя
          DoQuery_( Result);
        end;
        EXIT;
      end;

      // если указана территория - ищем что указано
      if (xTerritoryID <> '') then begin

        xSQL := xSQL + ' and eb.territory_id=?';
        VarArrayUnion( xParams, xTerritoryID);
        // exchange #3013
        // при множественном варианте добавляем получателя
        if NOT DoQuery_( Result) then begin
          DoAppendReceiver_;    // добавляем получателя
          DoQuery_( Result);
        end;
        EXIT;
      end;

      // если указан плательщик - ищем что указано
      if (aPayID <> '') then begin

        xSQL := xSQL + ' and eb.payer_id=?';
        VarArrayUnion( xParams, xPayID);
        // exchange #3013
        // при множественном варианте добавляем получателя
        if NOT DoQuery_( Result) then begin
          DoAppendReceiver_;    // добавляем получателя
          DoQuery_( Result);
        end;
        EXIT;
      end;


      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2494_KDD_Search');
    end;

end;


{~Exch2389_GetPURPOSEFULGRANT_ID_D
  Получение кода цели в доходной строке (КБК=20) доп.ЭД, порожденном от 24-го
  или 5-го классов.

    для доп.ЭД 25/19/79 классов код цели определяется так:
    1) если в наборе KDTYPE_SET есть шаблон 2:маска, то ищем в разрезе
        «получатель» + «КБК» + «КИФ» + «Доп.КД». если в файле нет 2:маска, или
        по маске не найдено, то поиск кода цели осуществляется в разрезе
        «получатель» + «КБК».
       где:
         КИФ (КВФО) определяется подпрограммой Exch2389_KIF_Search.
         Доп.КД определяется так:
           1.1) в начале ищем атрибут KDD_CODE в строке доп.ЭД.
                если такого поля нет, или указано пусто - см.пп.1.3.
           1.2) если атрибут указан - пробуем найти в справочнике:
                бюджет\классификаторы бюджета\дополнительный доходный код.
                если такого кода в справочнике нет - доп.КД не будет
                участвовать в поиске кода цели.
           1.3) если доп.КД не указан - пробуем найти его в ДЧБ в разрезе:
                «получатель» + «КБК». если код будет один - используем его при
                поиске кода цели.
           выполняется поиск Доп.КД ид ДЧБ (бюджет\просмотр доходной части бюджета).
           если определяется один код, тогда он используется для выборки кода цели.
           если найдено несколько кодов, то при поиске кода цели Доп.КД не используется.


    2) если в наборе KDTYPE_SET нет шаблона 2:маска или подходящего по маске,
        то поиск кода цели осуществляется в разрезе: «получатель» + «КБК».

    для доп.ЭД отличных от 25/19/79 классов код цели определяется в разрезе:
  «получатель» + «КБК».

  exchange #2494
  теперь для всех ЭД ищется в разрезе:
     «получатель» + «КБК» + «КИФ» [+ «Доп.КД»] [ + плательщик] + [территория].
  для КБК: КФСР+КЦСР+КВР+КОСГУ+КВСР.
    если доп.код =0, то в начале выполняется поиск кода цели с доп.кодом =0,
  и если код цели не найден однозначно - выполняется поиск кода цели без доп.кода.

  Параметры:
    aDopDocLine - расходная строка доп.документ (строка нового доп.ЭД):
    aBudgetID - бюджет доп.ЭД.
    aReceiverID - получатель - обязательный!
    aDopDocClass - класс доп.документа - обязательный!
    aKDT_Code - тип дохода.
              добавлен для доходных ЭД по exchange #2494.
              проблема в том, что в некоторых ЭД это поле определено в шапке,
              а в некоторых - в строках.
              для расходов и источников этот параметр не учитывается и его
              можно заполнять пустой строкой.
  результат
    ид. кода цели для строки расходного доп.ЭД по КБК, или пустая строка.}
function Exch2389_GetPURPOSEFULGRANT_ID_D( var aDopDocLine: Variant;
                                            aBudgetID: string;
                                            aReceiverID: string;
                                            aDopDocClass: string;
                                            aPayID: string;
                                            aTerritoryID: string;
                                            aKDT_Code: string): string;
var
  xCodes: string;
  xKADM_CODE : string;
  xKD_CODE   : string;
  xKESD_CODE : string;
  xKDD: string;
  xKVFO: string;
  xKBK: string;
  xFoundOneValue: boolean;
  xKDDIsZERO: boolean;
begin

    try
      xKADM_CODE := GetNodeAttributeEx( aDopDocLine, 'KADMD_CODE');
      xKD_CODE   := GetNodeAttributeEx( aDopDocLine, 'KD_CODE');
      xKESD_CODE := GetNodeAttributeEx( aDopDocLine, 'KESD_CODE');
      // exchange #3062
      // унификация кода: собираем строку кбк без форматирования кода !
      xKBK       := GetRuleKBKLine_D( xKADM_CODE,
                                      xKD_CODE,
                                      xKESD_CODE,
                                      '',       // бюджет здесь не нужен
                                      FALSE,   // без косгу
                                      '',
                                      FALSE);
//      xKBK       := xKADM_CODE + xKD_CODE + xKESD_CODE;

      { exchange 2494
        «получатель» + «КБК» + «КИФ» + «Доп.КД»}

      // определяем КИФ (КВФО)
      xKVFO := Exch2389_KIF_Search(
                    aDopDocLine,
                    'FSD_ID',
                    aBudgetID,
                    xKBK);

      // определяем доп.код из строки ЭД
      xKDD := TRIM( VarToStr( aDopDocLine.GetAttribute( 'KDD_CODE')));

      if (LENGTH( xKDD) <= 0)
        then xKDDIsZERO := FALSE
        else
          // проверяем указанный доп.КД в справочнике
          // если такой код не будет найден - считаем что доп.Код не указан.
          if ( QueryField(  ' Select COUNT( * ) N From KDD '
                           +' Where Budget_Id = ? And CODE = ? ',
                            [ aBudgetID,
                              xKDD],
                            'N') <> '1')
            then begin
              xKDD       := '';
              xKDDIsZERO := FALSE;    // доп.код не указан
            end else
              // exchange #2494
              // если доп.код указан =0 - ищем его в ДЧБ
              // exchange #3013
              // поскольку теперь доп.код определен по маске, то вместо =0
              // будем проверять на <>'' и замену всех нулей на ''.
              // т.е. код 0 и код 00 будут равноценны и равны пустой строке.
              if (StringReplace(xKDD, '0', '') <> '')
                then xKDDIsZERO := FALSE     // доп.код <>0
                else begin
                  xKDDIsZERO := TRUE;     // доп.код =0
                  xKDD       := Exch2494_KDD_Search(
                                              //aBudgetID: string;
                                              xKADM_CODE,
                                              xKD_CODE,
                                              xKESD_CODE,
                                              xKVFO,      // КИФ
                                              aBudgetID,         // бюджет
                                              aReceiverID,
                                              aPayID,
                                              aTerritoryID,
                                              aKDT_Code);       // доп.КД
                end;

      xCodes := Exch2389_GetPurposegrantID_D(
                          aReceiverID,
                          xKADM_CODE,
                          xKD_CODE,
                          xKESD_CODE,
                          xKVFO,      // КИФ
                          xKDD,       // доп.КД
                          aBudgetID,         // бюджет
                          aPayID,
                          aTerritoryID,
                          aKDT_Code,          // доп.КД
                          xFoundOneValue);

      // если доп.код =0 (но не пустой), и код цели не найден однозначно,
      // а так же, если код цели не найден совсем (но при этом значение
      // xFoundOneValue = TRUE):
      // тогда пробуем еще раз поискать код цели, но уже без доп.кода.

      if xKDDIsZERO then
        if ((NOT xFoundOneValue)
            OR (xFoundOneValue AND (LENGTH( xCodes) <= 0)))
          then xCodes := Exch2389_GetPurposegrantID_D(
                                              aReceiverID,
                                              xKADM_CODE,
                                              xKD_CODE,
                                              xKESD_CODE,
                                              xKVFO,      // КИФ
                                              '',       // доп.КД
                                              aBudgetID,         // бюджет
                                              aPayID,
                                              aTerritoryID,
                                              aKDT_Code,        // доп.КД
                                              xFoundOneValue);

      // если коды распознаны и если код только один - это и будет
      // искомый код цели
      Result := Exch2389_GetResult_PURPOSEFULGRANT_ID( xCodes, aBudgetID);

      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2389_GetPURPOSEFULGRANT_ID_D');
    end;

end;


{~Exch2389_Update_PURPOSEFULGRANT_ID
  обновление поля кода цели в доп.документе, порожденном от 24-го или 5-го
  классов.
  При стандартном алгоритме выполняется установка "Код цели" в зависимости
  от типа КБК:
    10 (расходы) - атрибут PURPOSEFULGRANT_ID
    20 (доходы) - атрибут PURPOSEFULGRANTD_ID
    31/32 (источники) - атрибут PURPOSEFULGRANTI_ID
  Если код цели в строке доп.ЭД небыл заполнен (пусто), то выполняется
  его поиск и подстановка в зависимости от типа КБК:
    10 -

  Параметры:
    aDopDoc - формируемый Доп.ЭД;
    aDopDocLine - строка доп.документ (строка нового дополнительного ЭД):
    aKBKType - тип КБК. Допускаются значения: 10,20,31(или 32).
    aBudgetID - бюджет. это нужно, поскольку непонятно из каких узлов доставать
                это значение из строки.
    aAttrName_PurposegrantID - непосредственное названия атрибута кода цели в
                строке доп.ЭД.
    aDopDocClass - класс доп.документа. в основном класс указывается в aDopDoc,
              но в скриптах BH, на момент вызова функции определения кода цели
              номер класса не указан, поэтому, если в этом параметре ничего
              не указано, номер класса берется из атрибуте aDopDoc.class,
              а если в параметре что либо указано - номер класса берется из
              этого параметра.
    aKDT_Code - тип дохода.
              добавлен для доходных ЭД по exchange #2494.
              проблема в том, что в некоторых ЭД это поле определено в шапке,
              а в некоторых - в строках.
              для расходов и источников этот параметр не учитывается и его
              можно заполнять пустой строкой.}
procedure Exch2389_Update_PURPOSEFULGRANT_ID( //var aDopDoc: Variant;
                                              var aDopDocLine: Variant;
                                              aKBKType: string;
                                              aBudgetID: string;
                                              aAttrName_PurposegrantID: string;
                                              aDopDocClass: string;
                                              aKDT_Code: string);
var
  xPURPOSEFULGRANT_ID: string;
//  xCodes: string;
  xPURPOSEFULGRANT_AttributeName: string;
  xDopDocClass: string;
begin

    try
      { определяем класс доп.ЭД}
      if (LENGTH( aDopDocClass) > 0)
        then xDopDocClass := aDopDocClass
        else xDopDocClass := aDopDocLine.ParentNode.ParentNode.GetAttribute( 'class');
        //aDopDoc.GetAttribute( 'class');


      { по типу КБК определяем алгоритм}
      case aKBKType of
        // расходная строка
        '10': begin
              // получаем код цели из строки ЭД. если он <>0 - ничего не будем
              // менять и выходим
              if (LENGTH( aAttrName_PurposegrantID) > 0)
                then xPURPOSEFULGRANT_AttributeName := aAttrName_PurposegrantID
                else xPURPOSEFULGRANT_AttributeName := 'PURPOSEFULGRANT_ID';
              if Exch2389_GetAttribute_PURPOSEFULGRANT_ID(
                                aDopDocLine,
                                xPURPOSEFULGRANT_AttributeName,
                                xPURPOSEFULGRANT_ID,
                                'расходная строка')
                then EXIT;
              // на всякий случай проверяем указание бланка расходов
              // здесь не просто проверяем наличие атрибута, но и значение
              // на пустую строку
              if (LENGTH( VarToStr( aDopDocLine.GetAttribute( 'ESTIMATE_ID'))) <= 0)
                then begin
                  WriteInfo( 'В строке ЭД код цели указывается нулевым - не определен бланк расходов.', FALSE);
                  // exchange #251
                  // при этом подставляется 0й код цели
                  aDopDocLine.SetAttribute(
                      xPURPOSEFULGRANT_AttributeName,
                      Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget( aBudgetID));
                  EXIT;
                end;
              aDopDocLine.SetAttribute(
                    xPURPOSEFULGRANT_AttributeName,
                    Exch2389_GetPURPOSEFULGRANT_ID_R(
                            aDopDocLine,
                            aBudgetID,
                            aDopDocLine.GetAttribute( 'ESTIMATE_ID'),
                            xDopDocClass));
            end;

        // доходы
        '20': begin
              // получаем код цели из строки ЭД. если он <>0 - ничего не будем
              // менять и выходим.

              if (LENGTH( aAttrName_PurposegrantID) > 0)
                then xPURPOSEFULGRANT_AttributeName := aAttrName_PurposegrantID
                else
              if XmlAttrExists( aDopDocLine, 'PURPOSEFULGRANTD_ID')
                then xPURPOSEFULGRANT_AttributeName := 'PURPOSEFULGRANTD_ID'
                else xPURPOSEFULGRANT_AttributeName := 'PURPOSEFULGRANT_ID';
              if Exch2389_GetAttribute_PURPOSEFULGRANT_ID(
                                            aDopDocLine,
                                            xPURPOSEFULGRANT_AttributeName,
                                            xPURPOSEFULGRANT_ID,
                                            'доходная строка')
                then EXIT;
              // на всякий случай проверяем указание получателя
              // здесь не просто проверяем наличие атрибута, но и значение
              // на пустую строку
              if (LENGTH( VarToStr( aDopDocLine.GetAttribute( 'RECEIVER_ID'))) <= 0)
                then begin
                  WriteInfo( 'В строке ЭД код цели указывается нулевым - не определен получатель.', FALSE);
                  // exchange #251
                  // при этом подставляется 0й код цели
                  aDopDocLine.SetAttribute(
                      xPURPOSEFULGRANT_AttributeName,
                      Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget( aBudgetID));
                  EXIT;
                end;
              aDopDocLine.SetAttribute(
                    xPURPOSEFULGRANT_AttributeName,
                    Exch2389_GetPURPOSEFULGRANT_ID_D(
                            aDopDocLine,
                            aBudgetID,
                            aDopDocLine.GetAttribute( 'RECEIVER_ID'),
                            xDopDocClass,
                            VarToStr( aDopDocLine.GetAttribute( 'PAYER_ID')),
                            VarToStr( aDopDocLine.GetAttribute( 'TERRITORY_ID')),
                            aKDT_Code));
            end;


        // для источников код цели устанавливается нулевым.
        '31','32': begin

              if (LENGTH( aAttrName_PurposegrantID) > 0)
                then xPURPOSEFULGRANT_AttributeName := aAttrName_PurposegrantID
                else
              if XmlAttrExists( aDopDocLine, 'PURPOSEFULGRANTI_ID')
                then xPURPOSEFULGRANT_AttributeName := 'PURPOSEFULGRANTI_ID'
                else xPURPOSEFULGRANT_AttributeName := 'PURPOSEFULGRANT_ID';
              if Exch2389_GetAttribute_PURPOSEFULGRANT_ID(
                                            aDopDocLine,
                                            xPURPOSEFULGRANT_AttributeName,
                                            xPURPOSEFULGRANT_ID,
                                            'строка источников')
                then EXIT;
              aDopDocLine.SetAttribute(
                        xPURPOSEFULGRANT_AttributeName,
                        Exch2389_GetResult_PURPOSEFULGRANT_ID( '', aBudgetID));
            end;


      end;//case//

      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2389_Update_PURPOSEFULGRANT_ID');
    end;

end;


{~Exch2389_Update_PURPOSEFULGRANT_ID_Ex
  заполняет поле "код цели" для строк 215-го класса из BH.
  поле заполняется как раньше, если оно указано во входном файле.
  если во входном файле поле не указано (пустая строка) или поле отсутствует,
  то заполнение "код цели" выполняется по новому алгоритму #2389.

    после определения и заполнения поля кода цели в aDopDocLine, лишние поля
  кода цели нужно очистить. например если заполняется код цели по доходам,
  то в поле в расходнах и источниках код цели необходимо очистить.
    т.е. если указано загружать в поле PURPOSEFULGRANTD_ID, то
  нужно почистить поля PURPOSEFULGRANT_ID и PURPOSEFULGRANTI_ID
  независимо от того, нужны они в документе или нет.
    это нужно делать, если в документе используются все 3 классификации,
  а заполняется только одно поле. при этом в процессе определения
  кода цели могут заполнится другие поля, что является ошибочным.

  проблема в том, что:
      не во всех документах есть поля кода цели для нескольких классификаций:
      доходы (PURPOSEFULGRANTD_ID)/источники(PURPOSEFULGRANTI_ID),
      а есть только одно поле PURPOSEFULGRAN_ID. в этом случае по типу КБК
      определяется поле PURPOSEFULGRANTD_ID/PURPOSEFULGRANTI_ID/PURPOSEFULGRANT_ID
      но в конечном итоге значение из этого поля копируется в указанное,
      в параметре aFieldName_PurposegrantID.

  параметры:
    aDopDoc - формируемый Доп.ЭД;
    aDopDocLine - строка формируемого доп.ЭД;
    aKBKType - тип КБК для формируемой строки (допускаются значения 10,20,31,32);
    aBudgetID - ид.бюджета формируемого ЭД;
    aFieldName_PurposegrantID - имя атрибута "код цели" в формируемой строке ЭД;
    aSource - строка ЭД исходного файла;
    aSource_ADD_KLASS - имя атрибута поля в строке исходного ЭД, откуда берется
                        код цели для сохранения в формируемом ЭД.
    aDopDocClass - класс доп.документа. в основном класс указывается в aDopDoc,
              но в скриптах BH, на момент вызова функции определения кода цели
              номер класса не указан, поэтому, если в этом параметре ничего
              не указано, номер класса берется из атрибуте aDopDoc.class,
              а если в параметре что либо указано - номер класса берется из
              этого параметра.
    aKDT_Code - тип дохода.
              добавлен для доходных ЭД по exchange #2494.
              проблема в том, что в некоторых ЭД это поле определено в шапке,
              а в некоторых - в строках.
              для расходов и источников этот параметр не учитывается и его
              можно заполнять пустой строкой.}
procedure Exch2389_Update_PURPOSEFULGRANT_ID_Ex(
    //                          var aDopDoc: Variant;
                              var aDopDocLine: Variant;
                              aKBKType: string;
                              aBudgetID: string;
                              aFieldName_PurposegrantID: string;
                              var aSource: Variant;
                              aSource_ADD_KLASS: string;
                              aDopDocClass: string;
                              aKDT_Code: string);

  {~DoClearField_
    очищает поле aFieldPurposegrantID в строке aDopDocLine,
    если aFieldAttribute <> aFieldPurposegrantID.
    это нужно  }
  procedure DoClearField_( aFieldAttribute, aFieldPurposegrantID: string);
  begin
    
        try
          if (aFieldPurposegrantID <> aFieldAttribute)
            then aDopDocLine.SetAttribute( aFieldPurposegrantID, '');

          except
            ExceptionLog( 'Сбой Un_Exch2389.Exch2389_Update_PURPOSEFULGRANT_ID_Ex.DoClearField_');
        end;
    
  end;

var
  xAttrValue: string;
  xFieldName_PurposegrantID: string;
begin

    try
      // exchange #2410 - название поля кода цели по типу КБК
      xFieldName_PurposegrantID :=
                  Exch2410_FieldName_PURPOSEFULGRANT_ID( aKBKType);
                                                  //aFieldName_PurposegrantID);
      // теперь переносим значение кода цели из атрибута PURPOSEFULGRANT_ID
      // в атрибут с новым названием
      Exch2410_ExchangeField_PURPOSEFULGRANT_ID(
                    aDopDocLine,
                    xFieldName_PurposegrantID,
                    Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget( aBudgetID));

      if NOT XmlAttrExists( aSource, aSource_ADD_KLASS)
        then Exch2389_Update_PURPOSEFULGRANT_ID(
//                  aDopDoc,
                  aDopDocLine,
                  aKBKType,
                  aBudgetID,
                  xFieldName_PurposegrantID,
                  aDopDocClass,
                  aKDT_Code)
        else begin
          xAttrValue := aSource.GetAttribute( aSource_ADD_KLASS);
          if ( LENGTH( xAttrValue) <= 0)
            then Exch2389_Update_PURPOSEFULGRANT_ID(
  //                        aDopDoc,
                          aDopDocLine,
                          aKBKType,
                          aBudgetID,
                          xFieldName_PurposegrantID,
                          aDopDocClass,
                          aKDT_Code)
            else begin
              aDopDocLine.SetAttribute(
                          xFieldName_PurposegrantID,
                          GetPurposeFulgrantId(
                                    aBudgetID,
                                    aSource.GetAttribute( aSource_ADD_KLASS)));

            end;
        end;

      // копируем значение из поля кода цели в котором получено значение
      // кода цели по КБК в указанное поле для строки документа.
      if (LENGTH( aFieldName_PurposegrantID) > 0) then begin
        if (aFieldName_PurposegrantID <> xFieldName_PurposegrantID)
          then aDopDocLine.SetAttribute(
                                aFieldName_PurposegrantID,
                                aDopDocLine.GetAttribute(
                                                  xFieldName_PurposegrantID));

        // подчищаем поля кода цели не являющиеся текущим полем.
        DoClearField_( aFieldName_PurposegrantID, 'PURPOSEFULGRANT_ID');
        DoClearField_( aFieldName_PurposegrantID, 'PURPOSEFULGRANTD_ID');
        DoClearField_( aFieldName_PurposegrantID, 'PURPOSEFULGRANTI_ID');
      end else begin
        // подчищаем поля кода цели не являющиеся заполняемым полем.
        DoClearField_( xFieldName_PurposegrantID, 'PURPOSEFULGRANT_ID');
        DoClearField_( xFieldName_PurposegrantID, 'PURPOSEFULGRANTD_ID');
        DoClearField_( xFieldName_PurposegrantID, 'PURPOSEFULGRANTI_ID');
      end;


      except
        ExceptionLog( 'Сбой Un_Exch2389.Exch2389_Update_PURPOSEFULGRANT_ID_Ex;');
    end;

end;


{~CheckPurposefullgrantCode
  поиск кода цели aCode в БД.
  возвращается пустая строка или id кода цели.}
function CheckPurposefullgrantCode( aBudgetID, aCode: string): string;
begin

    try
      Result := '';
      if ( LENGTH( aCode) <= 0) then EXIT;

      if (QueryField( ' Select count( * ) N From Purposefulgrant'
                     +' Where Budget_Id = ? and Code = ?',
                      [ aBudgetId,
                        aCode],
                      'N') <> '1')
        then EXIT;

      Result := QueryField( ' Select ID From Purposefulgrant'
                           +' Where Budget_Id = ? and Code = ?',
                            [ aBudgetId,
                              aCode],
                            'ID');
      except
        ExceptionProc( 'CheckPurposefullgrantCode');
    end;

end;


{~CheckPurposefullgrantCodeSet0
  поиск кода цели aCode в БД с заменой кода на 0, если aCode не указан
  или не найден в бд.

    возвращается id кода цели или пустая строка,
  если aChangeTo0=false, и КЦ не найден в БД.

  в aChange возвращается:
    =1 - код заменен на 0;
    =0 - код найден в бд.
    =2 - поле пустое - возвращается код 0}
function CheckPurposefullgrantCodeSet0( aBudgetID, aCode: string;
                                        var aChange: integer;
                                        aChangeTo0: boolean = TRUE): string;
begin

    try
      if ( LENGTH( aCode) <= 0) then begin
        aChange := 2;
        Result  := GetPurposeFulgrantId( aBudgetId, '0');
      end else begin
        Result := CheckPurposefullgrantCode( aBudgetID, aCode);
        if (LENGTH( Result) > 0)
          then aChange := 0
          else begin
            aChange := 1;
            if aChangeTo0 then begin
              WriteInfo( FORMAT( 'Код цели %s не найден в БД - заменен на 0.',
                                 [ aCode]));
              Result := GetPurposeFulgrantId( aBudgetId, '0');
            end else begin
              WriteInfo( FORMAT( 'Код цели %s не найден в БД.',
                                  [ aCode]));
              Result := '';
            end;
          end;
      end;
      except
        ExceptionProc( 'CheckPurposefullgrantCodeSet0');
    end;

end;


begin
end.
