function CheckPayOrder_General: Boolean;
var
  Unclassified: Boolean;
  MustBeGrantInvestCode: Boolean;
  xPi_Budget_Code: string;
  xKVR_Code, defKVR_Code, defKESR_Code, xKESR_Code, defKSDA_CODE, xKSDA_CODE: string;
  xKESR_TYPE, xKESR_DIRECTION, xDescription, xOrgID: string;
  kvrPos: Integer;
  KesrXml: Variant;
  xFillKSDA, xIsNeedFindKVR: boolean;
begin

    try
      Result := False;



    //  if AzkCompareVersion('2.26.42.11') < 0 then
      if NOT vIsAzkVersion2_26_42_11 then begin
        CDSListField_Message('Модуль проверки работает начиная с версии АЦК-Финансы 2.26.42.11');
        Exit;
      end;

      if NOT DoCheckRecAccountGeneral then EXIT;

      CDSListField_Enroll( NULL);

      //устанавливаем флаг неклассифицированных поступлений
      Unclassified := False;

      //определяет Id получателя из выписки
      if NOT DoCheckOrg( Unclassified) then EXIT;

      //Определяем список лицевых счетов открытых на б/с счете получателя 24 класса
      if (not Unclassified) then //begin
        if NOT DoSearchOrgAccount( {Xml, }1, 1, Unclassified)
          then EXIT;

      xFillKSDA := SysParamBudgetValue(cdsList.FieldByName('Budget_Id').AsString,
          'aubu.title','aubu.get_source_for_cr_anal_kind_direction') = '1';

      // exchange #2615
      // вариант поиска отраслевого кода
      if (vCfgModeSearchOtrKod = '#2615') then begin
        if (not Unclassified) then begin
          // новый вариант - только из назначения (комментария)
          //инициализирует значения отраслевого кода и косгу
          CDSListField_Industry_Code( '');
          CDSListField_Kesr_Code( '');
          //получает отраслевой код и косгу из назначения
          if not isParceDescription_General({cds, }'отр.код') then
          begin
            //Exchange #2281, если нет отр.кода, ищем один косгу,
            //без него классифицироать не будем
            if not xFillKSDA and not isParceDescription_General({cds, }'косгу') then begin
              CDSListField_Message('Не удалось получить КОСГУ из назначения платежа');
              Unclassified := True;
            end;
          end;
          if(xFillKSDA) then
          begin
            if not isParceDescription_General('ан. группа') and
                not isParceDescription_General('ан.группа') then
            begin
              CDSListField_Message('Не удалось получить Аналитическую группу из назначения платежа');
              Unclassified := True;
            end
            else
              CDSListField_Kesr_Code( '');
          end;
        end;
      end else begin
        // exchange #2758
        // если поле Идентификатора платежа (104 поле) не заполнено
        // или имеет значение «0», то косгу и отр.код берем из Назначения платежа.
        // поэтому, тут проверяем, если в идентификаторе платежа указана строка "0"
        // то это считаем за пустую строку.
        xPi_Budget_Code := CDSListField_Pi_Budget_Code_Get;
        if (xPi_Budget_Code = '0')
          then xPi_Budget_Code := '';


        // старый вариант
        if (not Unclassified) and (xPi_Budget_Code <> '') then begin
          //получает отраслевой код и косгу
          CDSListField_Industry_Code( Copy(xPi_Budget_Code, 1, 17));
          if (xFillKSDA) then
          begin
            xKSDA_CODE :=  Copy(xPi_Budget_Code, 18, 3);
            if(not KbkExists(cdsList.FieldByName('Budget_Id').AsString, xKSDA_CODE, 'KSDA'))
              or (Length(StringReplace(xKSDA_CODE, '0', '')) = 0) then
            begin
              isParceDescription_General('ан.группа');
            end;
            CDSListField_Ksda_Code(xKSDA_Code);
          end
          else
            CDSListField_Kesr_Code( Copy(xPi_Budget_Code, 18, 3));
        end else
          //104 поле не заполнено
          if (not Unclassified) and (xPi_Budget_Code = '') then begin
            //инициализирует значения отраслевого кода и косгу
            CDSListField_Industry_Code( '');
            CDSListField_Kesr_Code( '');
            //получает отраслевой код и косгу из назначения
            if not isParceDescription_General({cds, }'отр.код') then
            begin
              //Exchange #2281, если нет отр.кода, ищем один косгу,
              //без него классифицироать не будем
              if not xFillKSDA and not isParceDescription_General({cds, }'косгу') then begin
                CDSListField_Message('Не удалось получить КОСГУ из назначения платежа');
                Unclassified := True;
              end;
            end;
            if(xFillKSDA) then
            begin
              if not isParceDescription_General('ан. группа') and
                  not isParceDescription_General('ан.группа') then
              begin
                CDSListField_Message('Не удалось получить Аналитическую группу из назначения платежа');
                Unclassified := True;
              end
              else
                CDSListField_Kesr_Code( '');
            end;
          end else
            Unclassified := True;
        end;
      
      //проверка отраслевого кода на 17 нулей и замена на один системный ноль
      // exchange #2692
      // если отр.код нулевой, вместо нуля подставляем нулевой отраслевой код
      // по маске
      if not (Unclassified) then begin
        if (StringReplace(CDSListField_Industry_Code_Get, '0', '') = '')
          or (Trim(CDSListField_Industry_Code_Get) = '') then
        begin
          // здесь не получается найти код внутри edit...post - возникает сбой
          // поэтому реализовано через локальную переменную
          CDSListField_Industry_Code(
                  GetMaskIndustryCode( '0', CDSListField_Budget_Id_Get));
        end;
      end;


      // exchange #3053
      // Если КОСГУ=значению параметра «Значение КОСГУ для
      // зачисления на НВС»  – ставить ЭД на невыясненные.
      // здесь проверяем, если ЭД не переведен на "невыясненные",
      // т.е. косгу (поле 104) найден, проверяем его среди значений из конфига.
      // особенность в том, что проверяем код на вхождение в строку
      // перечислений кодов.
      // в конфиге указываются коды через запятую (например: 000,123,124,...).
      // при чтении настройки из конфига, строка обрамляется концевыми ",".
      // в результате строка в vCfg_KES_HBC будет вида: ,000,123,124,
      // ищем код КОСГУ 000 в строке, как подстроку ,000,

      if not Unclassified then
        if (LENGTH( CDSListField_Kesr_Code_Get) > 0) or (LENGTH( CDSListField_Ksda_Code_Get) > 0) then begin

         //инициализирует значениe кода субсидии
         DoResetGrantInvestment;

         //Exchange #2281, Exchange #2332
         //Код субсидии ищем только если второй разаряд лс отличен от 0 и 2
         MustBeGrantInvestCode :=
           not(Copy(CDSListField_LS_Number_Get, 2, 1) in ['0','2']);
    //    (Copy(cds.FieldByName('LS_Number').asString, 2, 1) <> '0') AND
    //    (Copy(cds.FieldByName('LS_Number').asString, 2, 1) <> '2');
        if (not Unclassified) then
          if (not isParceDescription_General({cds,} 'код субсидии')) then
            if MustBeGrantInvestCode then
            begin
              CDSListField_Message('Не удалось получить код субсидии из назначения платежа');
              Unclassified := True;
            end;
        //Код субсидии
        if (not Unclassified)
          then GrantInvestmentChooseGeneral( MustBeGrantInvestCode, Unclassified);

        //Отраслевой код
        // exchange #2692 - реализовано через общий механизм
        if (not Unclassified) then
          if NOT CheckIndustryCode//( cds)
            then Unclassified := True;

        //КВФО
        if not Unclassified then
          // Exchange #2362
          if DoSearchMatchLS_General
            then Unclassified := TRUE;
            
        if(not Unclassified) then
        begin
          //попробуем определить KVR_CODE по KESR_CODE
          defKVR_Code := FormatKbk( 'KVR', '0', cdsList.FieldByName('Budget_Id').AsString);
          defKESR_Code := FormatKbk( 'KES', '0', cdsList.FieldByName('Budget_Id').AsString);
          defKSDA_Code := FormatKbk('KSDA', '0', cdsList.FieldByName('Budget_Id').AsString);
          CDSListField_Kvr_Code(defKVR_Code);
          xKESR_Code := CDSListField_Kesr_Code_Get;
          xKSDA_Code := CDSListField_Ksda_Code_Get;
          if(LENGTH(xKESR_Code) = 0 ) then
            xKESR_Code := defKESR_Code;

          if (LENGTH( StringReplace( xKESR_Code, '0', '')) <= 0)
          then xKESR_TYPE := '2'    // расходы
          else begin
            KesrXML := QueryXml( ' select ANAL_KIND, DIRECTION from kes where code=?'
                                     +' and budget_id=?',
                                      [ xKESR_Code,
                                        cdsList.FieldByName('Budget_Id').AsString]);
            if(KesrXML.documentElement.ChildNodes.length > 0) then
            begin
              xKESR_TYPE := KesrXML.documentElement.ChildNodes[0].getAttribute('ANAL_KIND');
              xKESR_DIRECTION := KesrXML.documentElement.ChildNodes[0].getAttribute('DIRECTION');
            end
            else
            begin
              xKESR_TYPE := '';
              xKESR_DIRECTION := '';
            end;
            if (xKESR_TYPE = '0')
              then xKESR_TYPE := '1';  // доходы
          end;

          xIsNeedFindKVR := (xKESR_TYPE = '0') or (xKESR_TYPE = '2') or((xKESR_TYPE = '3') and (xKESR_DIRECTION = '2'));

          if ((Length(xKESR_Code) > 0) and (Length(StringReplace(xKSDA_Code, '0', '')) = 0)) then
          begin
            xKVR_CODE := defKVR_Code;
          end;

          if ((Length(xKESR_Code) > 0) and (Length(StringReplace(xKSDA_Code, '0', '')) = 0)) then
          begin
            if(vCfgModeSameMethodKesrAndKDMA) then
              xKSDA_CODE := xKESR_Code
            else
              xKSDA_CODE := defKESR_Code;
            if( not xIsNeedFindKVR) then
              xKVR_CODE := defKVR_Code;
          end;

          if ((Length(xKESR_Code) = 0) and (Length(StringReplace(xKSDA_Code, '0', '')) > 0)) then
          begin
            if(vCfgModeSameMethodKesrAndKDMA) then
              xKESR_CODE := xKSDA_Code
            else
              xKSDA_CODE := defKESR_Code;
          end;

          if( xIsNeedFindKVR) then
          begin
            xKVR_Code := defKVR_Code;
            xDescription := cdsList.FieldByName('Description').AsString;
            xDescription := UpperCase(StringReplace(XDescription, ' ', ''));
            kvrPos := pos('КВР', xDescription);
            if ((kvrPos > 0) and (Length(xDescription) >= Length('КВР') + Length(defKVR_Code))) then
            begin
              xKVR_Code := Copy(xDescription, kvrPos + Length('КВР'), Length(defKVR_Code));
              if(not KbkExists(cdsList.FieldByName('Budget_Id').AsString, xKVR_Code, 'KVR')) then
                xKVR_Code := defKVR_Code;
            end
            else
            begin
              KesrXML := QueryXml( ' select org_id from orgaccount where id=?',
                                        [ CDSListField_LS_ID_Get]);
              if(KesrXML.documentElement.ChildNodes.length > 0) then
              begin
                {WriteLog('BUDGET_ID = ' + CDSListField_Budget_Id_Get);
                WriteLog('Industrycode_Id = ' + CDSListField_Industrycode_Id_Get);
                WriteLog('Grantinvestment_Id = ' + CDSListField_Grantinvestment_Id_Get);
                WriteLog('Kesr_Code = ' + CDSListField_Kesr_Code_Get);
                WriteLog('FSR_ID = ' + CDSListField_FSR_ID_Get);
                WriteLog('defKESR_Code = ' + defKESR_Code);
                WriteLog('KESR_Code = ' + CDSListField_Kesr_Code_Get);}
                xOrgID := KesrXML.documentElement.ChildNodes[0].getAttribute('ORG_ID');
                xKVR_Code := SearchUnicalKVRCode(CDSListField_Budget_Id_Get,

                                           CDSListField_Industrycode_Id_Get,
                                           CDSListField_Grantinvestment_Id_Get,
                                           xKESR_Code,
                                           CDSListField_FSR_ID_Get,
                                           xOrgID,
                                           xKESR_Code <> defKESR_Code);
              end;
            end;
            CDSListField_Kvr_Code(xKVR_Code);
            if ((xKVR_Code = defKVR_Code) or (Length(xKVR_Code) = 0))
            then begin
              CDSListField_Message('Не удалось определить КВР');
              Unclassified := True;
            end;
          end
          else
          begin
            if (POS( ',' + CDSListField_Kesr_Code_Get + ',', vCfg_KES_HBC) > 0)
              then begin
                CDSListField_Message('Значение КОСГУ для зачисления на НВС');
                Unclassified := True;
            end;
         end;
         // exchange #2627 - расширенная проверка КОСГУ с учетом символьных КБК
         // если косгу не указан, он будет заменен на системный код 0.
         if not Unclassified then
           if NOT CheckKES//( cds)
             then Unclassified := TRUE
           else begin
              // exchange 2771
              // Если КОСГУ не определено (000) или не присутствует в справочнике,
              // устанавливать поступление на невыясненные.
              // для проверки удаляем все символы "0" в строке КОСГУ и проверяем
              // получившуюся строку - есть ли там что нибудь.
              // если ничего не осталось, значит КОСГУ содержит только символы "0",
              // тогда ставим поступление на невыясненные.
              if not xFillKSDA and (LENGTH( StringReplace( CDSListField_Kesr_Code_Get, '0', '')) <= 0)
                then begin
                  Unclassified := TRUE;
                  CDSListField_Message('Не удалось получить КОСГУ или КОСГУ равен 0');
                end;
           end;

        end;
      end;
      CDSListField_KSDA_Code(xKSDA_Code);
      CDSListField_KESR_Code(xKESR_Code);
      CDSListField_KVR_Code(xKVR_Code);

      if Unclassified then begin
        if NOT vIsAzkVersion2_26_46_1 then begin
          if NOT DoUnclassified_2_26_46_1_General( 1) then EXIT;
          Result := True;
        end else begin // версия системы выше чем 2.26.46.1
          if NOT DoUnclassified then EXIT;
          Result := True;
        end;
      end else begin
        DoCheckOk;
        Result := True;
      end;
      except
        ExceptionProc( 'CheckPayOrder_General');
    end;

end; {End CheckPayOrder}
{------------------------------------------------------------------------------}
{~isParceDescription_General
  разбирает основание платежа.
  параметры:
    keyword - ключевое слово
  если в основании платежа не встречаются ключевые слова или встречаются более
  одного раза то False.
  В зависимости от ключевого слова заполняются поля в наборе данных.

  exchange #2708
  доработка разбора параметра "код субсидии".}
function isParceDescription_General( aKeyword: String): Boolean;
begin

    try

      if (aKeyword = 'код субсидии')
        then Result := ParceDescriptionCDS(
                                '#2708',   // поиск по новому алгоритму
                                TRUE,
                                // получаем 0-й код выровненный по маске
                                // без разделителей (точек).
                                GetMaskGrantInvestmentCode(
                                          '0',
                                          CDSListField_Budget_Id_Get))
        else Result := ParceDescriptionCDS( aKeyword, TRUE);
      except
        ExceptionProc( 'isParceDescription_General');
    end;

end;
{------------------------------------------------------------------------------}
{~DoSearchMatchLS_General
  возвращает =true, если л/с не найден по маске.
  в этом случае ЭД нужно ставить на невыясненный.}
function DoSearchMatchLS_General: boolean;
  {----------------------------------------------------------------------------}
  function DoSearchMatchLS_( aSQLResult: string): boolean;
  var
    xXml: Variant;
    xRootXml: Variant;
    xNodeXml: Variant;
    i: integer;
    xFoundCount: integer;   // exchange #2578
    xLSNumber: string;      // exchange #2578
    xFoundCode: string;     // exchange #2578
    xIndustryCode: string;     // exchange #2578
  begin

      try
        Result := FALSE;
        xFoundCount := 0;    // exchange #2578 еще ничего не нашли
        xXml := vXML_Doc;// CreateOleObject('Microsoft.XmlDom');
        xXml.LoadXML( aSQLResult);
        xRootXml := xXml.documentElement;


        // exchange #2578 сохраняем строку проверяемого счета и отр.кода
        xLSNumber     := CDSListField_LS_Number_Get;
        xIndustryCode := CDSListField_Industry_Code_Get;

        // exchange #2578
        // для повышения скорости работы, выполняем проверку, если индустр.
        // код указан как системный (=0), то выполняем проверку только л/с.
        // это дает исключение операции на итерацию проверки л/с.
        // немного, но при большом справочнике масок это дает весомый результат.
        if (xIndustryCode = '0') then begin
          // вариант поиска только по счету
          for i := 0 to xRootXml.childNodes.Length - 1 do begin
            xNodeXml := xRootXml.childNodes.Item[i];
            if (MatchesMask( xLSNumber,
                              xNodeXml.getAttribute('ACCOUNTNUMBER_MASK'))) then begin
              // exchange #2578 подсчет найденных записей
              INC( xFoundCount);
              if not Result then begin
                Result := True;
                // exchange #2578 сохраняем промежуточный результат
                xFoundCode := xNodeXml.getAttribute('FSR_ID');
              end else begin
                Result := False;
                // exchange #2578
                // здесь не нужно пока формулировать сообщение, поскольку результат
                // еще неоднозначен.
                //CDSListField_Message( cds, 'Маска счета не совпадает с правилом соответствия классификаторов');
                Break;
              end;
            end;
          end;//for//
          // exchange #2578
          // если однозначно не нашли по маске л/с, но именно нашли неоднозначность,
          // то ищем полное совпащение маски со счетом. это дает то, что если
          // в справочнике указывают счет с маской и полный счет, то приоритетнее
          // именно полный счет, который полностью совпадает с искомым.
          if (xFoundCount > 1) then begin
            for i := 0 to xRootXml.childNodes.Length - 1 do begin
              xNodeXml := xRootXml.childNodes.Item[i];
              if (xLSNumber = xNodeXml.getAttribute('ACCOUNTNUMBER_MASK')) then
                if not Result then begin
                  Result := True;
                  xFoundCode := xNodeXml.getAttribute('FSR_ID');
                end else begin
                  Result := False;
                  Break;
                end;
            end;//for//
            // только в случае, если и теперь будет неоднозначен результат поиска,
            // можно четко об этом сообщить. это значит что указано несколько
            // одинаковых л/с в качестве масок.
            // так же если не будет найдено точного совпадения, это будет говорить
            // о том, что найдено несколько удовлетворяющих масок.
            if NOT Result
              then CDSListField_Message(
                      'Маска счета не совпадает с правилом соответствия классификаторов');
          end;
        end else begin
          // вариант поиска счет+отраслевой код
          for i := 0 to xRootXml.childNodes.Length - 1 do begin
            xNodeXml := xRootXml.childNodes.Item[i];
  
            if ( MatchesMask( xLSNumber,
                              xNodeXml.getAttribute('ACCOUNTNUMBER_MASK'))
                and
                 MatchesMask( xIndustryCode,
                              xNodeXml.getAttribute('INDUSTRYCODE_MASK'))) then begin
              // exchange #2578 подсчет найденных записей
              INC( xFoundCount);
              if not Result then begin
                Result := True;
                // exchange #2578 сохраняем промежуточный результат
                xFoundCode := xNodeXml.getAttribute('FSR_ID');
  
              end else begin
                Result := False;
                // exchange #2578
                // здесь не нужно пока формулировать сообщение, поскольку результат
                // еще неоднозначен.
                //CDSListField_Message( cds, 'Маски счета и отраслевого кода не совпадают с правилом' +
                //  ' соответствия классификаторов');
                Break;
              end;
            end;
          end;//for//
  
          // exchange #2578
          // если однозначно не нашли по маске л/с, но именно нашли неоднозначность,
          // то ищем полное совпащение маски со счетом. это дает то, что если
          // в справочнике указывают счет с маской и полный счет, то приоритетнее
          // именно полный счет, который полностью совпадает с искомым.
          if (xFoundCount > 1) then begin
            for i := 0 to xRootXml.childNodes.Length - 1 do begin
              xNodeXml := xRootXml.childNodes.Item[i];
  
              if ((xLSNumber = TRIM( xNodeXml.getAttribute('ACCOUNTNUMBER_MASK')))
                  and
                  MatchesMask( xIndustryCode,
                               xNodeXml.getAttribute('INDUSTRYCODE_MASK'))) then
                if not Result then begin
                  Result := True;
                  // exchange #2578 сохраняем промежуточный результат
                  xFoundCode := xNodeXml.getAttribute('FSR_ID');
  
                end else begin
                  Result := False;
                  Break;
                end;
            end;//for//
            // только в случае, если и теперь будет неоднозначен результат поиска,
            // можно четко об этом сообщить. это значит что указано несколько
            // одинаковых л/с в качестве масок.
            // так же если не будет найдено точного совпадения, это будет говорить
            // о том, что найдено несколько удовлетворяющих масок.
            if NOT Result
              then CDSListField_Message(
                      'Маски счета и отраслевого кода не совпадают с правилом'
                     +' соответствия классификаторов');
          end;
        end;
        // exchange #2578 заполняем найденный код
        if Result then begin
          CDSListField_Fsr_Id( xFoundCode);
  
        end;
        except
          ExceptionProc( 'DoSearchMatchLS_');
      end;

  end;
  {----------------------------------------------------------------------------}
var
  sql: string;
  xFlag: boolean;
  isOneMask: boolean;
begin

    try
      //проверяем найденные счет и отраслевой код подпадает под маски
      // exchange #2872 - добавлен бюджет в запрос
      sql := 'Select Accountnumber_Mask, Industrycode_Mask, Fsr_Id' +
        ' From AubuclassifiersRules where BUDGET_ID=? ';

    //  if AzkCompareVersion('2.26.46.1') < 0
      if NOT vIsAzkVersion2_26_46_1
        then sql := sql + ' and Unknown_Flag = 0 ';

      { Exchange #2364
        Если код субсидии для строки  заполняется  значением, отличным от
        системного 0 «Не указан» (Код «0», наименование «Не указан»,
        Учредитель «Не указан», тип «Не указан»)}
      if NOT DoGrantInvestmentCodeIsSystem{( cds)} then
      begin
        // алгоритм 1
        Query.AddStr( CDSListField_Budget_Id_Get);    // #2872
        Query.AddStr( CDSListField_Grantinvestment_Type_Get);
    
        isOneMask := DoSearchMatchLS_(
                          Query.Run(sql + ' and GrantinvestmentType = ?'));
      end
      else begin
        { алгоритм 2
        1)	Выбираются все правила, у которых  код субсидии = 0 «Не указан» и в поле «Тип субсидии»
        (GRANTINVESTMENTTYPE) указан код с типом FTYPE = 3 «Не указан»}
        
        Query.AddStr( CDSListField_Budget_Id_Get);    // #2872
        isOneMask := DoSearchMatchLS_(
                        Query.Run(sql + ' and GrantinvestmentType = 3'));
        { 4)	Если, в результате выполнения пунктов 1-3 Алгоритма2 не было
        найдено подходящее правило, то пункты 2 и 3 Алгоритма2 выполняются
        для правил, у которых в поле «Тип субсидии» (GRANTINVESTMENTTYPE)
        указаны коды с типами 0, 1, 2.}
        
        if NOT isOneMask then
        begin
          Query.AddStr( CDSListField_Budget_Id_Get);    // #2872
          isOneMask := DoSearchMatchLS_(
                        Query.Run(sql + ' and GrantinvestmentType in (0,1,2)'));
        end;
      end;

      // если подходящего л/с не нашли - ПП не линкуется
      if isOneMask
        then Result := FALSE
        else begin
          if NOT DoGrantInvestmentCodeIsSystem{( cds)} then
            CDSListField_Message( {cds, }
               'Не найдены правила соответствия классификаторов'
              +' по типу Кода субсидии или маска счета не совпадает с полученными'
              +' правилами')
          else
            CDSListField_Message( {cds, }
               'Не найдены правила соответствия классификаторов'
              +' или маска счета не совпадает с полученными правилами');
          Result := TRUE;
        end;
      except
        ExceptionProc( 'DoSearchMatchLS_General');
    end;

end;

begin
end.
