// изменение - exchange #2390

uses '..\Tools\Un_Query', '..\Version\CheckVersion';

var
  Form: TFormCustom;
  pbStatus: TProgressBar;
  dtmDate: TDateTimeEh;
  edtFileImport: TEdit;
  dlgFileImport: TOpenDialog;
  cbbAmountTypr: TComboBox;
  cbStatus10, chkDateFromPl, chkNumberAuto, chkDocDublCheck,
  chkTerritoryUses, chkGroupKvd: TCheckBox;
  btnExecute: TButton;
  FileLog, FileInfo, FieldAmt: string;
  ErrorList, List: TStrings;
  FAzkIsLogin, FDebuger, ErrorChecked: Boolean;
  classId, Period: integer;
  // exchange #2555 - дата утверждения
  dtDateConfirmation: TDateTimeEh;
  // exchange #2623 - дата утв. закона
  dteAPPROVE_BUDGET_DATE: TDateTimeEh;

const
  FFileVer = '1.0';
  FFileGuid = '{B9258167-CC39-4741-9135-85850E2E91DD}';

procedure Main(Xml: Variant);
begin
  CheckWorkAppVersion;
  with Xml.documentElement do
  if getAttribute('TESTED') = 'True' then
  begin
    setAttribute('TESTED', 'False');
    Xml.save('C:\Temp\PlanIncomImport.Xml');
    ShowMessage('Тестовый пример приготовлен: C:\Temp\PlanIncomImport.Xml');
    Exit;
  end;
  FAzkIsLogin := AzkIsLogin;
  if not AzkIsLogin then
    if not AzkLogin then
      Exit;
  try
    try
      initialization(Xml);
      if FDebuger 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 FAzkIsLogin then
      AzkLogout;
  end;
end; {End Main}

procedure initialization(Xml: Variant);
var
  i: Integer;
  IniPath: String;
begin
  IniPath := ExtractFilePath(Xml.documentElement.getAttribute('INI_FILE'));
  FileLog := IniPath + 'PlanIncomImport.Log';
  FileInfo := IniPath + 'PlanIncomImport.Inf';
  Form := TFormCustom.Create(Application);
  Form.LoadDfm(IniPath + 'Fm_PlanIncomImport.dfm');
  Form.Name := 'FmPlanIncomImport';
  WriteLog(' ====== ИНИЦИАЛИАЗЦИЯ ИМПОРТА ======');
  TLabel(Form.FindComponent('lblFormatVersion')).Caption := 'Версия формата: ' + FFileVer;
  WriteLog(TLabel(Form.FindComponent('lblFormatVersion')).Caption);
  pbStatus := TProgressBar(Form.FindComponent('pbStatus'));
  dtmDate := TDateTimeEh(Form.FindComponent('dtmDate'));
  dtmDate.Date := Date;
  edtFileImport := TEdit(Form.FindComponent('edtFileImport'));
  dlgFileImport := TOpenDialog(Form.FindComponent('dlgFileImport'));
  btnExecute := TButton(Form.FindComponent('btnExecute'));
  TSpeedButton(Form.FindComponent('btnFileImport')).OnClick := @btnFileImportOnClick;
  cbbAmountTypr := TComboBox(Form.FindComponent('cbbAmountTypr'));
  chkNumberAuto := TCheckBox(Form.FindComponent('chkNumberAuto'));
  chkDateFromPl := TCheckBox(Form.FindComponent('chkDateFromPl'));
  chkDocDublCheck := TCheckBox(Form.FindComponent('chkDocDublCheck'));
  chkTerritoryUses := TCheckBox(Form.FindComponent('chkTerritoryUses'));
  chkGroupKvd := TCheckBox(Form.FindComponent('chkGroupKvd'));
  cbStatus10 := TCheckBox(Form.FindComponent('cbStatus10'));
  Form.actExecute.OnExecute := @actExecuteExecute;
  Form.SimpleText := '';

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

  // exchange #2623 - дата утв. закона
  dteAPPROVE_BUDGET_DATE:= TDateTimeEh(Form.FindComponent('dteAPPROVE_BUDGET_DATE'));
  dteAPPROVE_BUDGET_DATE.Date := Date;
end; {End initialization}

procedure finalization;
begin
  WriteLog(' ======= ФИНАЛИЗАЦИЯ ИМПОРТА =======');
  Form.Free;
end; {End finalization}

procedure WriteLog(Message: String; SimpleText: Boolean = True);
begin
  if SimpleText then
    Form.SimpleText := Message;
  if FileLog <> '' then
    WriteLogFile(FileLog, Message);
end; {End WriteLog}

procedure WriteInfo(Message: String; Error: Boolean = False);
var
  str: String;
begin
  ErrorChecked := ErrorChecked or Error;
  str := MD5String(Message);
  if ErrorList.indexOf(str) < 0 then
  begin
    ErrorList.Add(str);
    if Error then
      WriteLogFile(FileInfo, 'Ошибка: ' + Message)
    else
      WriteLogFile(FileInfo, Message);
    WriteLogFile(FileLog, Message);
  end;
end; {End WriteInfo}

{ QuoteSubStr
   exchange #2623
   всвязи с переходрм на символьные БК, значения кодов теперь нужно
   указывать в кавычках}
function QuoteSubStr( aStr: string): string;
begin
  Result := #39 + aStr + #39;
end;


procedure btnFileImportOnClick(Sender: TObject);
begin
  if dlgFileImport.Execute then
    edtFileImport.Text := dlgFileImport.FileName;
end; {End btnFileImportOnClick}

procedure actExecuteExecute(Sender: TObject);
var
  Msg: String;
begin
  if FileExists(FileInfo) then DeleteFile(FileInfo);
  WriteLog('    ====== НАЧАЛО ИМПОРТА ======');
  SetAppDisable;
  ErrorList := TStringList.Create;
  List := TStringList.Create;
  QueryInitialization;
  try
    Period := StrToInt(VarToStrDef(QueryField(
      'Select Period From Budget Where Id = ?', [Budget_Id], 'PERIOD'), '1'));
    ErrorChecked := False;
    try
      if Trim(edtFileImport.Text) = '' then
        Raise('Не указана файл импорта');
      if not FileExists(edtFileImport.Text) then
        Raise('Не найден файл импорта: ' + edtFileImport.Text);
      WriteLog('     === ПАРАМЕТРЫ ИМПОРТА ===');
      WriteLog('Файл импорта: ' + edtFileImport.Text);
      WriteLog('Дата загружаемых документов: ' + FormatDateTime('yyyy-mm-dd', dtmDate.Date));
      if chkNumberAuto.Checked then
        WriteLog('Автонумерация документов')
      else
        WriteLog('Номера документов как в планировании');
      if chkDateFromPl.Checked then
        WriteLog('Дата документов из АЦК-Планирования')
      else
        WriteLog('Дата документов: ' + FormatDateTime('dd.mm.yyyy', dtmDate.Date));
      case cbbAmountTypr.itemIndex of
        0: FieldAmt := 'SUBJECT';
        1: FieldAmt := 'CITY';
        2: FieldAmt := 'MUNICIPAL';
        3: FieldAmt := 'COLONY';
      end;
      WriteLog('Сумма берётся из поля: ' + FieldAmt + ' - ' + cbbAmountTypr.Text);
      ExecuteProcess;
    except
      WriteLog(' !!! ' + LastException.Message + ' !!! ');
      raise(LastException.Message);
    end;
  finally
    List.Free;
    ErrorList.Free;
    QueryFinalization;
    pbStatus.Position := 0;
    SetAppEnable;
    if FileExists(FileInfo) then
      ShellExecute('NotePad', FileInfo, False);
    WriteLog(Msg);
    WriteLog('    ==== ЗАВЕРШЕНИЕ ИМПОРТА ====');
    Form.SimpleText := '';
  end;
end; {End actExecuteExecute}

procedure ExecuteProcess;
var
  i: Integer;
  Id: String;
  Xml, Doc: Variant;
begin
  Xml := CreateOleObject('Microsoft.XmlDom');
  WriteLog('Загрузка файла импорта: '  + edtFileImport.Text);
  if not Xml.load(edtFileImport.Text) then
    raise('Ошибка загрузки файла импорта: ' + edtFileImport.Text);
  WriteLog('Проверка заголовочной части ...');
  with Xml.documentElement do
  begin
    if VarIsNull(getAttribute('FILEGUID')) then
      raise('Отсутствует обязательный параметр: <FILEGUID>');
    if getAttribute('FILEGUID') <> FFileGuid then
      raise('Не совпадает параметр <FILEGUID>, требуется: ' + FFileGuid);
    if VarIsNull(getAttribute('FILEVER')) then
      raise('Отсутствует обязательный параметр: <FILEVER>');
    if getAttribute('FILEVER') <> FFileVer then
      raise('Не совпадает параметр <FILEVER>, требуется: ' + FFileVer);
    if VarIsNull(getAttribute('FINYEAR')) then
      raise('Отсутствует обязательный параметр: <FINYEAR>');
    if getAttribute('FINYEAR') <> FormatDateTime('yyyy', Work_Date) then
      raise('Не совпадает параметр <FINYEAR>, требуется: ' + FormatDateTime('yyyy', Work_Date));
    if VarIsNull(getAttribute('DOCUMENTCLASS')) then
      raise('Отсутствует обязательный параметр: <DOCUMENTCLASS>');
    if not (getAttribute('DOCUMENTCLASS') in ['112', '162']) then
      raise('Не совпадает параметр <DOCUMENTCLASS>, требуется: 112 или 162');
    WriteLog('Класс документов: ' + getAttribute('DOCUMENTCLASS'));
  end;
  Xml := ConvertPlToAzk(Xml);
  if ErrorChecked then
  begin
    WriteInfo('Импорт прекращён из за несответствий, исправте их и повторите импорт');
    Exit;
  end;

  // exchange #2933
  // чтобы пользоваться выражениями XPath нужно установить язык запросов у документа
  Xml.setProperty('SelectionLanguage','XPath');

  if chkGroupKvd.checked then
    Xml := GroupByKdCode(Xml);
  GroupByLine(Xml);
  Xml.LoadXml('<?xml version="1.0" encoding="Windows-1251"?>' +
        FormatXmlData(Xml.documentElement.xml));

  // exchange #2933
  // чтобы пользоваться выражениями XPath нужно установить язык запросов у документа
  Xml.setProperty('SelectionLanguage','XPath');

  Xml := Xml.selectNodes('//DOCUMENT');
  for i := 0 to Xml.Length - 1 do
  begin
    Doc := Xml.items[i];
    if chkDocDublCheck.Checked and (QueryField('Select Id From InDoc Where DocumentClass_Id = ?' +
      ' And DispStatus_Id <> -1 And Foreign_Key = ?',
      [Doc.getAttribute('class'), Doc.getAttribute('FOREIGN_KEY')], 'ID') <> Null) then
      WriteInfo('Документ был ранее загружен: ' + Trim(Doc.xml), False)
    else
    begin
      WriteLog('Документ к вставке: ' + Trim(Doc.xml), False);
      with CreateOleObject('Microsoft.XmlDom') do
      begin
        LoadXml(ExecXML(Doc.xml));
        WriteLog('Результат вставки документа: ' + Trim(documentElement.xml), False);
        Id := documentElement.getAttribute('ID');
        Doc.setAttribute('ID', Id);
        if cbStatus10.checked then
        try
          Doc.setAttribute('action', 'process');
          Doc.setAttribute('FROM', 'AZK_Exchange');
          ExecXML(Doc.xml);
          // exchange #2623
          // поменялась логика для ЭД 42го класса в версии АЦК-Ф v2.32
          // теперь вместо commit нужно вызывать process
          Doc.setAttribute('action', 'process');
          // exchange #2390 ФТ.5
          Doc.setAttribute('FROM', 'AZK_Exchange');
           // exchange #2555
           //  фиксировать дату утверждения датой из контрола "дата утверждения"}
           Doc.setAttribute( 'CARRY_DATE',
                              FormatDateTime( 'yyyy-mm-dd',
                                              dtDateConfirmation.Date));
          ExecXML(Doc.xml);

          // exchange #2420
          // перевод до 10-го статуса
          Doc.setAttribute('action', 'onsign');
          Doc.setAttribute('FROM', 'AZK_Exchange');
          ExecXML(Doc.xml);
          Doc.setAttribute('action', 'commit');
          Doc.setAttribute('FROM', 'AZK_Exchange');
           // exchange #2555
           //  фиксировать дату утверждения датой из контрола "дата утверждения"}
           Doc.setAttribute( 'CARRY_DATE',
                              FormatDateTime( 'yyyy-mm-dd',
                                              dtDateConfirmation.Date));
           // exchange #2623
           //  фиксировать дату утверждения датой из контрола
           // "дата утверждения закона"}
           Doc.setAttribute( 'APPROVE_BUDGET_DATE',
                              FormatDateTime( 'yyyy-mm-dd',
                                              dteAPPROVE_BUDGET_DATE.Date));
          ExecXML(Doc.xml);
        except
          LoadXml(LastException.Message);
          WriteInfo(documentElement.getElementsByTagName('MESSAGE').item[0].firstChild.data, True);
        end;
      end;
    end;
  end;
end; {End ExecuteProcess}

function ConvertPlToAzk(Xml: Variant): Variant;
var
  i, j, k: Integer;
  Rows, Row, Lines, Line, Payer, Receiver, Territory, Docs, Doc, DocLines,
  DocLine, Amount: Variant;
  function NodeByName(Node: Variant; nodeName: String): Variant;
  begin
    Result := Node.selectNodes(nodeName);
    if Result.Length = 0 then
      Result := Node.ownerDocument.createElement(nodeName)
    else
      Result := Result.items[0];
  end;
begin
  Result := CreateOleObject('Microsoft.XmlDom');
  Docs := Result.appendChild(Result.createElement('ROOT'));
  Rows := Xml.selectNodes('//ROOT/ROW');
  pbStatus.Max := Rows.Length;
  for i := 0 to Rows.Length - 1 do
  begin
    Form.SimpleText := Format('Конвертация документов в формат АЦК ... %d из %d',
      [pbStatus.Position + 1, Rows.length]);
    Row := Rows.items[i];
    Doc := Docs.appendChild(Result.createElement('DOCUMENT'));
    Doc.setAttribute('action', 'insert');
    case Xml.documentElement.getAttribute('DOCUMENTCLASS') of
      112: Doc.setAttribute('class', 42);
      162: begin
             Doc.setAttribute('class', 26);
             // exchange #3017
             // при импорте УиБН по доходам, импортируем поле
             // "Источник изменения БН"
             Doc.setAttribute( 'SOURCE_INDOC_ID',
                               VarToStr( Row.getAttribute('SOURCE_INDOC_ID')));
           end;
    end;
    if chkNumberAuto.Checked then
      Doc.setAttribute('num_gen_type', 'generate')
    else
      Doc.setAttribute('DOC_NUMBER', Row.getAttribute('DOC_NUMBER'));
    if chkDateFromPl.Checked then
      Doc.setAttribute('DOC_DATE', Row.getAttribute('DOC_DATE'))
    else
      Doc.setAttribute('DOC_DATE', FormatDateTime('yyyy-mm-dd', dtmDate.Date));
    Doc.setAttribute('OPERTYPE_ID', VarToStrDef(Row.getAttribute('OPERTYPE_ID'), '0'));
    Doc.setAttribute('KDT_CODE', VarToStrDef(Row.getAttribute('KDT_CODE'), '0'));
    if QueryField('Select Id From Kdt Where Code = ?', [Doc.getAttribute('KDT_CODE')], 'ID') = Null then
      WriteInfo('Не найден в АЦК код типа доходов: ' + Doc.getAttribute('KDT_CODE'), True);
    Doc.setAttribute('FOREIGN_KEY', 'PLI_' + Row.getAttribute('ID'));
    Doc.setAttribute('DESCRIPTION', Row.getAttribute('DESCRIPTION'));

    {exchange #2390
     При импорте ЭД УБН, УиБН по доходам из АЦК-Планирование через АЦК-Exchange
     поле «Дата утверждения» заполнять значением по умолчанию.
     exchange #2555
     фиксировать дату утверждения датой из контрола "дата утверждения"}
     Doc.setAttribute( 'CARRY_DATE',
                        FormatDateTime( 'yyyy-mm-dd',
                                        dtDateConfirmation.Date));
//    Doc.setAttribute('CARRY_DATE', '');

    DocLines := Doc.appendChild(Result.createElement('INCHANGE'));
    Lines := Row.selectNodes('LINE');
    for j := 0 to Lines.Length - 1 do
    begin
      Line := Lines.items[j];
      DocLine := DocLines.appendChild(Result.createElement('INCHANGE'));

      // исправление атрибута кода цели по доходам из PURPOSEFULGRANT_ID
      // в PURPOSEFULGRANT_ID. в данном случае это не требуется, поскольку
      // поле кода цели только одно.
      DocLine.setAttribute('PURPOSEFULGRANT_ID', VarToStr(Line.getAttribute('PURPOSEFULGRANT_ID')));
      if (VarToStr(DocLine.getAttribute('PURPOSEFULGRANT_ID')) <> '') and
        (QueryField('Select Id From PurposeFulGrant Where Id = ?',
          [VarToStr(DocLine.getAttribute('PURPOSEFULGRANT_ID'))], 'ID') = Null) then
        WriteInfo('В АЦК не найден код субсидии с референсом: ' +
          VarToStr(DocLine.getAttribute('PURPOSEFULGRANT_ID')), True);
      // Суммы
      for k := 1 to Period do
      begin
        Amount := NodeByName(Line, 'AMOUNT' + IntToStr(k) + 'YEAR');
        DocLine.setAttribute('Y' + IntToStr(k) + '_AMT', VarToStrDef(Amount.getAttribute(FieldAmt), '0'));
      end;
      // КБК
      DocLine.setAttribute('KDD_CODE', VarToStrDef(Line.getAttribute('KDD_CODE'), '0'));
      if QueryField('Select Code From Kdd Where Budget_Id = ? And Code = ?',
        [Budget_Id, DocLine.getAttribute('KDD_CODE')], 'CODE') = Null then
        WriteInfo('Не найден в АЦК дополнительный доходный код: ' + DocLine.getAttribute('KDD_CODE'), True);
      DocLine.setAttribute('KADMD_CODE', VarToStrDef(Line.getAttribute('KADMD_CODE'), '0'));
      if QueryField('Select Code From Kadm Where Budget_Id = ? And Code = ?',
        [Budget_Id, DocLine.getAttribute('KADMD_CODE')], 'CODE') = Null then
        WriteInfo('Не найден в АЦК классификатор администраторов поступлений и выбытий: ' +
          DocLine.getAttribute('KADMD_CODE'), True);
      DocLine.setAttribute('KD_CODE', VarToStrDef(Line.getAttribute('KD_CODE'), '0'));
      if QueryField('Select Code From Kd Where Budget_Id = ? And Code = ?',
        [Budget_Id, DocLine.getAttribute('KD_CODE')], 'CODE') = Null then
        WriteInfo('Не найден в АЦК классификатор видов доходов: ' + DocLine.getAttribute('KD_CODE'), True);
      DocLine.setAttribute('KESD_CODE', VarToStrDef(Line.getAttribute('KESD_CODE'), '0'));
      if QueryField('Select Code From Kes Where Budget_Id = ? And Code = ?',
        [Budget_Id, DocLine.getAttribute('KESD_CODE')], 'CODE') = Null then
        WriteInfo('Не найден в АЦК классификатор доходов: ' + DocLine.getAttribute('KESD_CODE'), True);
      DocLine.setAttribute('FSD_ID', VarToStrDef(Line.getAttribute('FSD_ID'), '0'));
      if QueryField('Select Id From FinSource Where Id = ?', [DocLine.getAttribute('FSD_ID')], 'ID') = Null then
        WriteInfo('Не найден в АЦК код источника финансирования: ' + DocLine.getAttribute('FSD_ID'), True);
      // Плательщик
      Payer := NodeByName(Line, 'PAYER');
      DocLine.setAttribute('PAYER_ID', VarToStrDef(Payer.getAttribute('ID'), '0'));
      VarValue := QueryField('Select Id, TaxCode From Org Where Id = ?',
        [DocLine.getAttribute('PAYER_ID')], 'ID');
      if VarValue = Null then
        WriteInfo('Плательщик не найден: (Референс: ' +
          DocLine.getAttribute('PAYER_ID') + ', Наименование: ' +
          VarToStrDef(Receiver.getAttribute('CAPTION'), '') + ')');
      // Получатель доходов
      Receiver := NodeByName(Line, 'RECEIVER');
      DocLine.setAttribute('RECEIVER_ID', VarToStrDef(Receiver.getAttribute('ID'), '0'));
      VarValue := QueryField('Select distinct R.Id From Receiver R' +
        ' Left Join Org O On (O.Id = R.Org_Id)' +
        ' Where R.Budget_Id = ? And R.Id = ?', [Budget_Id, DocLine.getAttribute('RECEIVER_ID')], 'ID');
      if VarValue = Null then
      begin
        VarValue := QueryField('Select distinct R.Id From Receiver R' +
          ' Left Join Budget B On (B.Org_Id = R.Org_Id)' +
          ' Where R.IsDefault = 1 and R.Budget_Id = ? And B.Id Is Not Null', [Budget_Id], 'ID');
        if VarValue = Null then
          WriteInfo('Получатель не найден: (Референс: ' +
            DocLine.getAttribute('RECEIVER_ID') + ', Наименование: ' +
            VarToStrDef(Receiver.getAttribute('CAPTION'), '') + ')' +
            ', не найден также получатель из финоргана')
        else
        begin
          WriteInfo('Получатель не найден: (Референс: ' +
            DocLine.getAttribute('RECEIVER_ID') + ', Наименование: ' +
            VarToStrDef(Receiver.getAttribute('CAPTION'), '') + ')' +
            ', заменён на финорган');
          DocLine.setAttribute('RECEIVER_ID', VarValue);
        end;
      end;
      // Территория
      Territory := NodeByName(Line, 'TERRITORY');
      DocLine.setAttribute('TERRITORY_ID', VarToStrDef(Territory.getAttribute('ID'), '0'));
      if chkTerritoryUses.Checked then
      begin
        VarValue := QueryField('Select Id From Territory Where Caption = ?',
          [Territory.getAttribute('CAPTION')], 'ID');
        if VarValue = Null then
          WriteInfo('Территория не не найдена: ' +
            VarToStrDef(Territory.getAttribute('CAPTION'), '') +
            ', заменена на Не указана');
        DocLine.setAttribute('TERRITORY_ID', VarToStrDef(VarValue, '0'));
      end;
    end;
    pbStatus.Position := pbStatus.Position + 1;
  end;
  pbStatus.Position := 0;
  Form.SimpleText := '';
end;

function GroupByKdCode(Xml: Variant): Variant;
var
  i, j: Integer;
  Docs, Doc, OldDoc, Lines, Line, Nodes, DocLines: Variant;
begin
  Result := CreateOleObject('Microsoft.XmlDom');
  Docs := Result.appendChild(Result.createElement('ROOT'));
  Lines := Xml.selectNodes('//DOCUMENT/INCHANGE/INCHANGE');
  i := 0;
  pbStatus.Max := Lines.Length;
  while i < Lines.Length do
  begin
    Form.SimpleText := Format('Группировка документов по строкам ... %d из %d',
      [pbStatus.Position + 1, pbStatus.Max]);
    Line := Lines.items[i];
    Nodes := Xml.selectNodes('//DOCUMENT[@KDT_CODE=' +
    // exchange #2623
    // всвязи с переходрм на символьные БК, значения кодов теперь нужно
    // указывать в кавычках
      QuoteSubStr( Line.parentNode.parentNode.getAttribute('KDT_CODE')) +
      ' and @OPERTYPE_ID=' + Line.parentNode.parentNode.getAttribute('OPERTYPE_ID') +
      ']/INCHANGE/INCHANGE[substring(@KD_CODE, 1, 1)=' +
      QuoteSubStr( Copy(Line.getAttribute('KD_CODE'), 1, 1)) + ']');
    if Nodes.Length = 1 then
    begin
      Inc(i);
      pbStatus.Position := pbStatus.Position + 1;
    end
    else
    begin
      Doc := Docs.appendChild(Result.createElement('DOCUMENT'));
      Doc.setAttribute('action', 'insert');
      OldDoc := Line.parentNode.parentNode;
      Doc.setAttribute('class', OldDoc.getAttribute('class'));
      Doc.setAttribute('num_gen_type', 'generate');
      Doc.setAttribute('DOC_DATE', FormatDateTime('yyyy-mm-dd', dtmDate.Date));
      Doc.setAttribute('OPERTYPE_ID', OldDoc.getAttribute('OPERTYPE_ID'));
      Doc.setAttribute('KDT_CODE', OldDoc.getAttribute('KDT_CODE'));
      DocLines := Doc.appendChild(Result.createElement('INCHANGE'));
      for j := 0 to Nodes.Length - 1 do
      begin
        DocLines.appendChild(Nodes.items[j].cloneNode(true));
        Nodes.items[j].parentNode.removeChild(Nodes.items[j]);
        Form.SimpleText := Format('Группировка документов по строкам ... %d из %d',
          [pbStatus.Position + 1, pbStatus.Max]);
        pbStatus.Position := pbStatus.Position + 1;
      end;
      Doc.setAttribute('FOREIGN_KEY', 'PLI_GEN_' + MD5String(Doc.xml));
    end;
    Lines := Xml.selectNodes('//DOCUMENT/INCHANGE/INCHANGE');
  end;
  Nodes := Xml.selectNodes('//DOCUMENT/INCHANGE');
  for i := 0 to Nodes.length - 1 do
    if Nodes.items[i].childNodes.Length = 0 then
      Xml.documentElement.removeChild(Nodes.items[i].parentNode);
  Nodes := Xml.selectNodes('//DOCUMENT');
  for i := 0 to Nodes.length - 1 do
    Docs.appendChild(Nodes.items[i].cloneNode(True));
  pbStatus.Position := 0;
  Form.SimpleText := '';
end; {End GroupByKdCode}

procedure GroupByLine(Xml: Variant);
var
  i, j, k, l: Integer;
  Amount: string;
  Docs, Doc, Lines, Line, Nodes: Variant;
begin
  Docs := Xml.selectNodes('//DOCUMENT');
  pbStatus.Max := Docs.Length;
  for i := 0 to Docs.Length - 1 do
  begin
    Form.SimpleText := Format('Группировка строк в документах ... %d из %d',
      [pbStatus.Position + 1, pbStatus.Max]);
    Doc := Docs.items[i];
    Lines := Doc.selectSingleNode('INCHANGE');
    for j := 0 to Lines.childNodes.Length - 1 do
    begin
      Line := Lines.childNodes.items[j];
      try
      // exchange #2623
      // всвязи с переходрм на символьные БК, значения кодов теперь нужно
      // указывать в кавычках
      Nodes := Lines.selectNodes('INCHANGE['
             +     '@KDD_CODE=' +QuoteSubStr( Line.getAttribute('KDD_CODE'))
             +' and @KADMD_CODE=' + QuoteSubStr( Line.getAttribute('KADMD_CODE'))
             +' and @KD_CODE=' + QuoteSubStr( Line.getAttribute('KD_CODE'))
             +' and @KESD_CODE=' + QuoteSubStr( Line.getAttribute('KESD_CODE'))
             +' and @FSD_ID=' + QuoteSubStr( Line.getAttribute('FSD_ID'))
             +' and @PAYER_ID=' + QuoteSubStr( Line.getAttribute('PAYER_ID'))
             +' and @RECEIVER_ID=' + QuoteSubStr( Line.getAttribute('RECEIVER_ID'))
             +' and @TERRITORY_ID=' + QuoteSubStr( Line.getAttribute('TERRITORY_ID'))
             // exchange #2871
             // в группировку добавлен код цели
             +' and @PURPOSEFULGRANT_ID=' + QuoteSubStr( Line.getAttribute('PURPOSEFULGRANT_ID'))
             + ']');
      except
        raise( 'Сбой GroupByLine: ' + LastException.Message);
      end;
      if Nodes.Length > 1 then
        for k := 1 to Nodes.Length - 1 do
        begin
          for l := 1 to Period do
          begin
            Amount := 'Y' + IntToStr(l) + '_AMT';
            Line.setAttribute(Amount, StrToCurr(VarToStrDef(Line.getAttribute(Amount), '0')) +
              StrToCurr(VarToStrDef(Nodes.items[k].getAttribute(Amount), '0')));
          end;
          Lines.removeChild(Nodes.items[k]);
        end;
      if j >= Lines.childNodes.Length - 1 then
        Break;
    end;
    pbStatus.Position := pbStatus.Position + 1;
  end;
  pbStatus.Position := 0;
  Form.SimpleText := '';
end; {End GroupByLine}

var
  Xml: Variant;

begin
  FAzkIsLogin := AzkIsLogin;
  FDebuger := True;
  Xml := CreateOleObject('Microsoft.XmlDom');
  if not FileExists('C:\Temp\PlanIncomImport.Xml') then
  begin
    ShowMessage('Не найден тестовый пример: C:\Temp\PlanIncomImport.Xml');
    Exit;
  end;
  if not Xml.Load('C:\Temp\PlanIncomImport.Xml') then
  begin
    ShowMessage('Ошибка при загрузке: C:\Temp\PlanIncomImport.Xml');
    Exit;
  end;
  if not AzkIsLogin then
    if not AzkLogin then //ExtractFilePath(ParamStr(0)) 'D:\azk2\AzkScript\AzkFinance\Azk2Clnt.ini'
      Exit;
  try
    Main(Xml);
  finally
    if not FAzkIsLogin then
      AzkLogout;
  end;
end. {End Start}