// Импортируемые классы документов
// Планирование (8, 9, 121, 122) - АКЦ (127, 133)
// Планирование (160, 163, 164) - АКЦ (128, 134)
// Планирование (161) - АКЦ (127)
// изменение - exchange #2390
// изменение - exchange #2395

uses
  '..\Tools\QueryKbkFormat',
  '..\Tools\Un_QueryEx',
  '..\Tools\Un_Default',
  '..\Tools\Un_ToolsXml',
  '..\Tools\Un_Tools',
  '..\Tools\Un_Progress',
  '..\Tools\Un_System',
  '..\Tools\Un_LogEx',
  '..\Tools\Un_Debug',
  '..\Version\CheckVersion',
  '..\Tools\Un_Direction';

const
  FileXmlNameOnly = 'PlanAssDocImport.xml';
  FileXmlName = 'C:\Temp\' + FileXmlNameOnly;
  FileVer = '2.0';
  FileGuid = '{B576A4A6-F1B5-44E7-9425-0066A1D10CC8}';

  // exchange #2859
  // спец. поле для замены foregn_key в сводных убн, которые создаются по
  // ращепленным убн (подпрограммой SplitDoc).
  cnFieldForegnkeyInt = '_key_';

var
  Xml: Variant;
  AzkLogined, Debuged: Boolean;
  Period: Integer;
  FileLog, FileInfo, FileError, Temp_Id: String;
  Form: TFormCustom;
  Timer: TTimer;
  cmbClass, cmbOper, cmbOperChange: TComboBox;
  btnExecute: TButton;
  ProgressBar: TProgressBar;
  edtDate: TDateTimeEh;
  edtFile: TEdit;
  chkGroup, chkGroupRec, chkQuart, chkNumber, chkToStatus10,
  chkCombined, chkDescription, chkDeleteEmpty, chkAssingOnly, chkDocDate: TCheckBox;
  edtQuart1, edtQuart2, edtQuart3, edtQuart4: TDBNumberEditEh;
  edtDescription: TMemo;
  rgOper, rgNumber: TRadioGroup;
  DocumentClass_Id: Integer;
  ListInfoError: TStrings;
  openDialog: TOpenDialog;
  XmlOper, XmlOperChange: Variant;
  // exchange #2555 - дата утверждения
  dtDateConfirmation: TDateTimeEh;
  // exchange #2777 - Принимать сумму ЛБО только текущего года
  cbSummaLBOcurrYear: TCheckBox;
  // exchange #2859
  // список ассоциаций key=id для новых foregnkey определяемых
  // в подпрограмме SplitDoc
//  vListNewKey: TStrings;
//  vListNewKeyID: TStrings;
{-----------------------------------------------------------------------------}
procedure Main(Xml: Variant);
begin
  // exchange #2390 добавлено для возможности отладки
  InitializeMain( Xml, FileXmlNameOnly, '');
  try



    CheckWorkAppVersion;
    with Xml.documentElement do
    if getAttribute('TESTED') = 'True' then
    begin
      setAttribute('TESTED', 'False');
      Xml.save(FileXmlName);
      ShowMessage('Тестовый пример приготовлен: ' + FileXmlName);
      Exit;
    end;
    AzkLogined := AzkIsLogin;
    if not AzkLogined then
      if not AzkLogin then
        Exit;
    try
      try
        initialization(Xml);
        Timer.Enabled := True;
        if Debuged then
        begin
          Form.Show;
          repeat
            Application.HandleMessage;
          until(not Form.Visible);
        end
        else
          Form.ShowModal;
      except
        WriteLog(LastException.Message);
        raise(LastException.Message);
      end;
    finally
      finalization;
      if not AzkLogined then
        AzkLogout;
    end;
    finally
      // exchange #2390 добавлено для возможности отладки
      FinalizeMain;
  end;
end; {End Main}
{-----------------------------------------------------------------------------}
procedure WriteLog(Message: String; simpledText: Boolean = False);
begin

  if simpledText then
    Form.SimpleText := Message;
  WriteLogFile(FileLog, Message);
end; {End WriteLog}
{-----------------------------------------------------------------------------}
procedure WriteInfo(Message: String; simpledText: Boolean = False);
begin

  if ListInfoError.IndexOf(MD5String(Message)) < 0 then
  begin
    ListInfoError.Add(MD5String(Message));
    WriteLogFile(FileInfo, Message);
    WriteLog(Message, simpledText);
  end;
end; {End WriteInfo}
{-----------------------------------------------------------------------------}
procedure WriteError(Message: String; simpledText: Boolean = False);
begin

  if ListInfoError.IndexOf(MD5String(Message)) < 0 then
  begin
    ListInfoError.Add(MD5String(Message));
    WriteLogFile(FileError, Message);
    WriteLog(Message, simpledText);
  end;
end; {End WriteError}
{-----------------------------------------------------------------------------}
procedure LoadOperType(Sender: TObject);
  function FillOper(ComboBox: TComboBox; ClassArray: Variant = Null): Variant;
  var
    i, ItemIndex: Integer;
  begin

      try
        Result := QueryXml(Sql, ClassArray);
        Result := Result.documentElement.childNodes;
        if Sender = Timer then
          TPropStorageEh(Form.FindComponent('propStorage')).LoadProperties;
        ItemIndex := ComboBox.ItemIndex;
        if ComboBox.Items.count > 0 then
          ComboBox.Items.Clear;
        for i := 0 to Result.length - 1 do
          ComboBox.Items.Add(Result.items[i].getAttribute('CAPTION'));
        ComboBox.ItemIndex := ItemIndex;
        if ComboBox.ItemIndex < 0 then
          ComboBox.ItemIndex := 0;
        except
          ExceptionProc( 'FillOper');
      end;

  end;
begin

    try
      Timer.Enabled := False;
      Sql := 'Select O.Id, O.Caption From OperType O' +
        ' Left Join ClassOper C On (C.OperType_Id = O.Id)' +
        ' Where C.DocumentClass_Id = ? Or O.Id = 0';
      QueryInitialization;
      try
        if cmbClass.ItemIndex = 0 then
        begin
          XmlOper := FillOper(cmbOper, [127]);
          XmlOperChange := FillOper(cmbOperChange, [128]);
        end
        else
        begin
          XmlOper := FillOper(cmbOper, [133]);
          XmlOperChange := FillOper(cmbOperChange, [134]);
        end;
      finally
        QueryFinalization;
      end;
      except
        ExceptionProc( 'LoadOperType');
    end;

end; {End LoadOperType}
{-----------------------------------------------------------------------------}
function CashPlanExpensePeriod: String;
var
  i: integer;
  s: string;
  Xml: Variant;
  xSysParamData: string;
  xXmlParamValues: Variant;
  xXmlParamValue: Variant;
begin

    try
      Xml := CreateOleObject('Microsoft.XmlDom');
      Xml.appendChild(Xml.createElement('EXCH_UTILS_PROCESSOR'));
      Xml.documentElement.setAttribute('action', 'get_sys_param_value');
      Xml.documentElement.appendChild(Xml.createElement('SYSPARAM')).
        setAttribute('NAME', 'cashplan.expense.period');
      Xml.loadXml(ExecXml(Xml.xml));

      if Xml.documentElement.childNodes.length = 0
        then Result := '1'
        else Result := Xml.documentElement.firstChild.text;

      // exchange #2799
      // начиная с версии АЦК-Ф 2.35 добавлен системный параметр, где указывается
      // для каждого бюджета определение типа периода. если для этого бюджета
      // период не указан, будет взято значение по умолчанию.
      // бюджет берем прилогиненный, поскольку при создании ЭД из файла выполняются
      // проверки привязки бланков расходов к текущему бюджету.
      Xml.loadXml( '');
      Xml.appendChild(Xml.createElement('EXCH_UTILS_PROCESSOR'));
      Xml.documentElement.setAttribute('action', 'get_sys_param_value');
      Xml.documentElement.appendChild(Xml.createElement('SYSPARAM')).
        setAttribute('NAME', 'cashplan.expense.period.budget');
      Xml.loadXml(ExecXml(Xml.xml));

      xXmlParamValues := Xml.getElementsByTagName('PARAM_VALUE');
      if (xXmlParamValues.length = 1) then begin
        xSysParamData := xXmlParamValues.items[0].firstChild.Data;
        Xml.loadXml( xSysParamData);

        // разбираем xml вида - нужен перебор узлов LINE:
        // <cashplan.expense.period.budget>
        //   <LINE BUDGET_ID="1000000150" PERIOD="1"/>
        // </cashplan.expense.period.budget>
        if (Xml.documentElement.childNodes.length > 0) then
          for i := 0 to Xml.documentElement.childNodes.length - 1 do begin
            xXmlParamValue := Xml.documentElement.childNodes.items[ i];
            if (xXmlParamValue.NodeName = 'LINE') then begin
              if ( VarToStr( xXmlParamValue.GetAttribute( 'BUDGET_ID')) = Budget_Id)
                then begin
                  // получаем значение периода, и если значение указано (не пустое)
                  // возвращаем его в качестве результата.
                  // т.е. допускается вариант, когда будет указано несколько
                  // значений (одно пустое, а другое не пустое), для одного бюджета.
                  s := TRIM( VarToStr( xXmlParamValue.GetAttribute( 'PERIOD')));
    
                  if (LENGTH( s) > 0) then begin
                    Result := s;
                    BREAK;
                  end;
                end;
            end;
          end;//for//
      end;
      except
        ExceptionProc( 'CashPlanExpensePeriod');
    end;

end; {End CashPlanExpensePeriod}
{-----------------------------------------------------------------------------}
procedure checkKBK(Xml: Variant; CodeName: String);
begin

    try
      sql := 'Select Code From ';
      case CodeName of
        'KADMR': sql := sql + 'Kadm';
        'KCSR': sql := sql + 'Kcsr';
        'KDE': sql := sql + 'Kde';
        'KDF': sql := sql + 'Kdf';
        'KDR': sql := sql + 'Kdr';
        'KESR': sql := sql + 'Kes';
        'KFSR': sql := sql + 'Kfsr';
        'KVR': sql := sql + 'Kvr';
      end;
      sql := sql + ' Where Budget_Id = ? And Code = ?';
      if QueryField(sql, [Budget_Id, Xml.getAttribute(CodeName + '_CODE')], 'CODE') = Null then
      begin
        writeError('В АЦК не найден ' + captionKbkByCodeName(CodeName) + ': ' +
          Xml.getAttribute(CodeName + '_CODE'));
      end;
      except
        ExceptionProc( 'checkKBK');
    end;

end; {End checkKBK}
{-----------------------------------------------------------------------------}
function captionKbkByCodeName(CodeName: String): String;
begin

    try
      case CodeName of
        'KADMR': Result := 'ведомственный классификатор расходов';
        'KCSR':  Result := 'классификатор целевой статью';
        'KDE':   Result := 'дополнительный экономический код';
        'KDF':   Result := 'дополнительный функциональный код';
        'KDR':   Result := 'дополнительный код расходов';
        'KESR':  Result := 'экономический классификатор расходов';
        'KFSR':  Result := 'Функциональный классификатор расходов';
        'KVR':   Result := 'классификатор видов расходов';
      end;
      except
        ExceptionProc( 'captionKbkByCodeName');
    end;

end; {End captionKbkByCodeName}
{-----------------------------------------------------------------------------}
function FormatKbk_(CodeName, Code: String; const formatType: Integer = 0): String;
{var
  i, n, count: Integer;
  mask, sql: String;}
begin

    try
      Result := FormatKbk( CodeName, Code, Budget_Id, formatType);
      {
      sql := 'Select Tree_Mask From CodeType Where Name = ? and Budget_Id = ?';
      mask := QueryField(sql, [CodeName, Budget_Id], 'TREE_MASK');
      n := Length(mask);
      count := 0;
      Result := '';
      for i := 1 to n do
      begin
        if (formatType = 0) and (Result <> '') then
          Result := Result + '.';
        Result := Result + Format('%0.*d', [StrToInt(mask[i]), 0]);
        count := count + StrToInt(mask[i]);
      end;
      if n > 1 then
        Result := Result + ';0';
      Result := FormatMaskText(Result, StringOfChar('0', count - Length(Code)) + Code);
      }
      except
        ExceptionProc( 'FormatKbk_');
    end;

end; {End FormatKbk_}
{-----------------------------------------------------------------------------}
function RowInfo(Row: Variant): String;
begin

    try
      Result :=
        'Бланк расходов: ' + Row.getAttribute('EST_CAPTION') + #13 + #10 +
        'Бюджетополучатель: ' + Row.getAttribute('RECIPIENT_CAPTION') + #13 + #10 +
        '[КФСР=' + FormatKbk_('KFSR', Row.getAttribute('KFSR_CODE')) +
        ',КЦСР=' + FormatKbk_('KCSR', Row.getAttribute('KCSR_CODE')) +
        ',КВР=' + FormatKbk_('KVR', Row.getAttribute('KVR_CODE')) +
        ',КОСГУ=' + FormatKbk_('KES', Row.getAttribute('KESR_CODE')) +
        ',КВСР=' + FormatKbk_('KADM', Row.getAttribute('KADMR_CODE')) +
        ',Доп. ФК=' + FormatKbk_('KDF', Row.getAttribute('KDF_CODE')) +
        ',Доп. ЭК=' + FormatKbk_('KDE', Row.getAttribute('KDE_CODE')) +
        ',Доп. КР=' + FormatKbk_('KDR', Row.getAttribute('KDR_CODE')) + ']';
      except
        ExceptionProc( 'RowInfo');
    end;

end; {End RowInfo}
{-----------------------------------------------------------------------------}
{~GroupDoc
  выполняется объединение эд в списке эд aXmlDocs.
  эд, у которых одинаковый класс эд, бланк расходов и Дата утверждения закона
  объединяются - объединяются строки таких эд в один эд.}
procedure GroupDoc(Xml: Variant);
var
  i, j, idx: Integer;
  Md5Str: String;
  List: TStrings;
  Row: Variant;
begin
{ объединение эд по полям в шапке.
  при объединении строки из одного эд переносятся в другой.}

    try
      i := 0;
      List := TStringList.Create;
      try
        ProgressBar.Max := Xml.childNodes.length;
        while i <> Xml.childNodes.length do
        begin
          Row := Xml.childNodes.items[i];

          Md5Str := 'DOCUMENTCLASS_ID = ' + Row.getAttribute('DOCUMENTCLASS_ID')
                   +'ESTIMATE_ID = ' + Row.getAttribute('EST_ID')
                   { exchange #2390 - ФТ.3 пп.1
                     объединение ЭД с учетом поля «Дата утверждения закона»}
                   + 'APPROVE_BUDGET_DATE = '
                          + VarToStr( Row.GetAttribute( 'APPROVE_BUDGET_DATE'));
          DetectDocumentClass(Row);
          if chkCombined.Checked and (DocumentClass_Id in [127, 128]) and
            (VarToStr(Row.getAttribute('PLANASSDOCSUM_ID')) <> '')then
            Md5Str := Md5Str + 'ID = ' + VarToStr(Row.getAttribute('ID'));
//

          Md5Str := MD5String(Md5Str);
          idx := List.IndexOf(Md5Str);
          if idx = -1 then begin
            List.Add(Md5Str);
            Inc(i);

          end else begin
            for j := 0 to Row.childNodes.length - 1 do
              Xml.childNodes.items[idx].appendChild(Row.childNodes.items[j].cloneNode(true));
            Xml.removeChild(Row);

          end;
          ProgressBar.Position := ProgressBar.Position + 1;
          Form.SimpleText := Format('Группировка документов %d из %d', [ProgressBar.Position, ProgressBar.Max]);
          Application.ProcessMessages;
        end;
      finally
        ProgressBar.Position := 0;
        Form.SimpleText := '';
        List.Free;
      end;
      except
        ExceptionProc( 'GroupDoc');
    end;

end; {End GroupDoc}
{-----------------------------------------------------------------------------}
{~GroupLine
  объединение строк в эд Xml.
  при объединении строк, суммы строк суммируются.}
procedure GroupLine(Xml: Variant);
var
  i, j, idx: Integer;
  Md5Str: String;
  List: TStrings;
  Row: Variant;

  procedure AddAmounts(AttributeName: String);
  begin

      try
        // exchange 2395
        // проблема в том, что для функции StrToCurrDef требуется указания строки
        // путь даже и пустой, а тут будет попытка объединить строки с
        // возможно отсутствующими полями месячных сумм (тем более здесь нет проверки
        // на класс и операцию формирования ЭД). поэтому, если не ввести операторы
        // преобразования значений NULL в пустую строку, то будет сбой при
        // преобразовании значения NULL в число.
        Xml.childNodes.items[idx].setAttribute(AttributeName,
          StrToCurrDef(
              VarToStr( Xml.childNodes.items[idx].getAttribute(AttributeName)), 0.00) +
          StrToCurrDef(
              VarToStr( Row.getAttribute(AttributeName)), 0.00));
        except
          ExceptionProc( 'AddAmounts');
      end;

  end;
begin
  { тут выполняется группировка строк.
    для одинаковых строк выполняется суммирование полей сумм строк, и лишняя
    строка удаляется.}

    try
      i := 0;
      List := TStringList.Create;
      try
        while i < Xml.childNodes.length do begin
          Row := Xml.childNodes.items[i];
    
          Md5Str :=
              MD5String(
                'RECIPIENT_ID = ' + Row.getAttribute('RECIPIENT_ID') +
                'FSR_ID = ' + Row.getAttribute('FSR_ID') +
                'KADMR_CODE = ' + Row.getAttribute('KADMR_CODE') +
                'KCSR_CODE = ' + Row.getAttribute('KCSR_CODE') +
                'KDE_CODE = ' + Row.getAttribute('KDE_CODE') +
                'KDF_CODE = ' + Row.getAttribute('KDF_CODE') +
                'KDR_CODE = ' + Row.getAttribute('KDR_CODE') +
                'KESR_CODE = ' + Row.getAttribute('KESR_CODE') +
                'KFSR_CODE = ' + Row.getAttribute('KFSR_CODE') +
                'KVR_CODE = ' + Row.getAttribute('KVR_CODE') +
                'DIRECTION_ID = ' + Row.getAttribute('PL_DIRECTION_ID') +
                'PURPOSEFULGRANT_ID = ' + VarToStr(Row.getAttribute('PURPOSEFULGRANT_ID')));
          idx := List.IndexOf(Md5Str);
//    
          if idx = -1 then begin
            List.Add(Md5Str);
            Inc(i);

          end else begin

            AddAmounts('YEAR_AMT');
            AddAmounts('AMOUNT2YEAR');
            AddAmounts('AMOUNT3YEAR');
            AddAmounts('Q1_AMT');
            AddAmounts('Q2_AMT');
            AddAmounts('Q3_AMT');
            AddAmounts('Q4_AMT');
            // exchange #2395
            // учитываем помесячную разбивку сумм
            try
              AddAmounts('QM1_AMT');
              AddAmounts('QM2_AMT');
              AddAmounts('QM3_AMT');
              AddAmounts('QM4_AMT');
              AddAmounts('QM5_AMT');
              AddAmounts('QM6_AMT');
              AddAmounts('QM7_AMT');
              AddAmounts('QM8_AMT');
              AddAmounts('QM9_AMT');
              AddAmounts('QM10_AMT');
              AddAmounts('QM11_AMT');
              AddAmounts('QM12_AMT');
              except
                ExceptionLog( 'Сбой при суммировании месячных сумм.');
            end;
            Xml.removeChild(Row);
          end;
        end;//while//
      finally
        List.Free;
      end;
      except
        ExceptionProc( 'GroupLine');
    end;

end; {End GroupLine}
{-----------------------------------------------------------------------------}
{~CopyHeadDoc
  копирование шапки эд Source в Dest.
  здесь не копируются строки или вложенные узлы.}
procedure CopyHeadDoc(Source, Dest: Variant);
begin

    try
      Dest.setAttribute('action', 'insert');
      Dest.setAttribute('class', DocumentClass_Id);

      {
      // exchange #2859
      // копируем промежуточные атрибуты.
      // они понадобятся позже в SplitDoc.
      Dest.setAttribute( '_ID_', Source.getAttribute('ID'));
      Dest.setAttribute( '_DOCUMENTCLASS_ID_', Source.getAttribute('DOCUMENTCLASS_ID'));
      }

      if chkDocDate.Checked and not chkGroup.Checked
        then Dest.setAttribute('DOC_DATE', Source.getAttribute('DOC_DATE'))
        else Dest.setAttribute('DOC_DATE', FormatDateTime('yyyy-mm-dd', edtDate.Date));

      Dest.setAttribute('ASSIGN_SOURCE_ID', VarToStr(Source.getAttribute('ASSIGN_SOURCE_ID')));

      if chkGroup.Checked or (rgNumber.ItemIndex = 0)
        then Dest.setAttribute('num_gen_type', 'generate')
        else Dest.setAttribute('DOC_NUMBER', Source.getAttribute('DOC_NUMBER'));

      if rgOper.ItemIndex = 1
        then Dest.setAttribute('OPERTYPE_ID', Source.getAttribute('OPERTYPE_ID'))
        else
          case DocumentClass_Id of
            127, 133: Dest.setAttribute('OPERTYPE_ID', XmlOper.items[cmbOper.ItemIndex].getAttribute('ID'));
            128, 134: Dest.setAttribute('OPERTYPE_ID', XmlOperChange.items[cmbOperChange.ItemIndex].getAttribute('ID'));
          end;//case//

      if DocumentClass_Id in [133, 134]
        then Dest.setAttribute('PERIOD', CashPlanExpensePeriod);

      if chkDescription.checked
        then Dest.setAttribute('DESCRIPTION', edtDescription.Lines.Text)
        else Dest.setAttribute('DESCRIPTION', Source.getAttribute('DESCRIPTION'));

      if not chkGroup.checked and chkNumber.checked
        then Dest.setAttribute(
                  'REMARK',
                  'Документ автоматически сформирован из системы '
                 +'"АЦК-Планирование" N ' + Source.getAttribute('DOC_NUMBER')
                 +' от ' + FormatDateTime('dd.mm.yyyy', XMLToDateTime(Source.getAttribute('DOC_DATE'))))
        else Dest.setAttribute(
                  'REMARK',
                  VarToStr(Source.getAttribute('REMARK')));

      if not chkGroup.Checked
        then Dest.setAttribute('FOREIGN_KEY', Source.getAttribute('ID') + Source.getAttribute('DOCUMENTCLASS_ID'));

      if chkCombined.Checked and (DocumentClass_Id in [127, 128])
         and (VarToStr(Source.getAttribute('PLANASSDOCSUM_ID')) <> '')
        then begin
          Dest.setAttribute('FOREIGN_KEY', Source.getAttribute('ID') + Source.getAttribute('DOCUMENTCLASS_ID'));
          Dest.setAttribute('PLANASSDOCSUM_ID', Source.getAttribute('ID'));
          if rgNumber.ItemIndex = 1 then begin
            Dest.setAttribute('DOC_NUMBER', Source.getAttribute('DOC_NUMBER'));
            Dest.removeAttribute('num_gen_type');
          end;
        end;

      { exchange #2390 ФТ.1
          При создании ЭД УБН, УиБН  по расходам, связанных со сводными ЭД
        (тег PLANASSDOCSUM_ID заполнен), и не связанных со сводными ЭД, импортом с
        помощью АЦК-Exchange из Планирования поле «Дата утверждения закона» в
        создаваемом в АЦК-Финансы документе должно заполняться значением тега
        «APPROVE_BUDGET_DATE».
          Копирование ЭД - на всякий случай проверяем, если значение даты в теге
        указано - копируем тег, если нет - тег не копируем.
        это аналогия проверки указания тега
          По сути тут просто копируется тег (если он есть) из исходного XML в
        результат.}

      if XmlAttrExists( Source, 'APPROVE_BUDGET_DATE')
        then Dest.SetAttribute( 'APPROVE_BUDGET_DATE',
                                VarToStr( Source.GetAttribute(
                                                          'APPROVE_BUDGET_DATE')));


      {exchange #2390 ФТ.10
       При импорте ЭД УБН, УиБН по расходам из АЦК-Планирование через АЦК-Exchange
       поле «Дата утверждения» заполнять значением по умолчанию (пустое значение).}
      // Dest.setAttribute( 'CARRY_DATE', '');
      {exchange #2555
       пп.1.2) заполнять поле «Дата утверждения»-заполнять значением
       из поля «Дата утверждения» окна-импорта всегда для УБН\УиБН.}
       if DocumentClass_Id in [127, 128]
         then Dest.setAttribute( 'CARRY_DATE',
                                  FormatDateTime( 'yyyy-mm-dd',
                                                  dtDateConfirmation.Date));
      except
        ExceptionProc( 'CopyHeadDoc');
    end;

end; {End CopyHeadDoc}
{-----------------------------------------------------------------------------}
{~GetPurposeFulgrantId
  Возвращает ID субсидии по коду. Если код отсутствует, тогда пусто.}
function GetPurposeFulgrantId(aBudgetId, Code: String): String;
var
  Xml: Variant;
begin

    try
      Result := '';
      if Length(Code) > 20 then
        Raise(Format('%s неверное значение кода субсидии. Код субсидии не может' +
            ' быть больше 20 знаков.', [Code]));
      Xml := QueryXml('Select * From PurposeFulgrant Where Budget_Id = ?' +
        ' and code = ? and Block_Flag = 0', [aBudgetId, Code]);
      if Xml.documentElement.ChildNodes.Length = 1 then
        Result := Xml.documentElement.ChildNodes.item[0].getAttribute('ID');
      except
        ExceptionProc( 'GetPurposeFulgrantId');
    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
        ExceptionProc( 'Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget');
    end;

end;
{-----------------------------------------------------------------------------}
{~IsImport_ExpKassPlan_MonthDiv
  exchange #2395
  возвращает =true, если указанный документ является классом 160?, 163, 164,
  в режиме импорта с учетом помесячного планирования и загружаемые в
  «АЦК-Финансы» как документ ИКП (класс 134).
  только для случая, когда настройка «Сервис – Системные параметры –
  Кассовый план – Разбивка кассового плана по расходам» равна «Помесячно».
  exchange #2799
   теперь разрешается загружать ЭД КП (133й класс) с месячной разбивкой.
   поэтому проверяем, если загружается из планирования ЭД классы, которые
   создают 133й класс в АЦК-Ф, то проверяем, указана ли месячная разбивка
   из сист.настроек для текущего бюджета.}
function IsImport_ExpKassPlan_MonthDiv( aDoc: Variant): boolean;
begin

    try
      Result := FALSE;
      if (CashPlanExpensePeriod <> '2') then EXIT;
      if NOT (VarToStr( aDoc.getAttribute('DOCUMENTCLASS_ID'))
              in [ {'160',} '163', '164',
                    // exchange #2799
                    // теперь разрешается загружать ЭД КП (133й класс)
                    // с месячной разбивкой
                    '8', '9', '121', '122'])
        then EXIT;
      Result := (DetectDocumentClassExRow( aDoc) in [134,133]);
      except
        ExceptionProc( 'IsImport_ExpKassPlan_MonthDiv');
    end;

end;
{-----------------------------------------------------------------------------}
{~CopyLineDoc
  копирование строк эд из Source в Dest}
procedure CopyLineDoc(Source, Dest: Variant);
var
  FlagLim, FlagAss: Boolean;
  dclass: integer;
  xGroupByBudg: string;
  xBuhByBudg: string;
  xAMOUNT2YEAR: string;
  xAMOUNT3YEAR: string;
begin

    try
      dclass :=
        StrToIntDef( VarToStr( Source.parentNode.getAttribute('DOCUMENTCLASS_ID')) , 0 );

      if (161 = dclass) then begin
        Dest.setAttribute('ESTIMATE_ID', Source.getAttribute('EST_ID'));
        Dest.setAttribute('ESTIMATE_CAPTION', Source.getAttribute('EST_CAPTION'));
      end else begin
        Dest.setAttribute('ESTIMATE_ID', Source.parentNode.getAttribute('EST_ID'));
        Dest.setAttribute('ESTIMATE_CAPTION', Source.parentNode.getAttribute('EST_CAPTION'));
      end;

      Dest.setAttribute('RECIPIENT_ID', Source.getAttribute('RECIPIENT_ID'));
      Dest.setAttribute('FSR_ID', Source.getAttribute('FSR_ID'));
      Dest.setAttribute('KADMR_CODE', Source.getAttribute('KADMR_CODE'));
      Dest.setAttribute('KCSR_CODE', Source.getAttribute('KCSR_CODE'));
      Dest.setAttribute('KDE_CODE', Source.getAttribute('KDE_CODE'));
      Dest.setAttribute('KDF_CODE', Source.getAttribute('KDF_CODE'));
      Dest.setAttribute('KDR_CODE', Source.getAttribute('KDR_CODE'));
      Dest.setAttribute('KESR_CODE', Source.getAttribute('KESR_CODE'));
      Dest.setAttribute('KFSR_CODE', Source.getAttribute('KFSR_CODE'));
      Dest.setAttribute('KVR_CODE', Source.getAttribute('KVR_CODE'));
      Dest.setAttribute('DIRECTION_ID', Source.getAttribute('PL_DIRECTION_ID'));
      Dest.setAttribute('PURPOSEFULGRANT_ID', VarToStr(Source.getAttribute('PURPOSEFULGRANT_ID')));

      if CompareVersion(AzkVersion, '2.17.17.1') > 0 then begin
        //with CreateOleObject('Microsoft.XmlDom') do begin
        Xml := CreateOleObject('Microsoft.XmlDom');
        Xml.appendChild(Xml.createElement('BUDGETLINE'));
        Xml.documentElement.setAttribute('action', 'check_bring_lim_ass');
        Xml.documentElement.setAttribute('ESTIMATE_ID', Dest.getAttribute('ESTIMATE_ID'));
        Xml.documentElement.setAttribute('RECIPIENT_ID', Dest.getAttribute('RECIPIENT_ID'));
        Xml.documentElement.setAttribute('KFSR_CODE', Dest.getAttribute('KFSR_CODE'));
        Xml.documentElement.setAttribute('KCSR_CODE', Dest.getAttribute('KCSR_CODE'));
        Xml.documentElement.setAttribute('KVR_CODE', Dest.getAttribute('KVR_CODE'));
        Xml.documentElement.setAttribute('KESR_CODE', Dest.getAttribute('KESR_CODE'));
        Xml.documentElement.setAttribute('KADMR_CODE', Dest.getAttribute('KADMR_CODE'));
        Xml.documentElement.setAttribute('KDF_CODE', Dest.getAttribute('KDF_CODE'));
        Xml.documentElement.setAttribute('KDE_CODE', Dest.getAttribute('KDE_CODE'));
        Xml.documentElement.setAttribute('KDR_CODE', Dest.getAttribute('KDR_CODE'));
        Xml.documentElement.setAttribute('FSR_ID',  Dest.getAttribute('FSR_ID'));
        // exchange #2389
        // для версии АЦК-финансы 2.29.0.80 обязательно требуется передавать код цели
        // без этого возникает сбой!
        // если значение кода субсидии не передается - подставляем нулевое значение
        // для ткущего бюджета
        if (LENGTH( VarToStr( Dest.getAttribute('PURPOSEFULGRANT_ID'))) <= 0)
          then Xml.documentElement.setAttribute( 'PURPOSEFULGRANT_ID',
                        Exch2389_GetPURPOSEFULGRANT_ID_NULL_ByBudget( Budget_Id))

          else Xml.documentElement.setAttribute( 'PURPOSEFULGRANT_ID',
                                          Dest.getAttribute('PURPOSEFULGRANT_ID'));

        Xml.LoadXml(ExecXml(Xml.xml));

        FlagLim := (Xml.documentElement.getAttribute('result') and 1) > 0;
        FlagAss := (Xml.documentElement.getAttribute('result') and 2) > 0;
      end else begin
        Xml := QueryXml('Select E.Bring_Ass_Flag, E.Bring_Lim_Flag' +
          ' From Estimate E Where E.Id = ?', [Dest.getAttribute('ESTIMATE_ID')]);
        Xml := Xml.documentElement.childNodes.items[0];
        FlagAss := Xml.getAttribute('BRING_ASS_FLAG') = 1;
        FlagLim := Xml.getAttribute('BRING_LIM_FLAG') = 1;
      end;



      // exchange #2777
      // чтобы меньше дорабатывать код, согласовано с АО, если активна
      // настройка "Принимать сумму ЛБО только текущего года", то вместо
      // проверки параметра "Разрешать доведение лимитов" в карточке бланка расходов,
      // поля из входной xml AMOUNT2YEAR и AMOUNT3YEAR просто будут сброшены в нулевое
      // значение, а в конце подпрограммы - восстановлены.
      // в результате логика п.3 задания отработается как раньше,
      // а будет всего лишь работать логика по п.2 задания.
      // сохраняем значения параметров из входной xml
      xAMOUNT2YEAR := VarToStr( Source.getAttribute('AMOUNT2YEAR'));
      xAMOUNT3YEAR := VarToStr( Source.getAttribute('AMOUNT3YEAR'));
      try
        // сбрасываем поля суммы 2,3 года из входной xml (из планирования)

        if cbSummaLBOcurrYear.Checked then begin

          Source.SetAttribute('AMOUNT2YEAR', '0');
          Source.SetAttribute('AMOUNT3YEAR', '0');
        end;

        if FlagAss then begin

          Dest.setAttribute('ASSIGN_AMT1', Source.getAttribute('YEAR_AMT'));
          if Period = 3 then begin
            // exchange #2841
            // суммы по ассигнованиям не должны очищаться, поэтому заполняем
            // их не из xml а из промежуточных переменных, в которых храняться
            // необнуленные значения.

            Dest.setAttribute('ASSIGN_AMT2', xAMOUNT2YEAR);//Source.getAttribute('AMOUNT2YEAR'));
            Dest.setAttribute('ASSIGN_AMT3', xAMOUNT3YEAR);//Source.getAttribute('AMOUNT3YEAR'));
          end;
        end;

        if FlagLim and not chkAssingOnly.Checked then begin

          Dest.setAttribute('LIMIT_AMT1', Source.getAttribute('YEAR_AMT'));
          if Period = 3 then begin

            Dest.setAttribute('LIMIT_AMT2', Source.getAttribute('AMOUNT2YEAR'));
            Dest.setAttribute('LIMIT_AMT3', Source.getAttribute('AMOUNT3YEAR'));
          end;
        end;
        finally
          // восстанавливаем параметры во входной xml (из планирования)
          if cbSummaLBOcurrYear.Checked then begin
            Source.SetAttribute('AMOUNT2YEAR', xAMOUNT2YEAR);
            Source.SetAttribute('AMOUNT3YEAR', xAMOUNT3YEAR);
          end;
      end;

      if not FlagAss and chkAssingOnly.Checked then begin
        if not chkGroup.checked then
          with Source.parentNode do begin
            if Temp_Id <> Source.parentNode.getAttribute('ID') then
              WriteInfo('Документ № ' + getAttribute('DOC_NUMBER') +
              ' от ' + getAttribute('DOC_DATE') + ' - не будет создан');
            Temp_Id := Source.parentNode.getAttribute('ID');
            Dest.parentNode.parentNode.setAttribute('ERROR', 'ERROR');
          end;//with//
        WriteInfo('По строке не разрешено доведение ассигнований:' + #13 + #10 + RowInfo(Source));
      end;

      if FlagAss or (DocumentClass_Id in [133, 134]) then begin
        Dest.setAttribute('Q1_AMT', Source.getAttribute('Q1_AMT'));
        Dest.setAttribute('Q2_AMT', Source.getAttribute('Q2_AMT'));
        Dest.setAttribute('Q3_AMT', Source.getAttribute('Q3_AMT'));
        Dest.setAttribute('Q4_AMT', Source.getAttribute('Q4_AMT'));
        if (chkQuart.checked
            And (( StrToCurrDef(Source.getAttribute('Q1_AMT'), 0)
                  +StrToCurrDef(Source.getAttribute('Q2_AMT'), 0)
                  +StrToCurrDef(Source.getAttribute('Q3_AMT'), 0)
                  +StrToCurrDef(Source.getAttribute('Q4_AMT'), 0)) = 0)
            And (StrToCurrDef(Source.getAttribute('YEAR_AMT'), 0) <> 0))
          then begin
            Dest.setAttribute(
                  'Q1_AMT',
                  CurrToStr( 0.01
                            *StrToCurrDef(Source.getAttribute('YEAR_AMT'), 0)
                            *edtQuart1.Value));
            Dest.setAttribute(
                  'Q2_AMT',
                  CurrToStr( 0.01
                            *StrToCurrDef(Source.getAttribute('YEAR_AMT'), 0)
                            *edtQuart2.Value));
            Dest.setAttribute(
                  'Q3_AMT',
                  CurrToStr( 0.01
                            *StrToCurrDef(Source.getAttribute('YEAR_AMT'), 0)
                            *edtQuart3.Value));
            Dest.setAttribute(
                  'Q4_AMT',
                  CurrToStr( 0.01
                            *StrToCurrDef(Source.getAttribute('YEAR_AMT'), 0)
                            *edtQuart4.Value));
          end;

        if (( StrToCurrDef(Source.getAttribute('Q1_AMT'), 0)
             +StrToCurrDef(Source.getAttribute('Q2_AMT'), 0)
             +StrToCurrDef(Source.getAttribute('Q3_AMT'), 0)
             +StrToCurrDef(Source.getAttribute('Q4_AMT'), 0)) <> 0)
            and (StrToCurrDef(Source.getAttribute('YEAR_AMT'), 0) = 0)
          then
            Source.setAttribute(
                'YEAR_AMT',
                CurrToStr( StrToCurrDef(Source.getAttribute('Q1_AMT'), 0)
                          +StrToCurrDef(Source.getAttribute('Q2_AMT'), 0)
                          +StrToCurrDef(Source.getAttribute('Q3_AMT'), 0)
                          +StrToCurrDef(Source.getAttribute('Q4_AMT'), 0)));

        Dest.setAttribute('M_01_AMT', Dest.getAttribute('Q1_AMT'));
        Dest.setAttribute('M_04_AMT', Dest.getAttribute('Q2_AMT'));
        Dest.setAttribute('M_07_AMT', Dest.getAttribute('Q3_AMT'));
        Dest.setAttribute('M_10_AMT', Dest.getAttribute('Q4_AMT'));
      end;

      { exchange #2395
        для случая, когда настройка «Сервис – Системные параметры – Кассовый план
        – Разбивка кассового плана по расходам» равна «Помесячно»
        переносим поля месячных сумм }
      if IsImport_ExpKassPlan_MonthDiv( Source.parentNode) then
        try
    
          Dest.setAttribute('M_01_AMT', VarToStr( Source.getAttribute('QM1_AMT')));
          Dest.setAttribute('M_02_AMT', VarToStr( Source.getAttribute('QM2_AMT')));
          Dest.setAttribute('M_03_AMT', VarToStr( Source.getAttribute('QM3_AMT')));
          Dest.setAttribute('M_04_AMT', VarToStr( Source.getAttribute('QM4_AMT')));
          Dest.setAttribute('M_05_AMT', VarToStr( Source.getAttribute('QM5_AMT')));
          Dest.setAttribute('M_06_AMT', VarToStr( Source.getAttribute('QM6_AMT')));
          Dest.setAttribute('M_07_AMT', VarToStr( Source.getAttribute('QM7_AMT')));
          Dest.setAttribute('M_08_AMT', VarToStr( Source.getAttribute('QM8_AMT')));
          Dest.setAttribute('M_09_AMT', VarToStr( Source.getAttribute('QM9_AMT')));
          Dest.setAttribute('M_10_AMT', VarToStr( Source.getAttribute('QM10_AMT')));
          Dest.setAttribute('M_11_AMT', VarToStr( Source.getAttribute('QM11_AMT')));
          Dest.setAttribute('M_12_AMT', VarToStr( Source.getAttribute('QM12_AMT')));
          except
            ExceptionLog( 'Сбой при переносе полей месячных сумм.');
        end;

        { exchange #2511
          переносим значение "группа по бюджету" из карточки бланка расходов
          в поле "группа по бюджету" строки для ЭД класса 127.
          если поле не заполнено (=null) то поле не заполняем.
          проверять нужно не класс документа, а переменную класса из конфига.
          таким же образом переносится группа по бухгалтерии.}
        if (DocumentClass_Id = 127) then
          if (LENGTH( VarToStr( Dest.GetAttribute('ESTIMATE_ID'))) > 0) then begin
    
            xGroupByBudg  := TRIM(
                               QueryField(
                                 ' select e.DEF_LINEGROUP_CODE, e.DEF_MEMGROUP_CODE'
                                +' from estimate e where e.ID=?',
                                 [ Dest.GetAttribute('ESTIMATE_ID')],
                                 'DEF_LINEGROUP_CODE'));
            xBuhByBudg    := TRIM(
                               QueryField(
                                 ' select e.DEF_LINEGROUP_CODE, e.DEF_MEMGROUP_CODE'
                                +' from estimate e where e.ID=?',
                                 [ Dest.GetAttribute('ESTIMATE_ID')],
                                 'DEF_MEMGROUP_CODE'));
    
            if (LENGTH( xGroupByBudg) > 0)
              then Dest.SetAttribute( 'LINEGROUP_CODE', xGroupByBudg);
            if (LENGTH( xBuhByBudg) > 0)
              then Dest.SetAttribute( 'MEMGROUP_CODE', xBuhByBudg);
          end;
      except
        ExceptionProc( 'CopyLineDoc');
    end;

end; {End CopyLineDoc}
{-----------------------------------------------------------------------------}
function DoSearchDoc_( aXmlPlan: Variant;
                       aDocClass, aInClassID, aInID: string;
                       var aLineFound: Variant): Variant;
var
  i,i2: integer;
  xXmlDoc: Variant;
  xXmlDocNode: Variant;
  xXmlDocLine: Variant;
  xXmlDocLineNew: Variant;
begin

    try
      xXmlDoc := aXmlPlan;//Xml.ParentNode;
      for i := 0 to xXmlDoc.childNodes.length - 1 do begin
        xXmlDocNode := xXmlDoc.childNodes.items[ i];
        if (GetNodeAttributeEx( xXmlDocNode, 'DOCUMENTCLASS_ID') = aDocClass) then
          for i2 := 0 to xXmlDocNode.childNodes.length - 1 do begin
            xXmlDocLine := xXmlDocNode.childNodes.items[ i2];
            if (xXmlDocLine.NodeName = 'LINE') then
              if (GetNodeAttributeEx( xXmlDocLine, 'DOCUMENTCLASS_ID') = aInClassID) then
                if (GetNodeAttributeEx( xXmlDocLine, 'ID') = aInID) then begin
                  aLineFound := xXmlDocLine;
                  Result := xXmlDocNode;
                  EXIT;
                end;
          end;//for//
      end;//for//
      Result := NULL;
      aLineFound := NULL;
      except
        ExceptionProc( 'DoSearchDoc_');
    end;

end;
{-----------------------------------------------------------------------------}
{~SplitDoc
  разделение эд Xml на несколько, для разных строк в исходном эд.
  фактически это группировка строк в каждом убн.
  если в убн есть строки отличающиеся кбк (квфо+кбк+доп кд+RECIPIENT_ID),
  то такие строки переносятся в разные убн.
  т.е. в каждом убн должны быть строки с одинаковыми кбк, но отличающиеся кц.
  это разделение безусловно, т.е. галки группировки на это не влияют.

  exchange #3088
  группировка будет работаеть только по настройке "группировать документы".
  если группировка отключена - строки не будут группироваться в эд}
function SplitDoc(Xml: Variant): Variant;
  {==========================================================================}
  procedure DoUpdateDoc_( aDocClass, aInClassID, aInID, aNewKey: string;
                          aUpdateLine: boolean);
  var
    xXmlDoc: Variant;
    xXmlDocLineNew: Variant;
  begin

      try
        xXmlDoc := DoSearchDoc_( Xml.ParentNode,
                                 aDocClass,
                                 aInClassID,
                                 aInID,
                                 xXmlDocLineNew);
        if NOT VarIsNULL( xXmlDoc) then begin
          // добавляем строку в эд с новым описанием эд
          // поскольку на текущий момент поле id не известно,
          // сохраняем поле key.
          if NOT aUpdateLine then begin
            xXmlDocLineNew := xXmlDoc.appendChild(
                                  xXmlDoc.OwnerDocument.createElement( 'LINE'));
            xXmlDocLineNew.SetAttribute( 'DOCUMENTCLASS_ID', aInClassID);
          end;

          xXmlDocLineNew.SetAttribute( cnFieldForegnkeyInt, aNewKey);
          (*
          // сохраняем новое значение key с пустым id
          vListNewKey.Add( aNewKey);
          vListNewKeyID.Add( '');
          *)

        end;
        except
          ExceptionProc( 'DoUpdateDoc_');
      end;

  end;
  {==========================================================================}
var
  i, xIndex: Integer;
  Md5Str: String;
  List: TStrings;
  xXmlLine: Variant;
  xResultDoc: Variant;
  xResultDocNew: Variant;
  xResultDocNewLines: Variant;
//  xResultDocNewLine: Variant;
  xListDocLines: Variant;
  xResultDocCount: integer;
  xFOREIGN_KEY: string;
begin

    try
      Result := CreateOleObject('Microsoft.XmlDom');
      xResultDoc := Result.appendChild( Result.createElement('ROOT'));
      List := TStringList.Create;
      xListDocLines := NULL;
      // необходимо инициализировать локальные переменные,
      // иначе при повторном запуске скрипта, будет проблема - видимо
      // они не инициализируются и возникает непредсказуемый сбой.
      xResultDocNewLines := NULL;
      xResultDocCount := 0;
      //Temp_Id := '';
      try
        for i := 0 to Xml.childNodes.length - 1 do begin
          xXmlLine := Xml.childNodes.items[i];

          if chkGroup.Checked then begin
            // группировка

            Md5Str := MD5String(
                       'FSR_ID = ' + xXmlLine.getAttribute('FSR_ID')
                      +'KADMR_CODE = ' + xXmlLine.getAttribute('KADMR_CODE')
                      +'KCSR_CODE = ' + xXmlLine.getAttribute('KCSR_CODE')
                      +'KDE_CODE = ' + xXmlLine.getAttribute('KDE_CODE')
                      +'KDF_CODE = ' + xXmlLine.getAttribute('KDF_CODE')
                      +'KDR_CODE = ' + xXmlLine.getAttribute('KDR_CODE')
                      +'KESR_CODE = ' + xXmlLine.getAttribute('KESR_CODE')
                      +'KFSR_CODE = ' + xXmlLine.getAttribute('KFSR_CODE')
                      +'KVR_CODE = ' + xXmlLine.getAttribute('KVR_CODE')
                      +'RECIPIENT_ID = ' + xXmlLine.getAttribute('RECIPIENT_ID')
                      {
                      // exchange #2859
                      // добавлено разделение по КЦ
                      +'PURPOSEFULGRANT_ID = '
                            + VarToStr( xXmlLine.getAttribute('PURPOSEFULGRANT_ID'))});

            xIndex := List.IndexOf(Md5Str);
  //          if (xResultDoc.ownerDocument.documentElement.childNodes.length=0) then begin
            if (xIndex = -1) then begin
              List.Add( Md5Str);
              // создаем описание нового эд
  
              xResultDocNew := xResultDoc.appendChild(
                                              Result.createElement('DOCUMENT'));
              CopyHeadDoc(Xml, xResultDocNew);
              xResultDocNewLines := xResultDocNew.appendChild(
                                              Result.createElement('LINES'));
              // добавляем в массив индексов ЭД узел списка строк нового эд
              AddToVarArray( xListDocLines, xResultDocNewLines);

              // exchange #2859
              // при разделении эд на несколько по разным строкам, в разных эд
              // будет шапка одинаковая, включая foregnkey. при передачи таких эд
              // в ацк будетсоздан только первый эд, а прочие, с одинаковым
              // foregnkey не создаются. причем ацк не ругается.
              // даже если здесь подменить foregnkey нужно еще добавить такой эд
              // в сводный. т.е. найти сводный убн, в который входит убн, с таким
              // foregnkey и добавить новую строку для разделенного убн с новым
              // foregnkey. обновление foregnkey делаем только для всех строк,
              // но для первой строки id уже есть - он указан в хмл из
              // планирования, но на него не обращаем внимания.
              INC( xResultDocCount);
              xFOREIGN_KEY := GetNodeAttributeEx( xResultDocNew, 'FOREIGN_KEY');
              if (xFOREIGN_KEY <> '') then begin
                // обновляем FOREIGN_KEY в новом эд
                if (xResultDocCount > 1)
                  then xFOREIGN_KEY := xFOREIGN_KEY + '_' + INTTOSTR( xResultDocCount);
                xResultDocNew.SetAttribute( 'FOREIGN_KEY', xFOREIGN_KEY);
                // добавляем новые промежуточные поля в новый эд
                case GetNodeAttributeEx( xResultDocNew, 'class') of
                  // УБН: ищем сводный УБН в хмл из планирования (docclass=160),
                  // и строку в сводном (docclass=8, id=...).
                  // если такой сводный найден - добавляем новую строку
                  // в сводный с новым значением foregnkey, поскольку на этот
                  // момент id УБН не известен.
                  '127': DoUpdateDoc_( '160',
                                       '8',
                                       GetNodeAttributeEx( Xml, 'ID'),
                                       xFOREIGN_KEY,
                                       (xResultDocCount = 1));
                end;//case//
              end;
            end else begin
              xResultDocNewLines := xListDocLines[ xIndex];
  //            xResultDocNewLines := xListDocLines[ 0];
            end;
          end else begin
            // без группировки

            if VarIsNULL( xResultDocNewLines) then begin
              xResultDocNew := xResultDoc.appendChild(
                                              Result.createElement('DOCUMENT'));
              CopyHeadDoc(Xml, xResultDocNew);
              xResultDocNewLines := xResultDocNew.appendChild(
                                              Result.createElement('LINES'));
            end;
          end;
          // добавляем строку в эд

          CopyLineDoc( xXmlLine,
                       xResultDocNewLines.appendChild(
                                               Result.createElement('LINE')));
        end;//for//
        finally
          List.Free;
          xListDocLines := NULL;
      end;
      except
        ExceptionProc( 'SplitDoc');
    end;

end; {End SplitDoc}
{-----------------------------------------------------------------------------}
procedure DoUpdateForegnkeys( aDocAzk, aDocExported, aXmlPlan: Variant);
var
  i,i2: integer;
  xXmlDoc: Variant;
  xXmlDocNode: Variant;
  xXmlDocLine: Variant;
  xXmlDocLineNew: Variant;
  xIndex: integer;
  xSearchDocClass: string;
//  xID: string;
  xFOREIGN_KEY: string;
begin

    try
      // определяем, какие эд ищем по созданному эд aDocAzk
      case GetNodeAttributeEx( aDocAzk, 'DOCUMENTCLASS_ID') of
        '127': xSearchDocClass := '160';
        else EXIT;
      end;//case//

      xFOREIGN_KEY := GetNodeAttributeEx( aDocExported, 'FOREIGN_KEY');
      (*
      // устанавливаем ассоциацию: FOREIGN_KEY-ID
      xFOREIGN_KEY := GetNodeAttributeEx( aDocExported, 'FOREIGN_KEY');
      xIndex := vListNewKey.IndexOf( xFOREIGN_KEY);
      if (xIndex = -1) then EXIT;
      xID := GetNodeAttributeEx( aDocAzk, 'ID');

      vListNewKeyID.Strings[ xIndex] := xID;
      *)

      // ищем сводный УБН и заменяем спец поле foregnkey на ID
      xXmlDoc := aXmlPlan;
      for i := 0 to xXmlDoc.childNodes.length - 1 do begin
        xXmlDocNode := xXmlDoc.childNodes.items[ i];
        if (GetNodeAttributeEx( xXmlDocNode, 'DOCUMENTCLASS_ID') = xSearchDocClass) then
          for i2 := 0 to xXmlDocNode.childNodes.length - 1 do begin
            xXmlDocLine := xXmlDocNode.childNodes.items[ i2];
            if (xXmlDocLine.NodeName = 'LINE') then
              if (GetNodeAttributeEx( xXmlDocLine, cnFieldForegnkeyInt) = xFOREIGN_KEY)
                then begin
                  xXmlDocLine.SetAttribute( 'ID', GetNodeAttributeEx( aDocAzk, 'ID'));

                  EXIT;
                end;
          end;//for//
      end;//for//
      except
        ExceptionProc( 'DoUpdateForegnkeys');
    end;

end;
{-----------------------------------------------------------------------------}
{~FillDoc
  формирование УБН для АЦК-Фин (результат). на основе сводной бюджетной заявки
  (160й класс) из планирования (Xml).
  результат =null, если будет проблема с вложенными ЭД.}
function FillDoc(Xml: Variant): Variant;
var
  i: Integer;
  Doc: Variant;
  xLine: Variant;
  xCarryDateExists: boolean;
  xLines: variant;
  xCarryDateStr: string;
  xXmlLine: Variant;

  procedure WriteHeadError;
  begin
    if VarIsNull(Result) then
      Exit;
    WriteInfo('При вставке в сводный УБН, УиБН с номером: ' +
      Xml.getAttribute('DOC_NUMBER') + ' от: ' + Xml.getAttribute('DOC_DATE') +
      ' по бланку расходов: ' + Xml.getAttribute('EST_CAPTION') + ' - произошли ошибки:');
  end;
begin

    try
      Result := CreateOleObject('Microsoft.XmlDom');
      Result := Result.appendChild(Result.createElement('ROOT'));
      Result := Result.appendChild(Result.ownerDocument.createElement('DOCUMENT'));
      CopyHeadDoc(Xml, Result);
      Result.setAttribute('action', 'generate');

      // exchange #2859
      // отключается создание строк в сводном убн.
      // видимо в результате требуется формировать строки эксчем, которые будут
      // указаны во входной XML в описании сводного эд.
      // ОТЛОЖЕНО
      Result.setAttribute('DocsMustBeCalculated', 'YES');

      // тег строк входящих ЭД в сводный УБН
      xLines := Result.appendChild(
                    Result.ownerDocument.createElement('EXPASSIGNMENTDOC'));

      // пока дату утверждения в связанных ЭД не нашли
      xCarryDateExists := FALSE;

      for i := 0 to Xml.childNodes.length - 1 do begin
        xXmlLine := Xml.childNodes.items[i];

        // exchange #2859
        // поменялся формат xml, поэтому здесь нужно проверять теги LINE,
        // но не обрабатывать тег ROW
        if (xXmlLine.NodeName <> 'LINE')
          then CONTINUE;

        // exchange #2859
        // если есть спец поле замены forengkey, то id в сводном УБН
        // найдены по новым ключам, поэтому ищем напрямую по ID.
        if (GetNodeAttributeEx( xXmlLine, cnFieldForegnkeyInt) <> '')
          then Doc := QueryXml(  ' Select Id, Document_Id, Version,'
                                +'   DispStatus_Id, carry_date'
                                +' From ExpAssignment Where id = ?'
                                +' And Not DispStatus_Id In (-1, 7)',
                                 [ xXmlLine.getAttribute('ID')])
          else Doc := QueryXml(  ' Select Id, Document_Id, Version,'
                                +'   DispStatus_Id, carry_date'
                                +' From ExpAssignment Where Foreign_Key = ?'
                                +' And Not DispStatus_Id In (-1, 7)',
                                // exchange #2859
                                // по этой задаче запрос переделан в параметрезированный
                                 [ xXmlLine.getAttribute('ID')
                                  +xXmlLine.getAttribute('DOCUMENTCLASS_ID')]);
        if Doc.documentElement.childNodes.length = 0 then begin
          WriteHeadError;
          WriteInfo( 'Не найден УБН, УиБН с референсом: '
                    +Xml.childNodes.items[i].getAttribute('ID'));
          // при этом результат сбрасываем
          Result := Null;
          CONTINUE;
        end;
        if Doc.documentElement.childNodes.length > 1 then begin
          WriteHeadError;
          WriteInfo( 'В базе АЦК несколько УБН, УиБН с референсом: '
                    +xXmlLine.getAttribute('ID'));
          // при этом результат сбрасываем
          Result := Null;
          CONTINUE;
        end;

        if not VarIsNull(Result) then begin
          Doc := Doc.documentElement.childNodes.items[0];
          // добавление строки в сводный УБН
          xLine := xLines.appendChild(
                      Result.ownerDocument.createElement('EXPASSIGNMENTDOC'));
          xLine.setAttribute('DOC_ID'       , Doc.getAttribute('ID'));
          xLine.setAttribute('DOCUMENT_ID'  , Doc.getAttribute('DOCUMENT_ID'));
          xLine.setAttribute('VERSION'      , Doc.getAttribute('VERSION'));

         {exchange #2555
          1.3 При создании сводных ЭД УБН, УиБН  по расходам в результате импорта
              из АЦК-Планирование с помощью АЦК-Exchange документов, связанных со
              сводными ЭД (тег PLANASSDOCSUM_ID заполнен) поле «Дата утверждения» в
              создаваемом в АЦК-Финансы сводном документе должно заполняться значением
              поля «Дата утверждения» любого из связанных ЭД УБН, УиБН по расходам.}
          if chkToStatus10.checked then
            // до первой определенной даты утверждения (не null) в любом из
            // вложенном ЭД
            if NOT xCarryDateExists then begin
              xCarryDateStr := VarToStr( Doc.GetAttribute('CARRY_DATE'));
              if (LENGTH( xCarryDateStr) > 0) then
                // если во вложенном ЭД есть дата утверждения, то переносим ее
                // в шапку сводного убн. делаем это с отловом сбоя при преобразовании
                // даты из строки и формированием даты в поле атрибута.
                // если все пройдет успешно, то из других ЭД дату утверждения
                // переносить в сводный убн не будем.
                try
                  Result.SetAttribute( 'CARRY_DATE',
                                       FormatDateTime(
                                                'yyyy-mm-dd',
                                                XmlToDateTime( xCarryDateStr)));
                  xCarryDateExists := TRUE;
                  except
                end;
            end;
        end;
      end;//for//

      if not VarIsNull(Result)
        then Result := Result.ownerDocument;
      except
        ExceptionProc( 'FillDoc');
    end;

end; {End FillDoc}
{-----------------------------------------------------------------------------}
procedure checkEstimate(aRow: Variant);
begin

    try
      if ( LENGTH( VarToStr( aRow.getAttribute('EST_ID'))) <= 0)
        then WriteError('В ЭД не указан бланк расходов.')
        else begin
          Sql := 'Select Id From Estimate Where Budget_Id = ? And Id = ?';
          if QueryField(sql,[Budget_Id, aRow.getAttribute('EST_ID')], 'ID') = null
            then WriteError('В АЦК не найдена бланк расходов: ' + aRow.getAttribute('EST_CAPTION'));
        end;
      except
        ExceptionProc( 'checkEstimate');
    end;

end;
{-----------------------------------------------------------------------------}
procedure checkAssignSource(aRow: Variant);
begin

    try
      if VarToStrDef(aRow.getAttribute('ASSIGN_SOURCE_CODE'), '') <> '' then
      begin
        Sql := 'Select Id From Assign_Source Where Budget_Id = ? And Code = ?';
        VarValue := QueryField(sql,[Budget_Id, aRow.getAttribute('ASSIGN_SOURCE_CODE')], 'ID');
        if VarValue = null then
          WriteError('В АЦК не найдена код источника изменений ассигнований и лимитов: ' + aRow.getAttribute('ASSIGN_SOURCE_CODE'))
        else
          aRow.setAttribute('ASSIGN_SOURCE_ID', VarValue);
      end;

      except
        ExceptionProc( 'checkAssignSource');
    end;

end;
{-----------------------------------------------------------------------------}
{~CheckAPPROVE_BUDGET_DATE_By_PLANASSDOCSUM_ID
  exchange #2390 ФТ4 пп.1
  проверяется одинаковость тегов APPROVE_BUDGET_DATE для всех ЭД с
  одним и тем же значением тега PLANASSDOCSUM_ID для классов 163 и 164.
  тег PLANASSDOCSUM_ID определяет ид.сводного ЭД. сводный ЭД может включать
  множество ЭД классов 163 и 164. вот и надо проверить, чтобы у всех ЭД,
  входящих в сводный ЭД поле APPROVE_BUDGET_DATE было одинаковым.}
procedure CheckAPPROVE_BUDGET_DATE_By_PLANASSDOCSUM_ID(aXml: Variant);
var
  i,j: integer;
  xNode: Variant;
  xNodeEx: Variant;
  xPLANASSDOCSUM_ID: string;
  xAPPROVE_BUDGET_DATE: string;
  xSL: TStringList;
begin

    try
      xSL:= TStringList.Create;
      try
        for i := 0 to aXml.childNodes.length - 1 do begin
          xNode := aXml.childNodes.items[i];
  
          if (xNode.GetAttribute('DOCUMENTCLASS_ID') in ['163','164'])
            then begin
              xPLANASSDOCSUM_ID := VarToStr(xNode.getAttribute('PLANASSDOCSUM_ID'));
  
              // игнорируем пустые теги PLANASSDOCSUM_ID
              if (LENGTH( xPLANASSDOCSUM_ID) > 0) then
                // проверяем только разные теги PLANASSDOCSUM_ID
                // для этого проверенные значения тегов сохраняем в списке xSL
                // текущий проверяемый ЭД считаем эталонным
                if (xSL.IndexOf( xPLANASSDOCSUM_ID) = -1) then begin
                  // такой тег еще не проверяли
                  xSL.Add( xPLANASSDOCSUM_ID);
                  // проверяем, есть ли другие даты в группе ЭД с таким же тегом
                  // xPLANASSDOCSUM_ID?
                  xAPPROVE_BUDGET_DATE :=
                            VarToStr( xNode.getAttribute( 'APPROVE_BUDGET_DATE'));
                  for j := 0 to aXml.childNodes.length - 1 do begin
                    // пропускаем проверку одного и того же ЭД (т.е.эталонного)
                    if (i <> j) then begin
                      xNodeEx := aXml.childNodes.items[ j];
                      if (xNodeEx.GetAttribute('DOCUMENTCLASS_ID')
                                                            in ['163','164']) then
  
                        if (xPLANASSDOCSUM_ID
                            = VarToStr(xNodeEx.getAttribute('PLANASSDOCSUM_ID'))) then
                          if (xAPPROVE_BUDGET_DATE
                              <> VarToStr( xNodeEx.getAttribute('APPROVE_BUDGET_DATE')))
                            then begin
                              WriteError( 'В XML – сообщении присутствуют документы, '
                                          +'входящие в сводный документ, в которых '
                                          +'отличается дата утверждения закона.');
                              // до первой ошибки, поскольку здесь не расшифровываются
                              // какие именно ЭД являются ошибочными
                              EXIT;
                            end;
                    end;
                  end;//for//
                end;
            end;
        end;//for//
        finally
          xSL.Free;
      end;
      except
        ExceptionProc( 'CheckAPPROVE_BUDGET_DATE_By_PLANASSDOCSUM_ID');
    end;

end;
{-----------------------------------------------------------------------------}
procedure checkDoc(Xml: Variant);
var
  i, j, i2: Integer;
  Row, Line: Variant;
  class_id: integer;
  xTextError: string;
  xQMFieldName: string;
begin

    try
      // exchange #2390
      CheckAPPROVE_BUDGET_DATE_By_PLANASSDOCSUM_ID( Xml);

      ProgressBar.Max := Xml.childNodes.length;
      for i := 0 to Xml.childNodes.length - 1 do begin
        class_id := StrToIntDef(VarToStr(Xml.childNodes.items[i].getAttribute('DOCUMENTCLASS_ID')), 0);
        Row := Xml.childNodes.items[i];

        if (not (class_id in [161, 160])) then checkEstimate(Row);
        checkAssignSource(Row);
        // игнорируем сводные убн
        if (class_id <> 160) then
          for j := 0 to Row.childNodes.length - 1 do begin
            Line := Row.childNodes.items[j];
            if (class_id = 161) then checkEstimate(Line);
            checkKBK(Line, 'KADMR');
            checkKBK(Line, 'KCSR');
            checkKBK(Line, 'KDE');
            checkKBK(Line, 'KDF');
            checkKBK(Line, 'KDR');
            checkKBK(Line, 'KESR');
            checkKBK(Line, 'KFSR');
            checkKBK(Line, 'KVR');
            if QueryField('Select Id From FinSource Where Id = ?',
              [Line.getAttribute('FSR_ID')], 'ID') = null then
                WriteError('В АЦК не найден  код источника финансирования: ' + Line.getAttribute('FSR_ID'));
            if QueryField('Select Id From Org Where Id = ?',
              [Line.getAttribute('RECIPIENT_ID')], 'ID') = null then
                WriteError('В АЦК не найден  бюджетополучатель: ' + Line.getAttribute('RECIPIENT_CAPTION'));
            if Line.getAttribute('PL_DIRECTION_ID') <> '' then
              if QueryField('Select Id From Pl_Direction Where Id = ?',
                [Line.getAttribute('PL_DIRECTION_ID')], 'ID') = null then
                  WriteError('В АЦК не найдено  расходное обязательство: ' +
                    'Наименование - ' + Line.getAttribute('PL_DIRECTION_NAME') +
                    ', Наименование РБС/ПБС - ' + Line.getAttribute('PL_DIRECTION_ORG_NAME'));
            if VarToStr(Line.getAttribute('PURPOSEFULGRANT_ID')) <> '' then
              if QueryField('Select Id From PurposeFulGrant Where Id = ?',
                [VarToStr(Line.getAttribute('PURPOSEFULGRANT_ID'))], 'ID') = null then
                  WriteError('В АЦК не найден код субсидии с референсом: ' +
                    VarToStr(Line.getAttribute('PURPOSEFULGRANT_ID')));
            { exchange #2395
              для случая, когда настройка «Сервис – Системные параметры – Кассовый
              план – Разбивка кассового плана по расходам» равна «Помесячно»:
              1) Если в принимаемом документе обязательное поле не содержит значения,
                  то обработку документа прервать. Записать в лог строку:
                  «В документе №<номер документа> от <дата документа> отсутствуют значения
                  обязательных полей <перечень обязательных полей, значения которых»
                  отсутствуют>. Каждое поле выводить с новой строки.
                  Перейти к обработке следующего документа.
                  обязательными полями являются: QM1_AMT...QM12_AMT.
              2) В принимаемом документе квартальные суммы должны соответствовать
                 суммам по месяцам, входящим в каждый квартал, согласно условиям:
                  •	Q1_AMT = QM1_AMT + QM2_AMT + QM3_AMT;
                  •	Q2_AMT = QM4_AMT + QM5_AMT + QM6_AMT;
                  •	Q3_AMT = QM7_AMT + QM8_AMT + QM9_AMT;
                  •	Q4_AMT = QM10_AMT + QM11_AMT + QM12_AMT.
                  Если хотя бы одно условие не выполняется, то обработку документа
                  прервать. Записать в лог строку:
                  «В документе №<номер документа> от <дата документа> квартальная
                  сумма не соответствует сумме по месяцам».}
            try
              if IsImport_ExpKassPlan_MonthDiv( Row) then begin
      
                // проверка по пп.1
                xTextError := '';
                for i2 := 1 to 12 do begin
                  xQMFieldName  := FORMAT( 'QM%d_AMT', [ i2]);
      
                  if ( LENGTH( VarToStr( Line.GetAttribute( xQMFieldName))) <= 0)
                    then xTextError := xTextError
                                        + FORMAT( #13#10'Сумма месяца %d (поле - %s)',
                                                  [ i2, xQMFieldName]);
                end;//for//
      
                if (LENGTH( xTextError) > 0)
                  then WriteError(
                              FORMAT( 'В документе №%s от %s отсутствуют значения '
                                     +'обязательных полей, значения которых отсутствуют:%s',
                                      [ Row.GetAttribute('DOC_NUMBER'),
                                        Row.getAttribute('DOC_DATE'),
                                        xTextError]))
                  else begin
                    // проверка по пп.2
                    // здесь при ошибке не трубется детального уточнения,
                    // поэтому вместо флажка ошибки в строку текста ошибки помещаем
                    // произвольный текст, наличие которого и укажет на ошибку.
                    xTextError := '';
                    if ( StrToCurrDef( Line.GetAttribute('Q1_AMT'),0)
                         <> ( StrToCurrDef( Line.GetAttribute('QM1_AMT'),0)
                             +StrToCurrDef( Line.GetAttribute('QM2_AMT'),0)
                             +StrToCurrDef( Line.GetAttribute('QM3_AMT'),0)))
                      then xTextError := ' ';
                    if ( StrToCurrDef( Line.GetAttribute('Q2_AMT'),0)
                         <> ( StrToCurrDef( Line.GetAttribute('QM4_AMT'),0)
                             +StrToCurrDef( Line.GetAttribute('QM5_AMT'),0)
                             +StrToCurrDef( Line.GetAttribute('QM6_AMT'),0)))
                      then xTextError := ' ';
                    if ( StrToCurrDef( Line.GetAttribute('Q3_AMT'),0)
                         <> ( StrToCurrDef( Line.GetAttribute('QM7_AMT'),0)
                             +StrToCurrDef( Line.GetAttribute('QM8_AMT'),0)
                             +StrToCurrDef( Line.GetAttribute('QM9_AMT'),0)))
                      then xTextError := ' ';
                    if ( StrToCurrDef( Line.GetAttribute('Q4_AMT'),0)
                         <> ( StrToCurrDef( Line.GetAttribute('QM10_AMT'),0)
                             +StrToCurrDef( Line.GetAttribute('QM11_AMT'),0)
                             +StrToCurrDef( Line.GetAttribute('QM12_AMT'),0)))
                      then xTextError := ' ';
                    if (LENGTH( xTextError) > 0)
                      then WriteError(
                                  FORMAT( 'В документе №%s от %s квартальная сумма '
                                         +'не соответствует сумме по месяцам.',
                                          [ Row.GetAttribute('DOC_NUMBER'),
                                            Row.getAttribute('DOC_DATE')]));
                  end;
      
              end;
              except
                ExceptionLog( 'Сбой проверки полей источника с разбивкой кассового плана по расходам «Помесячно».');
            end;
          end;//for//
        ProgressBar.Position := ProgressBar.Position + 1;
        Form.SimpleText := Format('Проверка данных в документах %d из %d', [ProgressBar.Position, ProgressBar.Max]);
        Application.ProcessMessages;
      end;//for//
      ProgressBar.Position := 0;
      Form.SimpleText := '';
      except
        ExceptionProc( 'checkDoc');
    end;

end; {End checkDoc}
{-----------------------------------------------------------------------------}
procedure DocProcess(Doc: Variant; Action: String);
var
  Xml, Exc: Variant;
  s: string;
begin

    try
      Xml := CreateOleObject('Microsoft.XmlDom');
      Xml := Xml.appendChild(Xml.createElement('DOCUMENT'));
      Xml.setAttribute('action', Action);
      Xml.setAttribute('class', Doc.getAttribute('DOCUMENTCLASS_ID'));
      Xml.setAttribute('ID', Doc.getAttribute('ID'));
      // exchange #2390 ФТ3 пп.3
      // добавлен тег для задания finish_process чтобы распознать адресата процесса
      if (Action = 'finish_process') then begin
        Xml.setAttribute('FROM', 'AZK_Exchange');
        // exchange #2555
        // передаем дату утверждения еще раз из формы.
        // если этого не делать, дата будет меняться по логике АЦК-Ф
        Xml.setAttribute( 'CARRY_DATE',
                          FormatDateTime( 'yyyy-mm-dd',
                                          dtDateConfirmation.Date));
      end;
      try
    
        ExecXml(Xml.xml);
      except
        Exc := CreateOleObject('Microsoft.XmlDom');
        Exc.LoadXml(LastException.Message);
        s := StringReplace(Exc.getElementsByTagName('MESSAGE').item[0].
          firstChild.data, #10, #13#10);
        WriteError(s);
      end;
      except
        ExceptionProc( 'DocProcess');
    end;

end; {End DocProcess}
{-----------------------------------------------------------------------------}
procedure DivideByRecipient(Row: Variant);
var
  i, disp, idx: Integer;
  Md5Str: String;
  List: TStrings;
  Line: Variant;
begin

    try
      DetectDocumentClass(Row);
      if (Row.getAttribute('DOCUMENTCLASS_ID') = 160) or
        (chkCombined.Checked and (Row.getAttribute('PLANASSDOCSUM_ID') <> '') and
        (DocumentClass_Id in [127, 128])) then
        Exit;
      i := 0;
      disp := Row.parentNode.childNodes.length - 1;
      List := TStringList.Create;
      try
        while i <> Row.childNodes.length do
        begin
          Line := Row.childNodes.items[i];
          Md5Str := Line.getAttribute('RECIPIENT_ID');
          Md5Str := MD5String(Md5Str);
          if List.Count = 0 then
            List.Add(Md5Str);
          idx := List.IndexOf(Md5Str);
          if idx = 0 then
            Inc(i)
          else
          begin
            if idx = -1 then
            begin
              idx := List.Add(Md5Str);
              Row.parentNode.appendChild(Row.cloneNode(False));
            end;
            Row.parentNode.childNodes.items[disp + idx].appendChild(Line.cloneNode(True));
            Row.removeChild(Line);
          end;
        end;
      finally
        List.Free;
      end;
      except
        ExceptionProc( 'DivideByRecipient');
    end;

end;
{-----------------------------------------------------------------------------}
procedure DeleteEmpty(Doc: Variant);
var
  i: Integer;
  Lines, Row: Variant;
  function getCurrency(FieldName: String): Currency;
  begin
    result := StrToCurrDef(StringReplace(VarToStr(Row.getAttribute(FieldName)), ',', '.'), 0.00);
  end;
begin

    try
      i := 0;
      Lines := Doc.documentElement.childNodes.items[0].childNodes.items[0];
      while i < Lines.childNodes.length do begin
        Row := Lines.childNodes.items[i];
        if ( getCurrency('LIMIT_AMT1')
            +getCurrency('LIMIT_AMT2')
            +getCurrency('LIMIT_AMT3')
            +getCurrency('ASSIGN_AMT1')
            +getCurrency('ASSIGN_AMT2')
            +getCurrency('ASSIGN_AMT3') = 0)
          then Lines.removeChild(Row)
          else Inc(i);
      end;
      except
        ExceptionProc( 'DeleteEmpty');
    end;

end; {End DeleteEmpty}
{-----------------------------------------------------------------------------}
procedure setEstimateLevel(Xml: Variant);
var
  i: Integer;
  Nodes: Variant;
  value: String;

  procedure getEstimateLevel;
  var
    Xml: Variant;
  begin

      try
        Xml := QueryXml( ' Select Par.Id From Estimate E'
                        +' Join Estimate Par On Par.Id = E.Parent_Id'
                        +' Where E.Estkind_Id = 2'
                        +' And Par.Parent_Id Is Null'
                        +' And E.Id In (' + value + ')');
        value := IfThen( Xml.documentElement.childNodes.length > 0, '1', '2');

        except
          ExceptionProc( 'getEstimateLevel');
      end;

  end;

begin

    try
      Nodes := Xml.getElementsByTagName('LINE');
      if (Nodes.length = 0) or (VarToStr(Nodes.item[0].getAttribute('ESTIMATE_ID')) = '')
        then Exit;
      value := Nodes.item[0].getAttribute('ESTIMATE_ID');
      for i := 1 to Nodes.length - 1
        do value := value  + ',' + Nodes.item[i].getAttribute('ESTIMATE_ID');
      getEstimateLevel;
      Xml.setAttribute('ESTIMATE_LEVEL', value);
      for i := 0 to Nodes.length - 1
        do Nodes.item[i].setAttribute('ESTIMATE_LEVEL', value);
      except
        ExceptionProc( 'setEstimateLevel');
    end;

end;
{-----------------------------------------------------------------------------}
function IsExistsDoc(Doc: Variant): Boolean;
begin

    try
      Result := False;

      if VarToStr(Doc.getAttribute('FOREIGN_KEY')) = ''
        then Exit;

      Sql := 'Select Id From ' + IfThen(Doc.getAttribute('class') in [127, 128],
        'ExpAssignment', 'ExpCashPlan') + ' Where Foreign_Key = ? And not DispStatus_Id In (-1, 7)';
      Result := QueryField( Sql,
                            [ Doc.getAttribute('FOREIGN_KEY')],
                            'ID') <> Null;

      except
        ExceptionProc( 'IsExistsDoc');
    end;

end; {End IsExistsDoc}
{-----------------------------------------------------------------------------}
{~UpdateLimAss
  обновление полей лимитов и ассигнований в сводном УБН.
  aXmlDocPlan - исходный xml из планирования (сводный убн 160го класса)}
procedure UpdateLimAss(Doc, aXmlDocPlan: Variant);
var
  i: Integer;
  Xml: Variant;

  procedure UpdateAmount(Row: Variant);
  var
    FlagLim, FlagAss: Boolean;
    Xml: Variant;
    xXml_ROW_LINE: Variant;
    xA2Y, xA3Y: double;
    i2: integer;
    xXml_ROW_LINE_LINE: Variant;

    {~DoEquField_
      сравнение атрибута aFieldName строки xXml_ROW_LINE_LINE и Row }
    function DoEquField_( aFieldName: string): boolean;
    begin
      Result := GetNodeAttributeEx( xXml_ROW_LINE_LINE, aFieldName)
                = GetNodeAttributeEx( Row, aFieldName);
    end;

  begin

      try
        Xml := QueryXml('Select E.Bring_Ass_Flag, E.Bring_Lim_Flag' +
          ' From Estimate E Where E.Id = ?', [Row.getAttribute('ESTIMATE_ID')]);
        Xml := Xml.documentElement.childNodes.items[0];
        FlagAss := Xml.getAttribute('BRING_ASS_FLAG') = 1;
        FlagLim := Xml.getAttribute('BRING_LIM_FLAG') = 1;
        if FlagAss and (StrToCurrDef(StringReplace(Row.getAttribute('LIMIT_AMT1'), ',', '.'), 0.00) +
          StrToCurrDef(StringReplace(Row.getAttribute('LIMIT_AMT2'), ',', '.'), 0.00) +
          StrToCurrDef(StringReplace(Row.getAttribute('LIMIT_AMT3'), ',', '.'), 0.00) <> 0) and
          (StrToCurrDef(StringReplace(Row.getAttribute('ASSIGN_AMT1'), ',', '.'), 0.00) +
          StrToCurrDef(StringReplace(Row.getAttribute('ASSIGN_AMT2'), ',', '.'), 0.00) +
          StrToCurrDef(StringReplace(Row.getAttribute('ASSIGN_AMT3'), ',', '.'), 0.00) = 0) then
        begin
          Row.setAttribute('ASSIGN_AMT1', Row.getAttribute('LIMIT_AMT1'));
          Row.setAttribute('ASSIGN_AMT2', Row.getAttribute('LIMIT_AMT2'));
          Row.setAttribute('ASSIGN_AMT3', Row.getAttribute('LIMIT_AMT3'));
        end;
        if FlagLim and not chkAssingOnly.Checked and (StrToCurrDef(StringReplace(Row.getAttribute('ASSIGN_AMT1'), ',', '.'), 0.00) +
          StrToCurrDef(StringReplace(Row.getAttribute('ASSIGN_AMT2'), ',', '.'), 0.00) +
          StrToCurrDef(StringReplace(Row.getAttribute('ASSIGN_AMT3'), ',', '.'), 0.00) <> 0) and
          (StrToCurrDef(StringReplace(Row.getAttribute('LIMIT_AMT1'), ',', '.'), 0.00) +
          StrToCurrDef(StringReplace(Row.getAttribute('LIMIT_AMT2'), ',', '.'), 0.00) +
          StrToCurrDef(StringReplace(Row.getAttribute('LIMIT_AMT3'), ',', '.'), 0.00) = 0) then
        begin
          Row.setAttribute('LIMIT_AMT1', Row.getAttribute('ASSIGN_AMT1'));
          Row.setAttribute('LIMIT_AMT2', Row.getAttribute('ASSIGN_AMT2'));
          Row.setAttribute('LIMIT_AMT3', Row.getAttribute('ASSIGN_AMT3'));
        end;


        { exchange #2859
          при формировании документа "Уведомление о бюджетных назначениях"
          на основании сводной бюджетной заявки из АЦК-Планирование (фактически
          речь идет о сводных УБН)
          в режиме "Принимать суммы ЛБО только текущего года",
          если у вышестоящего БР (с типом роспись) - для УБС это проверка в БР
          (это уже и есть вышестоящий для входящих УБН в сводный).
          стоит признак "Разрешать доведение ассигнований"
          и поля "Ассигнования текущего +1" и "Ассигнования текущего +2"
          формируемого ЭД после генерации значений равны нулю (пустые) ,
          то брать значения из полей AMOUNT2YEAR , AMOUNT3YEAR
          загружаемой бюджетной заявки.
          по новому формату сводный УБН (aXmlDocPlan) выглядит так:
	<ROW DOCUMENTCLASS_ID="160" ...>
		<LINE DOCUMENTCLASS_ID="8" ID="965000024371"/>
		<LINE DOCUMENTCLASS_ID="8" ID="965000024372"/>
		<ROW_LINE>
			<LINE
        AMOUNT2YEAR="608.00"
        AMOUNT3YEAR="809.00"
        EST_CAPTION="Управление финансов"
        FSR_ID="1"
        INN="..."
        KADMR_CODE="061"
        KCSR_CODE="0530000"
        KDE_CODE="000"
        KDF_CODE="000"
        KDR_CODE="007"
        KESR_CODE="251"
        KFSR_CODE="0906"
        KPP="520801001"
        KVR_CODE="530"
        PL_DIRECTION_ID="..."
        PL_DIRECTION_NAME="..."
        PL_DIRECTION_ORG_NAME="..."
        PURPOSEFULGRANT_ID="965000001134"
        Q1_AMT="100.00" Q2_AMT="101.00" Q3_AMT="102.00" Q4_AMT="103.00"
        RECIPIENT_CAPTION="...финансов"
        RECIPIENT_ID="962000000001"
        YEAR_AMT="406.00"/>
			<LINE ... />
		</ROW_LINE>
	</ROW>
      в АЦК-Ф строка в сводном убн:
      <LINE
        ID="1000112813" BUDGET_ID="1000000166"
        ESTIMATE_ID="2000004039"
        AMOUNT="0"
        RECIPIENT_ID="1000000052"
        DIRECTION_ID=""
        ASSIGN_AMT1="8100"
        ASSIGN_AMT2="0"
        ASSIGN_AMT3="0"
        LIMIT_AMT1="8100"
        LIMIT_AMT2="0"
        LIMIT_AMT3="0"
        FUL_ASSIGN_AMT="8100"
        FUL_LIMIT_AMT="8100"
        LAWACT_ID=""
        Q1_AMT="2025" Q2_AMT="2025" Q3_AMT="2025" Q4_AMT="2025"
        BRING_LIM_ASS="3"
        MEMGROUP_CODE=""
        LINEGROUP_CODE=""
        ESTIMATE_CAPTION="..."
        RECIPIENT_CAPTION="..."
        DIRECTION_DATA=""
        KFSR_CODE="0702"
        KCSR_CODE="0130259"
        KVR_CODE="244"
        KESR_CODE="223"
        KADMR_CODE="002"
        KDF_CODE="000"
        KDE_CODE="003"
        KDR_CODE="000"
        PURPOSEFULGRANT_CODE="0"
        PURPOSEFULGRANT_ID="2000002676"
        FSR_ID="1"/>}

        if cbSummaLBOcurrYear.Checked then
          // проверяем: если у вышестоящего БР (с типом роспись)
          // стоит признак "Разрешать доведение ассигнований"
          // вышестоящий - это для УБН, а для сводного уже в АЦК-К подставляется
          // вышестоящий, относительно УБН, поэтому для сводного УБН
          // проверяем флажки БР в сводном УБН.
          if (QueryField( ' Select EP.Bring_Ass_Flag X From Estimate EP'
                         +' Where EP.Id =? and EP.ESTKIND_ID=2',
                          [Row.getAttribute('ESTIMATE_ID')],
                          'X') = '1') then
            if (Format( '%.2f',
                       [ StrToCurrDef(
                            StringReplace(Row.getAttribute('ASSIGN_AMT2'), ',', '.'),
                            0.00)
                        +StrToCurrDef(
                            StringReplace(Row.getAttribute('ASSIGN_AMT3'), ',', '.'),
                            0.00)]) = '0.00') then begin

              // ищем тег ROW_LINE в сводном убн
              xXml_ROW_LINE := NULL;
              for i2 := 0 to aXmlDocPlan.childNodes.Length - 1 do
                if (aXmlDocPlan.childNodes.Items[ i2].NodeName = 'ROW_LINE')
                  then begin
                    xXml_ROW_LINE := aXmlDocPlan.childNodes.Items[ i2];
                    BREAK;
                  end;
              if NOT VarIsNULL( xXml_ROW_LINE) then
                if (xXml_ROW_LINE.childNodes.Length > 0) then begin
                  // получаем суммы 2го и 3го года для всех строк LINE в ROW_LINE
                  // у которых кбк совпадает со строкой Row
                  xA2Y := 0;
                  xA3Y := 0;
                  for i2 := 0 to xXml_ROW_LINE.childNodes.Length - 1 do begin
                    xXml_ROW_LINE_LINE := xXml_ROW_LINE.childNodes.items[ i2];
  
                    if (    DoEquField_( 'RECIPIENT_ID')
                        AND DoEquField_( 'FSR_ID')
                        AND DoEquField_( 'KADMR_CODE')
                        AND DoEquField_( 'KCSR_CODE')
                        AND DoEquField_( 'KDE_CODE')
                        AND DoEquField_( 'KDF_CODE')
                        AND DoEquField_( 'KDR_CODE')
                        AND DoEquField_( 'KESR_CODE')
                        AND DoEquField_( 'KFSR_CODE')
                        AND DoEquField_( 'KVR_CODE')
                        AND DoEquField_( 'PL_DIRECTION_ID')
                        AND DoEquField_( 'PURPOSEFULGRANT_ID')) then begin
  
                      xA2Y := xA2Y + StrToCurrDef(
                                          StringReplace(
                                              xXml_ROW_LINE_LINE.getAttribute('AMOUNT2YEAR'), ',', '.'),
                                          0.00);
                      xA3Y := xA3Y + StrToCurrDef(
                                          StringReplace(
                                              xXml_ROW_LINE_LINE.getAttribute('AMOUNT3YEAR'), ',', '.'),
                                          0.00);
                    end;
                  end;//for//
                  Row.setAttribute('ASSIGN_AMT2', Format( '%.2f', [ xA2Y]));
                  Row.setAttribute('ASSIGN_AMT3', Format( '%.2f', [ xA3Y]));
                end;
  {
                Row.setAttribute('ASSIGN_AMT2', Row.getAttribute('AMOUNT2YEAR '));
                Row.setAttribute('ASSIGN_AMT3', Row.getAttribute('AMOUNT3YEAR '));
  }
              end;
        except
          ExceptionProc( 'UpdateAmount');
      end;

  end;

begin

    try
      Xml := CreateOleObject('Microsoft.XmlDom');
      Xml := Xml.appendChild(Xml.createElement('DOCUMENT'));
      Xml.setAttribute('action', 'read');
      Xml.setAttribute('class', Doc.getAttribute('DOCUMENTCLASS_ID'));
      Xml.setAttribute('ID', Doc.getAttribute('ID'));
      Xml.ownerDocument.loadXml(ExecXml(Xml.xml));
      Xml := Xml.ownerDocument.documentElement;
      if Xml.GetElementsByTagName('LINES').length > 0 then begin
        Xml := Xml.GetElementsByTagName('LINES').items[0].GetElementsByTagName('EXPASSIGNMENT');
        for i := 0 to Xml.Length - 1
          do UpdateAmount(Xml.items[i]);
        Xml := Xml.items[0].ownerDocument
      end else begin
        UpdateAmount(Xml);
        Xml := Xml.ownerDocument;
      end;
      Xml.documentElement.setAttribute('action', 'update');
      Xml.documentElement.setAttribute('class', DocumentClass_Id);

      ExecXml(Xml.xml);
      except
        ExceptionProc( 'UpdateLimAss');
    end;

end; {End UpdateLimAss}
{-----------------------------------------------------------------------------}
{~DetectDocumentClassEx
  exchange #2390
  в рамках этой задачи функция создана из DetectDocumentClass так, чтобы
  номер формируемого класса возвращался в результате функции, а не в переменной
  DocumentClass_Id. это дает возможность использовать эту функцию при проверке
  документа (см.CheckAPPROVE_BUDGET_DATE_By_PLANASSDOCSUM_ID)}
function DetectDocumentClassEx( aDOCUMENTCLASS_ID: string;
                                aPLANINDOCTYPE_ID: string): integer;
begin

    try
      case StrToIntDef( aDOCUMENTCLASS_ID, 0) of
        8, 9, 121, 122:
          if cmbClass.itemIndex = 0
            then Result := 127
            else Result := 133;
        163, 164:
          if cmbClass.itemIndex = 0
            then Result := 128
            else Result := 134;
        160:
          if cmbClass.itemIndex = 1
            then Result := 0
            else
              case StrToIntDef( aPLANINDOCTYPE_ID, 0) of
                4: Result := 127;
                5: Result := 128;
              // здесь непонятное: добавлено, поскольку непонятно, бывает такой
              // случай или нет. в старом коде такого небыло!
              else raise('Не допустимое значание атрибута PLANINDOCTYPE_ID: '
                                            + aPLANINDOCTYPE_ID);
              end;//case//
        161: Result := 127;
      else raise('Не допустимый класс документа: ' + aDOCUMENTCLASS_ID);
      end;//case//
      // exchange #2395
      // при создании кассового плана (134й класс) допускается разбивка по месяцам
      // и кварталам, а для класса 133 остается все по старому.
      // exchange #2799
      // теперь допускается загружать ЭД 133го класса с помесячной разбивкой
    {
      if (Result in [133]) and not chkQuart.checked
        then raise('При создании кассового плана (133, 134) для 133-го класса необходима поквартальная разбивка');
    }

      except
        ExceptionProc( 'DetectDocumentClassEx');
    end;

end; {End DetectDocumentClass}
{-----------------------------------------------------------------------------}
function DetectDocumentClassExRow( aRow: Variant): integer;
begin

    try
      Result := DetectDocumentClassEx(
                            VarToStr( aRow.getAttribute('DOCUMENTCLASS_ID')),
                            VarToStr( aRow.getAttribute('PLANINDOCTYPE_ID')));
      except
        ExceptionProc( 'DetectDocumentClassExRow');
    end;

end;
{-----------------------------------------------------------------------------}
procedure DetectDocumentClass(Row: Variant);
begin

    try
      DocumentClass_Id := DetectDocumentClassExRow( Row);

      except
        ExceptionProc( 'DetectDocumentClass');
    end;

{
  case Row.getAttribute('DOCUMENTCLASS_ID') of
    8, 9, 121, 122:
      if cmbClass.itemIndex = 0 then
        DocumentClass_Id := 127
      else
        DocumentClass_Id := 133;
    163, 164:
      if cmbClass.itemIndex = 0 then
        DocumentClass_Id := 128
      else
        DocumentClass_Id := 134;
    160:
      if cmbClass.itemIndex = 1 then
        DocumentClass_Id := 0
      else
        case Row.getAttribute('PLANINDOCTYPE_ID') of
          4: DocumentClass_Id := 127;
          5: DocumentClass_Id := 128;
        end;
    161: DocumentClass_Id := 127;
    else
      raise('Не допустимый класс документа: ' + Row.getAttribute('DOCUMENTCLASS_ID'));
  end;
  if (DocumentClass_Id in [133, 134]) and not chkQuart.checked then
    raise('При создании кассового плана (133, 134) - необходима поквартальная разбивка');
    }
end; {End DetectDocumentClass}
{-----------------------------------------------------------------------------}
{~ImportXmlFile
  нужно удалять сводный УБН перед началом импорта того же файла (повторный импорт).
  forenkey убн состоит из полей: DOCUMENTCLASS_ID +ID. поэтому если такой файл
уже импортировался и УБН + сводный создавались, то записи с такими foregnkey
уже есть в БД и они будут найдены именно в бд, а не добавлены новые из файла.
  в результате создания сводных УБН, УБН входящие в сводный переводятся на
статус "обработка ГРБС", а для создания УБН, вложенные УБН должны быть на
статусе "на утверждении ГРБС".}
procedure ImportXmlFile(Xml: Variant);
var
  i, j{, i2}: Integer;
  Doc, Error: Variant;
//  xNode: Variant;
  xXmlDocClass: string;
  xXmlTemp: Variant;
  xXmlDocPlan: Variant;
  xXmlDocExport: Variant;
{  xXmlNew: Variant;
  xXmlDocNew: Variant;
  xDocNode: Variant;  }
begin

    try
      check_autofill_direction(Budget_Id);
      Sql := 'Select Period From Budget Where Id = ?';
      Period := StrToIntDef(VarToStr(QueryField(sql, [Budget_Id], 'PERIOD')), 0);

      if chkGroup.checked then begin
        { exchange #2395
          при объединении документов может возникнуть ситуация, что в первичных
          ЭД, не все поля могут быть указаны. это касается случая, когда из планирования
          выгружают ЭД с разбивкой по кварталам, а импортируют в финансы с разбивкой
          по месяцам. тогда при объединении строк, отсутствующие атрибуты будут
          заполняться нулевыми значениями, и в результате сформируются заполненные
          строки с нулевыми месячными суммами.
          поэтому перед объединением проверяем исходные ЭД и в случае ошибок
          прерываем импорт.}
        checkDoc(Xml);
        if FileExists(FileError) then EXIT;
        GroupDoc(Xml);
      end;
      ProgressBar.Max := Xml.childNodes.length;
      { здесь выполняется группировка УБН (но не сводный - 160й класс).
      в результате формируется новый список ЭД.}
      for i := 0 to Xml.childNodes.length - 1 do
      begin
        if Xml.childNodes.items[i].getAttribute('DOCUMENTCLASS_ID') <> 160 then
          GroupLine(Xml.childNodes.items[i]);
        ProgressBar.Position := i + 1;
        Form.SimpleText := Format('Группировка строк в документах %d из %d',
          [ProgressBar.Position, ProgressBar.Max]);
        Application.ProcessMessages;
      end;
      j := Xml.childNodes.length - 1;
    
      if chkGroup.checked and chkGroupRec.checked then
        for i := 0 to j do
          DivideByRecipient(Xml.childNodes.items[i]);
      ProgressBar.Position := 0;
      Form.SimpleText := '';
      checkDoc(Xml);
      if FileExists(FileError) then
      begin
    //    WriteError('Импорт прекращён в результате проверки, исправте ошибки');
    //    ShellExecute('NotePad', FileError, False);
        Exit;
      end;


      { здесь выполняется последовательный перебор сгруппированных ЭД из файла.
      ид.ЭД еще из планирования.
        для сводных УБН, в начале идут все УБН, входящие в сводный. они добавляются
      в первую очередь. в конце идет сводный УБН. поэтому здесь нет алгоритмя
      как то выделять сводный УБН.
        сводный УБН (160й класс) обрабатывается только при наличие галочки "сводные
      документы".}
      for i := 0 to Xml.childNodes.length - 1 do begin
        xXmlDocPlan := Xml.childNodes.items[i];
    
        Doc := Null;
        DetectDocumentClass( xXmlDocPlan);

    

        xXmlDocClass := xXmlDocPlan.getAttribute('DOCUMENTCLASS_ID');
        if (xXmlDocClass = '160') then begin
    
          if chkCombined.Checked and (DocumentClass_Id in [127, 128])
            then Doc := FillDoc( xXmlDocPlan)
        end else begin
    
          Doc := SplitDoc( xXmlDocPlan);
    
          if chkDeleteEmpty.Checked then DeleteEmpty(Doc);
    
          if Doc.documentElement.childNodes.items[0].childNodes.items[0].childNodes.length = 0
            then Doc := Null;
        end;

    

        if VarIsNull(Doc) then CONTINUE;
                 (*
        // после SplitDoc, один убн может быть разделен на несколько.
        // в результате в Doc может быть несколько разделов DOCUMENT.
        // поэтому здесь обрабатываем каждый узел в ROOT.
        xXmlNew := CreateOleObject('Microsoft.XmlDom');
        xXmlNew.LoadXml( Doc.Xml);
        xXmlDocNew := xXmlNew.DocumentElement;

        for i2 := 0 to xXmlDocNew.childNodes.length - 1 do begin
          Doc := CreateOleObject('Microsoft.XmlDom');
          xDocNode := Doc.appendChild( Doc.createElement('ROOT'));
          xDocNode.appendChild( xXmlDocNew.childNodes.items[ i2].CloneNode( TRUE));
    
          Doc.loadXml(FormatXmlData(Doc.xml));
    

    *)
          //Doc.loadXml(FormatXmlData(Doc.xml));

          ProgressBar.Max := ProgressBar.Max + Doc.documentElement.childNodes.length - 1;
          for j := 0 to Doc.documentElement.childNodes.length - 1 do begin
            // exchange #2891
            // храним ссылку на текущий обрабатываемый эд, вместо обращения
            // к элементу списка.
            xXmlDocExport := Doc.documentElement.childNodes.items[j];
    
            // exchange #2891
            // немного доработана проверка условий: ошибка и повторная загрузка эд.
            // это дает возможность добавить сообщение в журнал загрузки,
            // если эд найден в бд.
            if (VarToStr( xXmlDocExport.getAttribute('ERROR')) = '') then
              if IsExistsDoc( xXmlDocExport)
                then WriteInfo(
                          FORMAT( 'В БД найден ЭД класса %s c № %s на дату %s - не будет создан в АЦК-Ф.',
                                  [ VarToStr( xXmlDocExport.GetAttribute( 'class')),
                                    VarToStr( xXmlDocExport.GetAttribute( 'DOC_NUMBER')),
                                    VarToStr( xXmlDocExport.GetAttribute( 'DOC_DATE'))]))
                else begin
                  xXmlTemp := CreateOleObject('Microsoft.XmlDom');// do begin
                  try
                    setEstimateLevel( xXmlDocExport);//Doc.documentElement.childNodes.items[j]);

                    xXmlTemp.loadXml( ExecXml( xXmlDocExport.Xml));
//                                Doc.documentElement.childNodes.items[j].xml));

                    // exchange #2859
                    // доработка обновления сводных эд после SplitDoc.
                    if (xXmlDocClass <> '160')
                      then DoUpdateForegnkeys( xXmlTemp.documentElement,
                                               xXmlDocExport,
                                               Xml);

                    if autofill_direction
                      then set_direction(
                                xXmlTemp.documentElement.getAttribute('ID'),
                                xXmlTemp.documentElement.getAttribute('DOCUMENTCLASS_ID'),
                                True);

  
  //                  if (Xml.childNodes.items[i].getAttribute('DOCUMENTCLASS_ID') = 160)
                    if (xXmlDocClass = '160')
                      then UpdateLimAss( xXmlTemp.documentElement,
                                          xXmlDocPlan);

  
                    if chkCombined.Checked
                        and (VarToStr(xXmlDocExport{Doc.documentElement.childNodes.items[j]}.getAttribute('PLANASSDOCSUM_ID')) <> '')
                      then begin
                        DocProcess(xXmlTemp.documentElement, 'process');
                        DocProcess(xXmlTemp.documentElement, 'control');
                      end else
                        if chkToStatus10.Checked
                            and (xXmlTemp.documentElement.getAttribute('DOCUMENTCLASS_ID') in [127, 128])
                          then DocProcess(xXmlTemp.documentElement, 'finish_process');
  
                  except
  
                    Error := CreateOleObject('Microsoft.XmlDom');
                    Error.LoadXml(LastException.Message);
                    WriteError(StringReplace(Error.getElementsByTagName('MESSAGE').item[0].
                    firstChild.data, #10, #13#10));
                  end;
                end;

            ProgressBar.Position := ProgressBar.Position + 1;
            Form.SimpleText := Format('Вставка документов %d из %d', [ProgressBar.Position, ProgressBar.Max]);
            Application.ProcessMessages;
    
          end;//for//
       // end;//for//
    
      end;//for//

      except
        ExceptionProc( 'ImportXmlFile');
    end;

end; {End ImportXmlFile}
{-----------------------------------------------------------------------------}
procedure actExecuteExecute(Sender: TObject);
begin

    try
      WriteLog('    ====== НАЧАЛО ИМПОРТА ======');
      if FileExists(FileInfo) then DeleteFile(FileInfo);
      if FileExists(FileError) then DeleteFile(FileError);
      SetAppDisable;
      btnExecute.Action := Form.actCancel;
      ListInfoError.Clear;
      try
        QueryInitialization;
        try
          if Trim(edtFile.Text) = '' then
            raise('Не указана файл импорта');
          if not FileExists(edtFile.Text) then
            raise('Не найден файл импорта: ' + edtFile.Text);
          if chkGroup.checked and (rgNumber.ItemIndex = 1) then
            if MessageDlg('Установлена группировка документов и номера из АЦК-Планирование' + #13+#10 +
              ', будет произведена автонумерация, продолжть ?', mtConfirmation, mbYes + mbNo, 0) <> mrYes then
              Exit;
          if chkGroup.checked and chkDocDate.checked then
            if MessageDlg('Установлена группировка документов и дата документов из АЦК-Планирование' + #13+#10 +
              ', дата будет проставлятся по умолчанию, продолжть ?', mtConfirmation, mbYes + mbNo, 0) <> mrYes then
              Exit;
          if chkGroup.checked and chkNumber.checked then
            if MessageDlg('Установлена группировка документов и сохранять информацию в комментарии' + #13+#10 +
              ', информация в комментарии не будет сохраняться, продолжть ?', mtConfirmation, mbYes + mbNo, 0) <> mrYes then
              Exit;
          WriteLog('     === ПАРАМЕТРЫ  ИМПОРТА ===');
          WriteLog('Файл импорта: ' + edtFile.Text);
          WriteLog('Создаваемые документы: ' + cmbClass.Text);
          WriteLog('Дата создаваемых документов: ' + DateToStr(edtDate.Date));
          WriteLog('Нумерация документов: ' + rgNumber.Items[rgNumber.ItemIndex]);
          if chkNumber.Checked then
            WriteLog('Сохраненять документа в комментарий');
          WriteLog('Тип операции: ' + rgOper.Items[rgOper.ItemIndex]);
          if rgOper.ItemIndex = 0 then
          begin
            WriteLog('Тип операции для первичных документов: ' + cmbOper. Text);
            WriteLog('Тип операции для документов изменений: ' + cmbOperChange.Text);
          end;
          if chkGroup.Checked then
            WriteLog('Производится обьединение документов')
          else if chkDocDate.Checked then
            WriteLog('Дата документов из АЦК-Планирование');
          if chkQuart.Checked then
            WriteLog('Производится поквартальная разбивка сумм');
          if chkQuart.Checked and
            ((edtQuart1.Value + edtQuart2.Value + edtQuart3.Value + edtQuart4.Value) <> 100) then
            raise('Не верно указана процентное соотношение сумма в итоге должна равнятся 100%');
          Xml := CreateOleObject('Microsoft.XmlDom');
          if not Xml.load(edtFile.Text) then
            raise('Ошибка загрузки файла импорта: ' + edtFile.Text);
          Xml := Xml.documentElement;
          WriteLog('Проверка заголовочной части Xml');
          if VarIsNull(Xml.getAttribute('FILEGUID')) then
            raise('Отсутствует обязательный параметр: <FILEGUID>');
          WriteLog('FILEGUID = ' + Xml.getAttribute('FILEGUID'));
          if Xml.getAttribute('FILEGUID') <> FileGuid then
            raise('Не совпадает параметр <FILEGUID>, требуется: ' + FileGuid);
          if VarIsNull(Xml.getAttribute('FILEVER')) then
            raise('Отсутствует обязательный параметр: <FILEVER>');
          WriteLog('FILEVER = ' + Xml.getAttribute('FILEVER'));
          if Xml.getAttribute('FILEVER') <> FileVer then
            raise('Не совпадает параметр <FILEVER>, требуется: ' + FileVer);
          if VarIsNull(Xml.getAttribute('FINYEAR')) then
            raise('Отсутствует обязательный параметр: <FINYEAR>');
          WriteLog('FINYEAR = ' + Xml.getAttribute('FINYEAR'));
          if Xml.getAttribute('FINYEAR') <> FormatDateTime('yyyy', Work_Date) then
            raise('Не совпадает параметр <FINYEAR> , требуется: ' + FormatDateTime('yyyy', Work_Date));
    
          ImportXmlFile(Xml);
    
        except
          WriteLog('Критическая ошибка: ' + LastException.Message);
          raise(LastException.Message);
        end;
        if FileExists(FileInfo) then
        begin
          WriteInfo('Импорт завершён с предупреждениями');
          ShellExecute('NotePad', FileInfo, False);
        end
      finally
        QueryFinalization;
        Form.SimpleText := '';
        ProgressBar.Position := 0;
        SetAppEnable;
        btnExecute.Action := Form.actExecute;
        WriteLog('    ==== ЗАВЕРШЕНИЕ ИМПОРТА ====');
        if FileExists(FileError) then
          ShellExecute('NotePad', FileError, False);
      end;
      except
        ExceptionProc( 'actExecuteExecute');
    end;

end; {End actExecuteExecute}
{-----------------------------------------------------------------------------}
procedure spbFileOnClick(Sender: TObject);
begin

    try
      if openDialog.Execute then
        edtFile.Text := openDialog.FileName;
      except
        ExceptionProc( 'spbFileOnClick');
    end;

end; {End spbFileOnClick}
{-----------------------------------------------------------------------------}
procedure initialization(Xml: Variant);
var
  IniPath: String;
begin
  ListInfoError := TStringList.Create;
  IniPath := ExtractFilePath(Xml.documentElement.getAttribute('INI_FILE'));
  FileLog := IniPath + 'PlanAssDocImport.log';
  FileInfo := IniPath + 'PlanAssDocImport.inf';
  FileError := IniPath + 'PlanAssDocImport.err';
  WriteLog(' ====== ИНИЦИАЛИАЗЦИЯ ИМПОРТА ======');
  Form := TFormCustom.Create(Application);
  Form.LoadDfm(IniPath + 'Fm_PlanAssDocImport.dfm');
  Form.Name := 'FmPlanAssDocImport';
  TLabel(Form.FindComponent('lblVersion')).Caption := 'Версия формата: ' + FileVer;
  WriteLog(TLabel(Form.FindComponent('lblVersion')).Caption);
  edtDate := TDateTimeEh(Form.FindComponent('edtDate'));
  edtDate.Date := Work_Date;
  Timer := TTimer(Form.FindComponent('Timer'));
  Timer.OnTimer := @LoadOperType;
  cmbOper := TComboBox(Form.FindComponent('cmbOper'));
  cmbOperChange := TComboBox(Form.FindComponent('cmbOperChange'));
  cmbClass := TComboBox(Form.FindComponent('cmbClass'));
  cmbClass.OnChange := @LoadOperType;
  btnExecute := TButton(Form.FindComponent('btnExecute'));
  ProgressBar := TProgressBar(form.FindComponent('ProgressBar'));
  edtFile := TEdit(form.FindComponent('edtFile'));
  openDialog := TOpenDialog(form.FindComponent('openDialog'));
  TSpeedButton(form.FindComponent('spbFile')).OnClick := @spbFileOnClick;
  chkGroup := TCheckBox(Form.FindComponent('chkGroup'));
  chkGroupRec := TCheckBox(Form.FindComponent('chkGroupRec'));
  chkQuart := TCheckBox(Form.FindComponent('chkQuart'));
  chkNumber := TCheckBox(Form.FindComponent('chkNumber'));
  chkToStatus10 := TCheckBox(Form.FindComponent('chkToStatus10'));
  chkCombined := TCheckBox(Form.FindComponent('chkCombined'));
  chkDescription := TCheckBox(Form.FindComponent('chkDescription'));
  chkDeleteEmpty := TCheckBox(Form.FindComponent('chkDeleteEmpty'));
  chkDocDate := TCheckBox(Form.FindComponent('chkDocDate'));
  edtDescription := TMemo(Form.FindComponent('edtDescription'));
  edtQuart1 := TDBNumberEditEh(Form.FindComponent('edtQuart1'));
  edtQuart2 := TDBNumberEditEh(Form.FindComponent('edtQuart2'));
  edtQuart3 := TDBNumberEditEh(Form.FindComponent('edtQuart3'));
  edtQuart4 := TDBNumberEditEh(Form.FindComponent('edtQuart4'));
  rgOper := TRadioGroup(Form.FindComponent('rgOper'));
  rgNumber := TRadioGroup(Form.FindComponent('rgNumber'));
  chkAssingOnly := TCheckBox(Form.FindComponent('chkAssingOnly'));
  Form.actExecute.OnExecute := @actExecuteExecute;

  // exchange #2555 - дата утверждения
  // по умолчанию дату установить в значение рабочей даты
  dtDateConfirmation := TDateTimeEh(Form.FindComponent('dtDateConfirmation'));
  dtDateConfirmation.Date := Work_Date;

  // exchange #2777
  cbSummaLBOcurrYear:= TCheckBox(Form.FindComponent('cbSummaLBOcurrYear'));

  // exchange #2859
//  vListNewKey := TStringList.Create;
//  vListNewKeyID := TStringList.Create;

  // exchange
  InitializeQueryKBKFormat;
end; {End initialization}
{-----------------------------------------------------------------------------}
procedure finalization;
begin
  FinalizeQueryKBKFormat;
  ListInfoError.Free;
//  vListNewKeyID.Free;
//  vListNewKey.Free;
  Form.Free;
  WriteLog(' ======= ФИНАЛИЗАЦИЯ ИМПОРТА =======');
end; {End finalization}
{-----------------------------------------------------------------------------}
begin
  AzkLogined := AzkIsLogin;
  Debuged := True;
  Xml := CreateOleObject('Microsoft.XmlDom');
  if not FileExists(FileXmlName) then begin
    ShowMessage('Не найден тестовый пример: ' + FileXmlName);
    Exit;
  end;
  if not Xml.Load(FileXmlName) then begin
    ShowMessage('Ошибка при загрузке: ' + FileXmlName);
    Exit;
  end;
  if not AzkLogined then
    if not AzkLogin
      then Exit;
  try
    Main(Xml);
    finally
      if not AzkLogined
        then AzkLogout;
  end;
end.
