// Импорт плана (58) и изменений планов (27) по источникам из  АЦК-Планирование
// Класс документа в Планировании (158) в АЦК (58)
// Класс документа в Планировании (177) в АЦК (27)
// Постановка - AzkExchange#1070
// изменение - exchange #2390

uses
  '..\Tools\Un_Query',
  '..\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 = 'PlanSrcDocImport.xml';
  fileXmlNameTesting = 'C:\Temp\' + FileXmlNameOnly;
  fileVer = '1.0';
  fileGuid = '{7476702C-25B6-4BF1-A983-147CBCF506FC}';

var
  Xml: Variant;
  form: TFormCustom;
  fAzkLogined, fDebuged: Boolean;
  fileLog, fileInfo, fileError: String;
  btnExecute: TButton;
  prbMain: TProgressBar;
  dttDate: TDateTimeEh;
  edtFile: TEdit;
  opdFile: TOpenDialog;
  cbStatus10, chkDate, chkNumber: TCheckBox;
  classId, Period: Integer;
  ListInfoError: TStrings;
  // exchange #2555 - дата утверждения
  dtDateConfirmation: TDateTimeEh;

procedure Main(Xml: Variant);
begin
  // exchange #2390 добавлено для возможности отладки
  InitializeMain( Xml, FileXmlNameOnly, '');

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

procedure ToStatus10(Doc: Variant );
var
 Xml, Exc: Variant;
 s, _ID: string;
begin
try
 _ID := Doc.getElementsByTagName('DOCUMENT').items(0).getAttribute('ID');
 Xml := CreateOleObject('Microsoft.XmlDom');
 Xml := Xml.appendChild(Xml.createElement('DOCUMENT'));
 Xml.setAttribute( 'action', 'process' );
 Xml.setAttribute( 'class', classId );
 Xml.setAttribute( 'ID', _ID );
 // exchange #2390 ФТ.6
 Xml.setAttribute('FROM', 'AZK_Exchange');
 // exchange #2555
 //  фиксировать дату утверждения датой из контрола "дата утверждения"}
 Xml.setAttribute( 'CARRY_DATE',
                    FormatDateTime( 'yyyy-mm-dd',
                                    dtDateConfirmation.Date));
 try
   s:= '';
   ExecXml(Xml.xml);
 except
   Exc := CreateOleObject('Microsoft.XmlDom');
   Exc.LoadXml( LastException.Message );
   s := Exc.getElementsByTagName('MESSAGE').item[0].firstChild.data ;
   WriteError(s);
 end;
except
  raise( 'Сбой ToStatus10.1: ' + LastException.Message );
end;
 if s <> '' then Exit;
 
try
 Xml := CreateOleObject('Microsoft.XmlDom');
 Xml := Xml.appendChild(Xml.createElement('DOCUMENT'));
 Xml.setAttribute( 'action', 'commit' );
 Xml.setAttribute( 'class', classId );
 Xml.setAttribute( 'ID', _ID );
 // exchange #2390 ФТ.6
 Xml.setAttribute('FROM', 'AZK_Exchange');
 // exchange #2555
 //  фиксировать дату утверждения датой из контрола "дата утверждения"}
 Xml.setAttribute( 'CARRY_DATE',
                    FormatDateTime( 'yyyy-mm-dd',
                                    dtDateConfirmation.Date));
 try
   s:= '';
   ExecXml(Xml.xml);
 except
   Exc := CreateOleObject('Microsoft.XmlDom');
   Exc.LoadXml( LastException.Message );
   s := Exc.getElementsByTagName('MESSAGE').item[0].firstChild.data ;
   WriteError(s);
 end;
except
  raise( 'Сбой ToStatus10.2: ' + LastException.Message );
end;
end;


procedure importXmlFile(Xml: Variant);
var
  i, j, k, idx: Integer;
  str, foreign_key: String;
  Row, Line, Doc, Node: Variant;
  xRecID: string;
  xREC_KPP: string;
  xREC_NAME: string;
  xREC_ACCOUNT_ID: string;
  xXmlACC: Variant;
begin
  check_autofill_direction(Budget_Id);
  writeLog('Начало импорта xml');
  for i := 0 to Xml.childNodes.length - 1 do
  begin
    prbMain.Position := prbMain.Position + 1;
    form.SimpleText := Format( 'Импорт данных ... %d из %d',
                               [prbMain.Position, prbMain.Max]);
    if form.actCancel.Checked then
    begin
      form.actCancel.Checked := False;
      if MessageDlg('Хотите прервать исполнение ?' + #10 +
        'На этапе импорта это не безопасно.', mtConfirmation  , mbYes + mbNo , 0) = mrYes then
        Exit
    end;
    Row := Xml.childNodes.items[i];
    foreign_key := 'PLI_' + IntToStr(classId) + '_' + Row.getAttribute('ID');

    // exchange #3110
    // изменены сообщения в логе. в начале указываем что за эд импортируется,
    // а потом все сообщения по нему будут.
    writeLog(
        FORMAT( 'Импорт ЭД: Референс (ID) = %s, Номер: %s, Дата: %s...',
                [ Trim(Row.getAttribute('ID')),
                  Trim(Row.getAttribute('DOC_NUMBER')),
                  Trim(Row.getAttribute('DOC_DATE'))]));

    Doc := CreateOleObject('Microsoft.XmlDom');
    Doc := Doc.appendChild(Doc.createElement('DOCUMENT'));
    Doc.setAttribute('action', 'insert');
    Doc.setAttribute('class', classId);
    Doc.setAttribute('FOREIGN_KEY', foreign_key);
    Doc.setAttribute('DESCRIPTION', Row.getAttribute('DESCRIPTION'));
    Doc.setAttribute('REMARK', Row.getAttribute('REMARK'));

   // exchange #3017
   // при импорте УиБН по источникам, импортируем поле "Источник изменения БН"
   if (classId = 27)
     then Doc.setAttribute( 'SOURCE_SRCDOC_ID',
                            VarToStr( Row.getAttribute('SOURCE_SRCDOC_ID')));

    if chkDate.Checked
      then Doc.setAttribute('DOC_DATE', Row.getAttribute('DOC_DATE'))
      else Doc.setAttribute('DOC_DATE', FormatDateTime('yyyy-mm-dd', dttDate.Date));

    if not chkNumber.Checked
      then Doc.setAttribute('num_gen_type', 'generate')
      else Doc.setAttribute('DOC_NUMBER', Row.getAttribute('DOC_NUMBER'));

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

    { exchange #3110
      При загрузке ЭД проверять значения полей REC_ID, REC_ACCOUNT_ID, REC_KPP,
      REC_NAME в xml:
      1. Если в xml не заполнено поле REC_ID, то в ЭД не заполнять поля блока
        «Получатель» (даже, если другие поля в xml заполнены).
      2. Если в xml заполнено поле REC_ID, то
      2.1. Если в xml заполнено поле REC_KPP, то проверять, чтобы в карточке
          организации, найденной по REC_ID, значение в поле «КПП» вкладки
          «Общая информация» = REC_KPP.
      2.1.1. Если значения совпадают, то в ЭД поле «КПП» блока «Получатель»
          заполнить данным значением.
      2.1.2. Если не совпадают, то не сохранять ЭД и выводить сообщение
          «Поле КПП не соответствует основному КПП организации. Документ
          не загружен».
      2.2. Если в xml не заполнено поле REC_KPP, то в ЭД не заполнять поле
          «КПП» блока «Получатель».
      2.3. Если в xml заполнено поле REC_NAME, то в ЭД поле «Организация»
          блока «Получатель» заполнять значением поля REC_NAME.
      2.4. Если в xml не заполнено поле REC_NAME, то в ЭД не заполнять поле
          «Организация» блока «Получатель».
      2.5. Если в xml не заполнено поле REC_ACCOUNT_ID, то в ЭД не заполнять
          поле «Счет» блока «Получатель».
      2.6. Если в xml заполнено поле REC_ACCOUNT_ID, то проверять, чтобы счет,
          найденный по REC_ACCOUNT_ID, имел тип 6 или 7 и принадлежал
          организации, найденной по REC_ID.
      2.6.1. Если оба условия по п.2.6. выполняются, то в ЭД поле
          «Счет» блока «Получатель» заполнить данным значением.
          Поля «БИК», «Коррсчет», «Банк», «ТОФК», «УФК», «Счет УФК»
          блока «Получатель» заполнить согласно текущей реализации заполнения
          при выборе в поле «Счет» счета 6 или 7 типа, а именно:
      2.6.1.1. Если в поле «Счёт» документа указан счёт с типом 7
        «Лицевой счет по источникам в ФК», то в документе поля соответствующего
        блока заполняются следующим образом:
      - поле «счёт УФК» заполняется значением поля «Единый счёт бюджета» из
        карточки счёта «Лицевой счет по источникам в ФК».
      - поле «БИК» заполняется значением поля «БИК» из карточки счёта,
        указанного в поле «счёт УФК» документа.
      - поле «Банк» заполняется значениями полей «Название», «Тип»
        (блок Населённый пункт), «Название» (блок Населённый пункт)
        (через пробел) из карточки банка, у которого поле «БИК»
        = значение поля «БИК» документа.
      - поле «Коррсчёт» заполняется значением поля «Коррсчёт» из карточки счёта,
        указанного в поле «счёт УФК» документа.
      - поле «ТОФК» заполняется значением поля «Официальное название»
        организации, которая указана в поле «ТОФК» из карточки счёта
        «Лицевой счет по источникам в ФК».
      - поле «УФК» заполняется значением поля «Официальное название»
        организации, которая указана в поле «УФК» из карточки счёта
        «Лицевой счет по источникам в ФК».
      2.6.1.2. Если в поле «Счёт» документа указан счёт с типом 6
          «Лицевой счет по источникам в ФО», у которого поле
          «Счёт организации» блока «Финорган» заполнен счётом
          «Лицевой счет бюджета в ФК» (9 тип), то в документе поля
          соответствующего блока заполняются следующим образом:
      - поле «счёт УФК» заполняется значением поля «Единый счёт бюджета»
        из карточки счёта «Лицевой счет бюджета в ФК».
      - поле «БИК» заполняется значением поля «БИК» из карточки счёта,
        указанного в поле «счёт УФК» документа.
      - поле «Банк» заполняется значениями полей «Название», «Тип»
        (блок Населённый пункт), «Название» (блок Населённый пункт)
        (через пробел) из карточки банка, у которого поле «БИК»
        = значение поля «БИК» документа.
      - поле «Коррсчёт» заполняется значением поля «Коррсчёт» из карточки счёта,
        указанного в поле «счёт УФК» документа.
      - поле «ТОФК» заполняется значением поля «Официальное название»
        организации, которая указана в поле «ТОФК» из карточки счёта
        «Лицевой счет бюджета в ФК».
      - поле «УФК» заполняется значением поля «Официальное название»
        организации, которая указана в поле «УФК» из карточки счёта
        «Лицевой счет бюджета в ФК».
      2.6.2. Если хотя бы одно условие по 2.6. не выполняется,
        то не сохранять ЭД и выводить сообщение «Поле Счет не соответствует
        счету организации или неверный тип счета организации.
        Документ не загружен».}
    xRecID := GetNodeAttributeEx( Row, 'REC_ID');
    if (LENGTH( xRecID) > 0) then begin
      // проверяем наличие орг по ид.
      if (QueryField( 'select count( * ) Z from org where id=?',
                      [ xRecID],
                      'Z') <> '1') then begin
        WriteInfo(
            FORMAT( 'Не найден получатель (REC_ID = %s). Документ не загружен.',
                    [ xRecID]));
        CONTINUE;
      end;
      Doc.setAttribute( 'REC_ID', xRecID);

      // проверям кпп для орг
      xREC_KPP := GetNodeAttributeEx( Row, 'REC_KPP');
      if (LENGTH( xREC_KPP) > 0) then begin
        if (QueryField( 'select count( * ) Z from org where id=? and kpp=?',
                        [ xRecID,
                          xREC_KPP],
                        'Z') <> '1') then begin
          WriteInfo(
              FORMAT( 'Поле КПП (%s) не соответствует основному КПП организации %s.'
                     +' Документ не загружен.',
                      [ xREC_KPP,
                        xRecID]));
          CONTINUE;
        end;
        Doc.setAttribute( 'REC_KPP', xREC_KPP);
      end;

      // заполнение поля «Организация»
      xREC_NAME := GetNodeAttributeEx( Row, 'REC_NAME');
      if (LENGTH( xREC_NAME) > 0)
        then Doc.setAttribute( 'REC_NAME', xREC_NAME);

      // поле счет
      xREC_ACCOUNT_ID := GetNodeAttributeEx( Row, 'REC_ACCOUNT_ID');
      if (LENGTH( xREC_ACCOUNT_ID) > 0) then begin
        case QueryField( ' select o.orgacctype_id Z from orgaccount o'
                        +' where o.id=? and o.org_id=?'
                        +' and o.orgacctype_id in (6,7)',
                         [ xREC_ACCOUNT_ID,
                           xRecID],
                         'Z') of

         { если часть данных не заполнена, тогда нужно применять left join
           проверено только на счете, где данные полностью заполнены.}
          '6': xXmlACC := QueryXml(
                            ' select oke.account_number OKENUM, bke.bic OKEBIC,'
                           +' bke.description OKEBD,'
                           +' bke.city_type OKEBCT, bke.city_name OKEBCN,'
                           +' bke.CORACCOUNT OKEBCA,'
                           +' ook.description ookd, ooe.description ooed,'
                           +' o.account_number ONUM'
                           +' from orgaccount o'
                           +' join orgaccount ok on ok.id=o.KEEPERACC_ID'
                           +' join org ook on ook.id=ok.keeper_id'
                           +' join org ooe on ooe.id=ok.executer_id'
                           +' join orgaccount oke on oke.id=ok.executeracc_id'
                           +' join bank bke on bke.id=oke.bank_id'
                           +' where o.orgacctype_id=6 and ok.orgacctype_id=9'
                           +' and o.id=? and o.org_id=?',
                            [ xREC_ACCOUNT_ID,
                              xRecID]);

          '7': xXmlACC := QueryXml(
                            ' select oke.account_number OKENUM, bke.bic OKEBIC,'
                           +' bke.description OKEBD,'
                           +' bke.city_type OKEBCT, bke.city_name OKEBCN,'
                           +' bke.CORACCOUNT OKEBCA,'
                           +' ook.description ookd, ooe.description ooed,'
                           +' o.account_number ONUM'
                           +' from orgaccount o'
                           +' join org ook on ook.id=o.keeper_id'
                           +' join org ooe on ooe.id=o.executer_id'
                           +' join orgaccount ok on ok.id=o.KEEPERACC_ID'
                           +' join orgaccount oke on oke.id=o.executeracc_id'
                           +' join bank bke on bke.id=oke.bank_id'
                           +' where o.orgacctype_id=7'
                           +' and o.id=? and o.org_id=?',
                            [ xREC_ACCOUNT_ID,
                              xRecID]);

          else xXmlACC := NULL;
        end;//case//

        if NOT VarIsNULL( xXmlACC) then
          if (xXmlACC.documentElement.ChildNodes.length <> 1)
            then xXmlACC := NULL;

        if VarIsNULL( xXmlACC) then begin
          WriteInfo(
              FORMAT( 'Поле Счет (REC_ACCOUNT_ID = %s) не соответствует'
                     +' счету организации или неверный тип счета организации %s.'
                     +' Документ не загружен.',
                      [ xREC_ACCOUNT_ID,
                        xRecID]));
          CONTINUE;
        end;

        Doc.setAttribute( 'REC_ACCOUNT_ID', xREC_ACCOUNT_ID);

        xXmlACC := xXmlACC.documentElement.ChildNodes.Items[ 0];
        Doc.setAttribute( 'REC_ACCOUNT',
                          GetNodeAttributeEx( xXmlACC, 'ONUM'));
        Doc.setAttribute( 'REC_UFK_ACCOUNT',
                          GetNodeAttributeEx( xXmlACC, 'OKENUM'));
        Doc.setAttribute( 'REC_BIC',
                          GetNodeAttributeEx( xXmlACC, 'OKEBIC'));
        Doc.setAttribute( 'REC_B_NAME',
                          FORMAT( '%s %s %s',
                                  [ GetNodeAttributeEx( xXmlACC, 'OKEBD'),
                                    GetNodeAttributeEx( xXmlACC, 'OKEBCT'),
                                    GetNodeAttributeEx( xXmlACC, 'OKEBCN')]));
        Doc.setAttribute( 'REC_CORACCOUNT',
                          GetNodeAttributeEx( xXmlACC, 'OKEBCA'));
        Doc.setAttribute( 'REC_OFK_NAME',
                          GetNodeAttributeEx( xXmlACC, 'OOKD'));
        Doc.setAttribute( 'REC_UFK_NAME',
                          GetNodeAttributeEx( xXmlACC, 'OOED'));
      end;
    end;

    Doc := Doc.appendChild(Doc.ownerDocument.createElement('SOURCECHANGE'));
    with TStringList.Create do
    try
      for j := 0 to Row.childNodes.length - 1 do
      begin
        Line := Row.childNodes.items[j];
        Node := Doc.appendChild(Doc.ownerDocument.createElement('SOURCECHANGE'));
        Node.setAttribute('KADMI_CODE', Line.getAttribute('KADMI_CODE'));
        Node.setAttribute('KI_CODE', Line.getAttribute('KI_CODE'));
        Node.setAttribute('KESI_CODE', Line.getAttribute('KESI_CODE'));
        Node.setAttribute('KDI_CODE', Line.getAttribute('KDI_CODE'));
        Node.setAttribute('FSI_ID', Line.getAttribute('FSI_ID'));
        Node.setAttribute('COUNTERAGENT_ID', Line.getAttribute('COUNTERAGENT_ID'));
        Node.setAttribute('DIRECTION_ID', Line.getAttribute('PL_DIRECTION_ID'));

        // добавление кода цели
        // в ЭД УБН по источникам поле кода цели называется PURPOSEFULGRANT_ID!
        Node.setAttribute( 'PURPOSEFULGRANT_ID',
                           VarToStr(Line.getAttribute('PURPOSEFULGRANTI_ID')));
        if (VarToStr(Node.getAttribute('PURPOSEFULGRANT_ID')) <> '') and
           (QueryField( 'Select Id From PurposeFulGrant Where Id = ?',
                        [ VarToStr( Node.getAttribute('PURPOSEFULGRANT_ID'))],
                        'ID') = Null)
          then WriteInfo( 'В АЦК не найден код субсидии с референсом: ' +
                          VarToStr( Node.getAttribute('PURPOSEFULGRANT_ID')), True);

        str := MD5String(Node.xml);
        Node.setAttribute('Y1_AMT', Line.getAttribute('AMOUNT1YEAR'));
        if Period > 1 then
        begin
          Node.setAttribute('Y2_AMT', Line.getAttribute('AMOUNT2YEAR'));
          Node.setAttribute('Y3_AMT', Line.getAttribute('AMOUNT3YEAR'));
        end;
        idx := IndexOf(str);
        if idx >= 0 then
        begin
          Doc.childNodes.items[idx].setAttribute('Y1_AMT',
            StrToFloatDef( VarToStr(Doc.childNodes.items[idx].getAttribute('Y1_AMT')), 0 ) +
            StrToFloatDef( VarToStr(Node.getAttribute('AMOUNT1YEAR')), 0) );
          if Period > 1 then
          begin
            Doc.childNodes.items[idx].setAttribute('Y2_AMT',
              StrToFloatDef( VarToStr(Doc.childNodes.items[idx].getAttribute('Y2_AMT')), 0 ) +
              StrToFloatDef( VarToStr(Node.getAttribute('AMOUNT2YEAR')), 0) );
            Doc.childNodes.items[idx].setAttribute('Y3_AMT',
              StrToFloatDef( VarToStr(Doc.childNodes.items[idx].getAttribute('Y3_AMT')), 0) +
              StrToFloatDef( VarToStr(Node.getAttribute('AMOUNT3YEAR')), 0) );
            Doc.removeChild(Node);
          end;
        end
        else
          Add(str);
      end;
    finally
      Free;
    end;
    if Row.childNodes.length <> Doc.childNodes.length then
    begin
    {
      writeLog('В результате обработки документа АЦК-Планирование: ' +
        'Референс: ' + Trim(Row.getAttribute('ID')) +
        ',Номер: ' + Trim(Row.getAttribute('DOC_NUMBER')) +
        ',Дата: ' + Trim(Row.getAttribute('DOC_DATE')));
        }
      writeLog('Документ был свёрнут по строкам: в АЦК-Планирование (' +
        IntToStr(Row.childNodes.length) + ') в АЦК-Финансы (' +
        IntToStr(Doc.childNodes.length) + ')');
    end;
    Doc := Doc.ownerDocument;
    sql := 'Select Id From SrcDoc Where DocumentClass_Id = ? ' +
      ' And DispStatus_Id <> -1 And Foreign_Key = ?';
    if (QueryField(sql, [classId, foreign_key], 'ID') <> Null) then
      writeInfo('Документ был ранее загружен: ' + Trim(Doc.xml))
    else
//    begin
    try
      writeLog('Документ к вставке: ' + Trim(Doc.xml), False);

      Doc.loadXml(ExecXML(Doc.xml));
      if autofill_direction then
        set_direction(Doc.documentElement.getAttribute('ID'),
          Doc.documentElement.getAttribute('DOCUMENTCLASS_ID'), True);

      if cbStatus10.checked then
        ToStatus10(Doc);
      writeLog('Результат вставки документа: ' + Trim(Doc.xml), False);
    except
      raise( 'Сбой importXmlFile.1: ' + LastException.Message );
    end;
  end;
  prbMain.Position := 0;
  writeLog('Завершение импорта xml');
end; {End importXmlFile}

procedure checkXmlFile(Xml: Variant);
var
  i, j: Integer;
  Row, Line: Variant;
begin
  writeLog('Проверка заголовочной части xml');
  if VarIsNull(Xml.getAttribute('FILEGUID')) then
    raise('Отсутствует обязательный параметр: <FILEGUID>');
  if Xml.getAttribute('FILEGUID') <> fileGuid then
    raise('Не совпадает параметр <FILEGUID>, требуется: ' + fileGuid);
  if VarIsNull(Xml.getAttribute('FILEVER')) then
    raise('Отсутствует обязательный параметр: <FILEVER>');
  if Xml.getAttribute('FILEVER') <> fileVer then
    raise('Не совпадает параметр <FILEVER>, требуется: ' + fileVer);
  if VarIsNull(Xml.getAttribute('FINYEAR')) then
    raise('Отсутствует обязательный параметр: <FINYEAR>');
  if Xml.getAttribute('FINYEAR') <> FormatDateTime('yyyy', Work_Date) then
    raise('Не совпадает параметр <FINYEAR> , требуется: ' + FormatDateTime('yyyy', Work_Date));
  if VarIsNull(Xml.getAttribute('DOCUMENTCLASS')) then
    raise('Отсутствует обязательный параметр: <DOCUMENTCLASS>');
  if not (Xml.getAttribute('DOCUMENTCLASS') in ['158', '175']) then
    raise('Не совпадает параметр <DOCUMENTCLASS>, требуется: 158 или 175');
  case Xml.getAttribute('DOCUMENTCLASS') of
    158: classId := 58;
    175: classId := 27;
  end;
  case classId of
    58: writeLog('Импортирумеый класс документов: План по источникам (58)');
    27: writeLog('Импортирумеый класс документов: Изменение план по источникам (27)');
  end;
  writeLog('Проверка содержательной части xml');
  prbMain.Max := Xml.childNodes.length;
  for i := 0 to Xml.childNodes.length - 1 do
  begin
    prbMain.Position := prbMain.Position + 1;
    form.SimpleText := Format('Проверка данных ... %d из %d', [prbMain.Position, prbMain.Max]);
    if form.actCancel.Checked then
    begin
      form.actCancel.Checked := False;
      if MessageDlg('Хотите прервать исполнение ?' + #10 +
        'На этапе проверки это безопасно.', mtConfirmation  , mbYes + mbNo , 0) = mrYes then
        Exit
    end;
    Row := Xml.childNodes.items[i];
    for j := 0 to Row.childNodes.length - 1 do
    begin
      Line := Row.childNodes.items[j];
      checkKBK(Line, 'KADMI');
      checkKBK(Line, 'KESI');
      checkKBK(Line, 'KI');
      checkKBK(Line, 'KDI');
      if QueryField('Select Id From FinSource Where Id = ?',
        [Line.getAttribute('FSI_ID')], 'ID') = null then
      begin
        writeInfo('В АЦК не найден  код источника финансирования: ' +
          Line.getAttribute('FSI_ID') + ', заменён на Неклассифицированный источник');
        Line.setAttribute('FSI_ID', 0);
      end;
      if QueryField('Select Id From Org Where Id = ?',
        [Line.getAttribute('COUNTERAGENT_ID')], 'ID') = null then
      begin
        writeInfo('В АЦК не найден контрагент: ' +
          Line.getAttribute('COUNTERAGENT_CAPTION') + ', референс: ' +
          Line.getAttribute('COUNTERAGENT_ID') + ', заменён на Не Указана');
        Line.setAttribute('COUNTERAGENT_ID', 0);
      end;
      if VarToStrDef(Line.getAttribute('PL_DIRECTION_ID'), '') <> '' then
      if QueryField('Select Id From Pl_Direction Where Id = ?',
        [Line.getAttribute('PL_DIRECTION_ID')], 'ID') = null then
      begin
        writeInfo('В АЦК не найдено расходное обязательство: ' +
          Line.getAttribute('PL_DIRECTION_NAME') + ', референс: ' +
          Line.getAttribute('PL_DIRECTION_NAME') + ', заменён на Не Указан');
        Line.setAttribute('PL_DIRECTION_ID', 0);
      end;
    end;
  end;
  prbMain.Position := 0;
  writeLog('Проверка xml завершена успешно');
end; {End checkXmlFile}

procedure checkKBK(Xml: Variant; CodeName: String);
begin
  sql := 'Select Code From ';
  case CodeName of
    'KADMI': sql := sql + 'Kadm';
    'KESI': sql := sql + 'Kes';
    'KI': sql := sql + 'Ki';
    'KDI': sql := sql + 'Kdi';
  end;
  sql := sql + ' Where Budget_Id = ? And Code = ?';
  if QueryField(sql, [Budget_Id, Xml.getAttribute(CodeName + '_CODE')], 'CODE') = null then
  begin
    writeInfo('В АЦК не найден ' + captionKbkByCodeName(CodeName) + ': ' +
      Xml.getAttribute(CodeName + '_CODE') + ', заменён на Не Указан');
    Xml.setAttribute(CodeName + '_CODE', 0);
  end;
end; {End checkKBK}

function captionKbkByCodeName(CodeName: String): String;
begin
  case CodeName of
    'KADMI': Result := 'классификатор администраторов поступлений и выбытий';
    'KESI': Result := 'экономический классификатор источников';
    'KI': Result := 'классификатор видов источников';
    'KDI': Result := 'дополнительный код источников';
  end;
end; {End captionKbkByCodeName}

procedure actExecuteExecute(Sender: TObject);
var
  Xml: Variant;
begin
  WriteLog('    ====== НАЧАЛО ИМПОРТА ======');
  if FileExists(fileInfo) then DeleteFile(fileInfo);
  if FileExists(fileError) then DeleteFile(fileError);
  SetAppDisable;
  btnExecute.Action := form.actCancel;
  ListInfoError.Clear;
  try
    QueryInitialization;
    Period := StrToInt(VarToStrDef(QueryField(
      'Select Period From Budget Where Id = ?', [Budget_Id], 'PERIOD'), '1'));
    try
      if Trim(edtFile.Text) = '' then
        raise('Не указана файл импорта');
      if not FileExists(edtFile.Text) then
        raise('Не найден файл импорта: ' + edtFile.Text);
      writeLog('     === ПАРАМЕТРЫ  ИМПОРТА ===');
      writeLog('Файл импорта: ' + edtFile.Text);
      if chkDate.Checked then
        writeLog('Даты из документов АЦК-планирование')
      else
        writeLog('Даты документов устанавливаются в ' + FormatDateTime('yyyy-mm-dd', dttDate.Date));
      if not chkNumber.Checked then
        writeLog('Номера из документов АЦК-планирование')
      else
        writeLog('Автонумерация документов');
      Xml := CreateOleObject('Microsoft.XmlDom');
      writeLog('Загрузка файла импорта: '  + edtFile.Text);
      if not Xml.load(edtFile.Text) then
        raise('Ошибка загрузки файла импорта: ' + edtFile.Text);
      checkXmlFile(Xml.documentElement);
      if not FileExists(fileError) then
      try
        ChangeSysParams;
        importXmlFile(Xml.documentElement);
      finally
        RestoreSysParams;
      end;
    except
      writeLog('Критическая ошибка: ' + LastException.Message);
      raise(LastException.Message);
    end;
    if FileExists(fileError) then
    begin
      writeError('Импорт прекращён в результате проверки, исправте ошибки');
      ShellExecute('NotePad', FileError, False);
    end
    else if FileExists(fileInfo) then
    begin
      writeInfo('Импорт завершён с предупреждениями');
      ShellExecute('NotePad', fileInfo, False);
    end
    else
      ShowMessage('Импорт завершён успешно');
  finally
    QueryFinalization;
    form.SimpleText := '';
    prbMain.Position := 0;
    SetAppEnable;
    btnExecute.Action := form.actExecute;
    writeLog('    ==== ЗАВЕРШЕНИЕ ИМПОРТА ====');
  end;
end; {End actExecuteExecute}

var
  XmlSysParams: Variant;

procedure ChangeSysParams;
var
  Xml: Variant;
begin
  WriteLog('Изменение системных параметров');
  XmlSysParams := CreateOleObject('Microsoft.XmlDom');
  XmlSysParams.appendChild(XmlSysParams.createElement('EXCH_UTILS_PROCESSOR'));
  XmlSysParams.documentElement.setAttribute('action', 'get_sys_param_value');
  XmlSysParams.documentElement.appendChild(XmlSysParams.createElement('SYSPARAM')).
    setAttribute('NAME', 'docs.inform_ki_code_has_children');
  XmlSysParams.loadXml(ExecXml(XmlSysParams.xml));
  Xml := CreateOleObject('Microsoft.XmlDom');
  Xml.loadXml(StringReplace(XmlSysParams.xml, 'EXCH_UTILS_PROCESSOR', 'REFERENCE'));
  Xml.documentElement.setAttribute('ref_name', 'SYSPARAM');
  Xml.documentElement.setAttribute('action', 'save');
  if  (Xml.documentElement.childNodes.Length > 0)
   and(Xml.documentElement.childNodes.item[0].childNodes.Length > 0)
   and(Xml.documentElement.childNodes.item[0].childNodes.item[0].childNodes.Length > 0)
  then begin
    Xml.documentElement.childNodes.item[0].childNodes.item[0].childNodes.item[0].nodeValue := '0';
    ExecXml(Xml.xml);
  end;
end; {End ChangeSysParams}

procedure RestoreSysParams;
begin
  WriteLog('Востановление системных параметров');
  XmlSysParams.documentElement.setAttribute('action', 'save');
  ExecXml(XmlSysParams.xml);
end; {End RestoreSysParams}

procedure spbFileOnClick(Sender: TObject);
begin
  if opdFile.Execute then
    edtFile.Text := opdFile.FileName;
end; {End spbFileOnClick}

procedure initialization(Xml: Variant);
var
  iniPath: String;
begin
  ListInfoError := TStringList.Create;
  iniPath := ExtractFilePath(Xml.documentElement.getAttribute('INI_FILE'));
  fileLog := iniPath + 'PlanSrcDocImport.log';
  fileInfo := iniPath + 'PlanSrcDocImport.inf';
  fileError := iniPath + 'PlanSrcDocImport.err';
  writeLog(' ====== ИНИЦИАЛИАЗЦИЯ ИМПОРТА ======');
  form := TFormCustom.Create(Application);
  form.LoadDfm(IniPath + 'Fm_PlanSrcDocImport.dfm');
  form.Name := 'FmPlanSrcDocImport';
  TLabel(form.FindComponent('lblVersion')).Caption := 'Версия формата: ' + fileVer;
  writeLog(TLabel(form.FindComponent('lblVersion')).Caption);
  prbMain := TProgressBar(form.FindComponent('prbMain'));
  btnExecute := TButton(Form.FindComponent('btnExecute'));
  dttDate := TDateTimeEh(form.FindComponent('dttDate'));
  dttDate.Date := work_Date;
  edtFile := TEdit(form.FindComponent('edtFile'));
  TSpeedButton(form.FindComponent('spbFile')).OnClick := @spbFileOnClick;
  opdFile := TOpenDialog(form.FindComponent('opdFile'));
  chkDate := TCheckBox(form.FindComponent('chkDate'));
  chkNumber := TCheckBox(form.FindComponent('chkNumber'));
  cbStatus10 := TCheckBox(form.FindComponent('cbStatus10'));
  form.actExecute.OnExecute := @actExecuteExecute;

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

end; {End initialization}

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

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}

begin
  fAzkLogined := AzkIsLogin;
  fDebuged := True;
  Xml := CreateOleObject('Microsoft.XmlDom');
  if not FileExists(fileXmlNameTesting) then
  begin
    ShowMessage('Не найден тестовый пример: ' + fileXmlNameTesting);
    Exit;
  end;
  if not Xml.Load(fileXmlNameTesting) then
  begin
    ShowMessage('Ошибка при загрузке: ' + fileXmlNameTesting);
    Exit;
  end;
  if not fAzkLogined then
    if not AzkLogin then
      Exit;
  try
    Main(Xml);
  finally
    if not fAzkLogined then
      AzkLogout;
  end;
end.  {End Start Testing}
