// постановка exchange #2409

const
  cnCfg_Exch_2409_Sign =
      'Устанавливать ЭЦП(ЗК) в ответах на запросы';
  cnCfg_Exch2409_GenerateAnswerOnED243 =
      'Формировать ответ на запрос уточнения реквизитов ПП ED244';
  cnCfg_Exch_2409_DirOut =
      'Каталог ответов на запрос уточнения реквизитов ПП ED244';
  cnCfg_Exch_2409_UIS =
      'Идентификатор составителя';
  cnCfg_Exch_2409_VCERTLocalPath =
      'Каталог справочников ЭЦП';
  cnCfg_Exch_2409_FormatVersion =
      'Версия формата';
  cnCfg_Exch_2409_FormatXml =
      'Форматировать XML в ответах на запросы';
  cnCfg_Exch_2409_EnvelopeCompessed =
      'Упаковывать Object в ответах на запросы';
  cnCfg_Exch_2409_EnvelopeCrypted =
      'Шифровать Object в ответах на запросы';
  cnCfg_Exch_2409_OIDEncrypt =
      'OID ключа адресата шифрования в ответах на запросы';
  cnCfg_Exch_2409_SigEnvelope =
      'Формировать конверт ЭЦП(КА) в ответах на запросы';
  cnCfg_Exch_2409_Envelope =
      'Формировать служебный конверт в ответах на запросы';
  cnCfg_Exch_2409_EnvelopeTo =
      'Адрес получателя в ответах на запросы';
  cnCfg_Exch_2409_EnvelopeFrom =
      'Адрес отправителя в ответах на запросы';
//  cnCfg_Exch_2409_EnvelopeSystemCode := Trim(Values['Признак системы обработки']);
  cnCfg_Exch_2409_EnvelopeMessageType =
      'Тип сообщения в ответах на запросы';
  cnCfg_Exch_2409_EnvelopePriority =
      'Приоритет сообщения в ответах на запросы';
  cnCfg_Exch_2409_EnvelopeAppMessageID =
      'Прикладной идентификатор в ответах на запросы';
  cnCfg_Exch_2409_EnvelopeMessageID =
      'Идентификатор сообщения в ответах на запросы';
  cnCfg_Exch_2409_EnvelopeTransportFileName =
      'Передавать имя файла в ответах на запросы';
{  cnCfg_Exch_2409_SystemCode =
      'Признак системы обработки в ответах на запросы';}

var
  // значение опции "Формировать ответ на запрос уточнения реквизитов ПП ED244"
  // =true если =да. =false если значение "нет" или такой опции в конфиге нет.
  vExch2409_GenerateAnswerOnED243: boolean;
  // УИС - берется из настроек
  vExch_2409_UIS: string;
  // номер выгружаемого ЭД
//  vExch_2409_No: integer;
  // каталог ответов на запросы
  vExch_2409_DirOut: string;
  // параметры xml формирвоания ответов
  vExch_2409_v1: string;
  vExch_2409_v2: string;
  vExch_2409_v3: string;
  vExch_2409_v4: string;
  vExch_2409_v5: string;
  // необходимость ЭЦП в ответе
  vExch_2409_Sign: boolean;
  // версия формата
  vExch_2409_FormatVersion: string;
  // подходящая версия (=true) для формирования ответов
  // если =false, то версия vExch_2409_FormatVersion не подходит для формирования
  // ответов на запросы.
  vExch_2409_VersionCorrect: boolean;
  // форматировать xml ответа
  vExch_2409_FormatXml: boolean;
  // каталог схемы по формату
  vExch_2409_DirSchems: string;
  // файл параметров для ответов
  vExch_2409_FileDatED244: string;
  // путь справочников ЭЦП
  vExch_2409_VCERTLocalPath: string;
  // параметры шифрования ответа
  vExch_2409_EnvelopeCrypted: boolean;
  vExch_2409_OIDEncrypt: string;
  vExch_2409_EnvelopeCompessed: boolean;
  // формировать конверт ответа
  vExch_2409_SigEnvelope: boolean;
  // формировать служебный конверт
  vExch_2409_Envelope: boolean;
  // параметры служебного конверта
  vExch_2409_EnvelopeTo: string;
  vExch_2409_EnvelopeFrom: string;
//  vExch_2409_EnvelopeSystemCode := Trim(Values['Признак системы обработки']);
  vExch_2409_EnvelopeMessageType: integer;
  vExch_2409_EnvelopePriority: string;
  vExch_2409_EnvelopeAppMessageID: boolean;
  vExch_2409_EnvelopeMessageID: boolean;
  vExch_2409_EnvelopeTransportFileName: boolean;
  // признак
//  vExch_2409_SystemCode: string;


  { с целью упрощения доступа к конфигурации, выделяем глобальную переменную
    в которой будем хранить копию файла cfg.создаваться и заполняться объект
    будет в инициализации, а разрушаться - в подпрограмме Exch2409_Finalize.
    тем.самым вместо того, чтобы делать переменные на каждый параметр,
    достаточно будет обратится к настройкам в нужный момент.
    такой подход удобен для медленных/разовых операций.
    для итерационных операций этот метод не эффективен.}
//  vExch_2409_Config: TStringList;

//  vExch_2409_ListNumber: TStringList;


function ConfigParamBoolean( aParam: string): boolean;
begin
  Result := (LOWERCASE( TRIM(aParam)) = 'да');
end;

function ConfigParamDefault( aParam, aDefault: string): string;
begin
  if ( LENGTH( aParam) > 0)
    then Result := aParam
    else Result := aDefault;
end;

function CheckSlashDirStr( aDir: string): string;
begin
  try
    Result := aDir;
    if (LENGTH( Result) > 0) then
      if ( Result[ LENGTH( Result)] <> '\')
        then Result := Result + '\';
    except
      ExceptionLog( 'Сбой Un_Exch2409.CheckSlashDirStr');
  end;
end;

{~Exch2409_Initialize
  инициализация модуля по #2409.
  aConfig - конфигурационные параметры (из Main.initialize).
  здесь проверяется значение опции в конфиге по #2409 и выполняется
  инициализация глобальной переменной vExch2409_GenerateAnswerOnED243 по
  этой опции. }
procedure Exch2409_Initialize( var aConfig: TStringList; aXML: Variant);
begin
  try
{    vExch_2409_Config:= TStringList.Create;
    vExch_2409_Config.Text := aConfig.Text;}

    vExch2409_GenerateAnswerOnED243 :=
          ConfigParamBoolean(
              aConfig.Values[ cnCfg_Exch2409_GenerateAnswerOnED243]);

    // УИС для ответа
    vExch_2409_UIS :=
          Trim( aConfig.Values[ cnCfg_Exch_2409_UIS]);

    // версия форматов загрузки
    vExch_2409_FormatVersion :=
          Trim( aConfig.Values[ cnCfg_Exch_2409_FormatVersion]);

    // каталог схемы по формату
    vExch_2409_DirSchems :=
          DirMain + 'UfebsSchems\' + vExch_2409_FormatVersion;

    // каталог результатов
    vExch_2409_DirOut :=
          CheckSlashDirStr(
            ConfigParamDefault(
              Trim( aConfig.Values[ cnCfg_Exch_2409_DirOut]),
              DirMain));

    // путь справочников ЭЦП
    vExch_2409_VCERTLocalPath :=
          CheckSlashDirStr(
            ConfigParamDefault(
              Trim( aConfig.Values[ cnCfg_Exch_2409_VCERTLocalPath]),
              DirMain));

    // описание xml
    vExch_2409_v1 := Trim(aConfig.Values['urn:cbr-ru:ed:v1.1']);
    vExch_2409_v2 := Trim(aConfig.Values['urn:cbr-ru:dsig:v1.1']);
    vExch_2409_v3 := Trim(aConfig.Values['urn:cbr-ru:dsig:env:v1.1']);
    vExch_2409_v4 := Trim(aConfig.Values['http://www.w3.org/2003/05/soap-envelope']);
    vExch_2409_v5 := Trim(aConfig.Values['urn:cbr-ru:msg:props:v1.1']);

    // необходимость ЭЦК в ответе
    vExch_2409_Sign :=
          ConfigParamBoolean(
              aConfig.Values[ cnCfg_Exch_2409_Sign]);

    // форматировать xml пакета ответа
    vExch_2409_FormatXml :=
          ConfigParamBoolean(
              aConfig.Values[ cnCfg_Exch_2409_FormatXml]);

    { проверяем версию в конфигурации
      если версия ниже 2.5.7 то формировать ответы не будем!}
    vExch_2409_VersionCorrect :=
          (Version_CompareEx( vExch_2409_FormatVersion, '2.5.7') >= 0);

    // файл параметров для ответов
    vExch_2409_FileDatED244 :=
          DirMain + 'ответы_ED244.dat';

    // параметры шифрования пакета ответа
    vExch_2409_EnvelopeCrypted :=
          ConfigParamBoolean(
              aConfig.Values[ cnCfg_Exch_2409_EnvelopeCrypted]);
    vExch_2409_OIDEncrypt :=
          TRIM( aConfig.Values[ cnCfg_Exch_2409_OIDEncrypt]);
    vExch_2409_EnvelopeCompessed :=
          ConfigParamBoolean(
              aConfig.Values[ cnCfg_Exch_2409_EnvelopeCompessed]);

    vExch_2409_SigEnvelope :=
          ConfigParamBoolean(
              aConfig.Values[ cnCfg_Exch_2409_SigEnvelope]);

    vExch_2409_Envelope :=
          ConfigParamBoolean(
              aConfig.Values[ cnCfg_Exch_2409_Envelope]);

    vExch_2409_EnvelopeTo :=
          Trim( aConfig.Values[ cnCfg_Exch_2409_EnvelopeTo]);
    vExch_2409_EnvelopeFrom :=
          Trim( aConfig.Values[ cnCfg_Exch_2409_EnvelopeFrom]);
//    vExch_2409_EnvelopeSystemCode := Trim(Values['Признак системы обработки']);
    try
      vExch_2409_EnvelopeMessageType :=
          STRTOINT( Trim( aConfig.Values[ cnCfg_Exch_2409_EnvelopeMessageType]));
      except
        ExceptionLog( FORMAT( 'Ошибка в значении параметра конфигурации "%s".'#13#10
                             +'Ожидается числовое значение.',
                              [ cnCfg_Exch_2409_EnvelopeMessageType]));
    end;

    vExch_2409_EnvelopePriority :=
          Trim( aConfig.Values[ cnCfg_Exch_2409_EnvelopePriority]);

    vExch_2409_EnvelopeAppMessageID :=
          ConfigParamBoolean(
              aConfig.Values[ cnCfg_Exch_2409_EnvelopeAppMessageID]);
    vExch_2409_EnvelopeMessageID :=
          ConfigParamBoolean(
              aConfig.Values[ cnCfg_Exch_2409_EnvelopeMessageID]);
    vExch_2409_EnvelopeTransportFileName :=
          ConfigParamBoolean(
              aConfig.Values[ cnCfg_Exch_2409_EnvelopeTransportFileName]);

{    vExch_2409_SystemCode :=
          Trim( aConfig.Values[ cnCfg_Exch_2409_SystemCode]);}

//    vExch_2409_ListNumber:= TStringList.Create;
    except
      ExceptionLog( 'Сбой Un_Exch2409.Exch2409_Initialize');
  end;
end;


{~Exch2409_Finalize
  завершение модуля по #2409
  оставлено на случай дополнительных объектов}
procedure Exch2409_Finalize;
begin
  try
//    vExch_2409_ListNumber.Free;
//    vExch_2409_Config.Free;
    except
      ExceptionLog( 'Сбой Un_Exch2409.Exch2409_Finalize');
  end;
end;

(*
{~DDdMMdYYYY_To_YYYYmMMmDD
  преобразование строки даты xStr из "ДД.ММ.ГГГГ" в строку "ГГГГ-ММ-ДД".}
function DDdMMdYYYY_To_YYYYmMMmDD( aDateTimeStr: string): string;
begin
  try
    if (LENGTH( aDateTimeStr) < 10)
      then Result := aDateTimeStr
      else Result := COPY( aDateTimeStr, 7, 4) + '-'
                    +COPY( aDateTimeStr, 4, 2) + '-'
                    +COPY( aDateTimeStr, 1, 2);
    except
      ExceptionLog( 'Сбой скрипта: "Un_Exch2409.DDdMMdYYYY_To_YYYYmMMmDD.');
  end;
end;
*)


{~AppendStringLeft
  дозаполнение строки aStr до длины aLength слева, подстрокой aSubStr.}
function AppendStringLeft( aStr: string; aLength: integer; aSubStr: string): string;
begin
  try
    Result := aStr;
    while (LENGTH( Result) < aLength) do Result := aSubStr + Result;
    // отсекаем лишнее слева
    if (LENGTH( Result) > aLength)
      then DELETE( Result, 1, LENGTH( Result) - aLength);
    except
      ExceptionLog( 'Сбой Un_Exch2409.AppendStringLeft');
  end;
end;

function SaveToEx_2409( aED: Variant; aDocInfo, aSubDir, aNo: string; var aFileName: string): boolean;
var
  xDir: string;
begin
  try
    aFileName := '';
    // каталог результата будет с подкаталогом даты
    xDir := vExch_2409_DirOut + aSubDir;
    ForceDirectories( xDir);
    if not DirectoryExists( xDir) then begin
      MoveToError( aDocInfo
                    + 'не сформирован.'
                    + FORMAT( #13#10'Ошибка при создании каталога результата: "%s".',
                              [ xDir]));
      Result := FALSE;
      EXIT;
    end;
    // имя файла будет соответствовать входному файлу
    { exchange #3038
      добавлен порядковый номер файла - этот номер добавляется к имени
      формируемого файла ED244.
      каталог + имя файла ED234/пакета + "-" + номер ED244 + расш.файла ED243/пакета
      если этого не делать, то при приходе пакета ED243, выходные файлы
      ответов ED244 будут перезатирать друг друга.}
    aFileName := xDir + ChangeFileExt( ExtractFileName(FileIn), '')
                      + '-' + aNo
                      + ExtractFileExt( FileIn);
//showmessage('отладка: FileOut="'+xDir + ExtractFileName(FileIn)+'".');
    aED.SaveToFile( aFileName);
    Result := TRUE;
    except
      ExceptionLog( 'Сбой Un_Exch2409.SaveToEx_2409');
  end;
end;


{~Get_EDNo_for_ED244
  exchange #3033
  получение номера ED244
  теперь номер ED244 должен быть синхронизирован с номерами выгружаемых
  ЭД через эксч. т.е. для получения номера нужно запрашивать сервер
  АЦК-Ф на каждый формируемый ED244. }
function Get_EDNo_for_ED244( aBudgetID, aExch_Name: string; aDate: TDateTime): integer;
var
  i: Integer;
  xXML: Variant;
  xXmlRoot: Variant;
begin

    try
      Result := 1;

      xXML := CreateOleObject('Microsoft.XmlDom');
      try
        // обращаемся к процессору работы с эксчем
        // нужно получить макс.число ЭД в пакете.
        xXmlRoot := xXML.appendChild( xXML.createElement('EXCHPDNUMBERINFO'));
        xXmlRoot.setAttribute( 'action', 'getData');
        xXmlRoot.setAttribute( 'BUDGET', aBudgetID);
        // по полю FORMAT+OPER_DATE формируется уникальный номер выгружаемого эд
        // поле FORMAT берем из "Идентификатор формата" файла конфигурации
        xXmlRoot.setAttribute( 'OPER_DATE', FormatDateTime('yyyy-mm-dd', aDate));
        xXmlRoot.setAttribute( 'FORMAT', aExch_Name);
        // запрашиваем номер для одного ЭД
        xXmlRoot.setAttribute( 'DOC_COUNT', '1');
        try
          xXml.loadXML(ExecXML( xXml.documentElement.xml));
          except
            ExceptionLog( 'Сбой запроса номера пакета');
            EXIT;
        end;
        { разбор ответа в виде XML:
<EXCHPDNUMBERINFO PORTION="1" START_DOC_NUMBER="1">
<METADATA/>
<PARAM ver="2.0"/>
<CUSTOM_VALUES/>
</EXCHPDNUMBERINFO>}
        for i := 0 to xXml.childNodes.length - 1 do begin
          xXmlRoot := xXml.childNodes.items[ i];
          if (xXmlRoot.nodeName = 'EXCHPDNUMBERINFO') then begin
            try
              Result := STRTOINT( VarToStr( xXmlRoot.getAttribute( 'START_DOC_NUMBER')));
              except
                ExceptionLog( 'Ошибка в результате запроса получения номера ЭД в пакете.');
            end;
          end;
        end;//for//
        finally
          xXML := NIL;
      end;
      except
        ExceptionProc( 'Get_EDNo_for_ED244');
    end;

end;



{~Exch2409_GenerateAnswerED244OnED243
  формирует ответ на документ aDocED243.
  параметры
    aDocED243 - объект OCX ED243;
    aDocPP - узел описания ЭД ПП.
  результат
    =true - функция отработана по exchange #2409.
            этот результат должен быть независимо от найденных ЭД ПП.
    =false - задача по #2409 не выполняется.
             этот результат формируется, если значение глобальной переменной
             vExch2409_GenerateAnswerOnED243=false.}
function Exch2409_GenerateAnswerED244OnED243( aED243: Variant;
                                              aDocPP_ID: string;
                                              aBudgetID: string;
                                              aExch_Name: string
                         //                     aDirIni: string;
                                              {aExchNumber: string}): boolean;



var
//  xSQL: string;
  i: Integer;
  xED243: Variant;
  xED244: Variant;
  xDocPPInfo: string;
  xXML: Variant;
  xXML_DocPP: variant;
//  xXml_DocLine: variant;
  s: string;
//  xFoundNumber: boolean;
  xFileIni: TIniFile;
//  xDate: TDateTime;
  xNow: TDateTime;
  xPacked: Variant;
  xFileED244: string;
  xFileED244_S: string;
  xFileED244_M: string;
  xED244_EDNo: string;
//  SL: tstringlist;   // отладка
begin
  try
    if NOT vExch2409_GenerateAnswerOnED243 then begin
      // задача #2409 не должна выполняться и код должен работать по старому
      Result := FALSE;
      EXIT;
    end;

    Result := TRUE;   // ответ на ЭД 243



    // если ПП не найдено среди выгруженных:
    // сообщаем об ошибке и отправляем файл запроса к ошибкам
    if (LENGTH(aDocPP_ID) <= 0) then begin
      MoveToError('ЭД не найден в базе');
      EXIT;
    end;

    // полное описание ЭД по ID
    xXml := QueryXml( 'select * from PayOrder where ID=?', [ aDocPP_ID]);
    xXML_DocPP := xXml.documentElement.ChildNodes.Item[0];

    xDocPPInfo := FORMAT( 'Ответ на запрос по платежному документу № %s Дата: %s '
                          +' Сумма: %.2f ',
                          [ Trim( xXML_DocPP.getAttribute('DOC_NUMBER')),
                            FormatDateTime(
                                  'dd.mm.yyyy',
                                  XmlToDateTime(
                                          xXML_DocPP.getAttribute('DOC_DATE'))),
                            StrToCurr( xXML_DocPP.getAttribute('AMOUNT'))]);

    // ответ формируем только если версия форматов не ниже нужной
    if NOT vExch_2409_VersionCorrect then begin
      MoveToError( xDocPPInfo
                    + ' не сформирован. Необходима версия форматов не ниже 2.5.7');
      EXIT;
    end;

    // ФТ-6: проверяем номер запроса
    // запрос на проверку дублей =03 игнорируем, как и =00 и =08.
    if NOT (aED243.EDDefineRequestCode in [1,2,4,5,6,7]) then begin
      // на коды 0,3 ответ не формируем, но это не ошибка - ОТ
      if (aED243.EDDefineRequestCode in [0,3])
        then WriteInfo(
                      xDocPPInfo
                    + FORMAT( ' не сформирован. Код запроса в ЭС ED243 %s не предусматривает ответ ED244.',
                              // здесь номер кода расшифровываем
                              [ scRequestCode[
                                    StrToInt( aED243.EDDefineRequestCode)]]))
        else MoveToError(
                      xDocPPInfo
                    + FORMAT( ' не сформирован. Код запроса в ЭС ED243 имеет непредусмотренное значение: %s.',
                // поскольку заранее номер кода неизвестен, а таблица имеет только
                // определенное описание кодов, то вместо строковой расшифровки
                // даем только номер кода.
                              [ aED243.EDDefineRequestCode]));
      EXIT;
    end;

//writeinfo('xXML_DocPP:'+#13#10+xXML_DocPP.xml);

    // фиксируем текущую системную дату
    xNOW := NOW;

    // файлы не созданы
    xFileED244 := '';
    xFileED244_S := '';
    xFileED244_M := '';
    try
      // создание пакета
      try
        xED244 := CreateOleObject('Ufebs.ED244');
        // заголовок пакета
        xED244.FormatVer := vExch_2409_FormatVersion;
        xED244.DirSchems := vExch_2409_DirSchems;
        xED244.NSType[0] := 'ed';
        // параметры шифрования пакета
        xED244.EnvelopeCrypted    := vExch_2409_EnvelopeCrypted;
        xED244.OIDEncrypt         := vExch_2409_OIDEncrypt;
        xED244.EnvelopeCompessed  := vExch_2409_EnvelopeCompessed;
    //    xED244.SystemCode         := vExch_2409_SystemCode;//'01';// FSystemCode;
        except
          ExceptionLog( 'Сбой создания ED244.');
          EXIT;
      end;

      //отладка
      //  xED244.DirSchems := 'd:\work\AzkScript\aED244\ufebsSchems\2.5.7';

      { Номер электронного документа.
        этот номер будем вести в отдельном ini файле. он будет уникальным
        для пакетов ответов в течении одного дня.
        файл такой:
          [ПАКЕТЫ]
          ДАТА=yyyy/mm/dd
          НОМЕР=N
        где:
          ДАТА - дата последнего формируемого ответа.
          НОМЕР - номер последнего сформированного пакета.
        если файл пустой или текущая дата не совпадает с указанной в параметре
        ДАТА, то новый номер начинается с единицы. после каждого формирования
        пакета ответа номер инкрементируется и файл перезаписывается, а поле
        ДАТА подставляется текущая системная дата.}
      try
        xED244.EDNo := 1;
(*
        xFileIni:= TIniFile.Create( vExch_2409_FileDatED244);
        try
          // ожидается строка: yyyyXmmXdd
          // где: X - любой разделитель ("/" или "-" или ".")
          // если строка содержит 10 символов, выделяем подстроки гггг,мм,дд и
          // сравниваем их со значением текущей системной даты.
          // в случае любых ошибок (преобразований строка->число), неверный формат
          // строки в поле ДАТА, перехватываем исключение и номер пакета начинаем
          // с единицы. если дата совпадает с системной, считываем номер последнего
          // пакета из поля НОМЕР, инкрементируем его и это и будет номер нового
          // пакета ответа.
          s := TRIM( xFileIni.ReadString( 'ПАКЕТЫ', 'ДАТА', ''));
          if (LENGTH(s) >= 10) then
            try
{
showmessage( 'дата = "'+s+'"'
            +#13#10'день='+inttostr(DayOf( xNOW))+' - файл:"'+inttostr(STRTOINT( COPY( s, 9, 2)))+'"'
            +#13#10'мес='+inttostr(MonthOf( xNOW))+' - файл:"'+inttostr(STRTOINT( COPY( s, 6, 2)))+'"'
            +#13#10'год='+inttostr(YearOf( xNOW))+' - файл:"'+inttostr(STRTOINT( COPY( s, 1, 4)))+'"'
            );
}
              if (( DayOf( xNOW) = STRTOINT( COPY( s, 9, 2)))
                  AND (MonthOf( xNOW) = STRTOINT( COPY( s, 6, 2)))
                  AND (YearOf( xNOW) = STRTOINT( COPY( s, 1, 4)))) then
                try
                  xED244.EDNo := (xFileIni.ReadInteger( 'ПАКЕТЫ', 'НОМЕР', 0) + 1);
//showmessage('инкремент нумерации: '+inttostr(xED244.EDNo));
                  except
                end;
              except
            end;
          // перезаписываем файл с новыми параметрами
          xFileIni.WriteInteger( 'ПАКЕТЫ', 'НОМЕР', xED244.EDNo);
          xFileIni.WriteString ( 'ПАКЕТЫ', 'ДАТА', FormatDateTime( 'yyyy/mm/dd', xNOW));
          xFileIni.UpdateFile;
          finally
            xFileIni.Free;
        end;
*)
        // exchange #3033
        // теперь номер ED244 должен быть синхронизирован с номерами выгружаемых
        // ЭД через эксч. т.е. для получения номера нужно запрашивать сервер
        // АЦК-Ф на каждый формируемый ED244.
        // дата для генерация номера ED244 определяется по системной дате
        // генерации ответа ED244
        xED244.EDNo := Get_EDNo_for_ED244( aBudgetID, aExch_Name, xNOW);

        // exchange #3033
        // ведение номера ЭД244 в имени файла.
        // это нужно для того, что когда приходит пакет ED243, ответ ED244
        // формируется в отдельном файле, и чтобы эти файлы не перезатирали
        // друг друга, к имени файла ED244 добавляется номер ED
        xED244_EDNo := INTTOSTR( xED244.EDNo);
        except
          ExceptionLog( 'Сбой определения поля номера пакета ED244.');
          EXIT;
      end;

      try
        // Текущая рабочая дата
        xED244.EDDate := xNow;

        // УИС
        xED244.EDAuthor := vExch_2409_UIS;

    //showmessage( 'vExch_2409_SystemCode = "'+vExch_2409_SystemCode+'".');
    //    xED244.SystemCode := '01';// + vExch_2409_SystemCode;//'01';// FSystemCode;

        // Значение кода из ED 243
        xED244.EDDefineRequestCode  := aED243.EDDefineRequestCode;

        // код ответа - Значение согласно условиям описанным в ФТ -  4, ФТ – 5.
        case aED243.EDDefineRequestCode of
          // ФТ-4
          1,2,6: xED244.EDDefineAnswerCode := '01';
          // ФТ-5
          4,5,7: xED244.EDDefineAnswerCode := aED243.EDDefineRequestCode;
        end;//case//

        // Значение поля «Номер» платежного поручения.
        // если номер документа не будет цифровым - будет сбой!
        xED244.OriginalEpd.EDNo :=
                            StrToInt( TRIM( xXML_DocPP.getAttribute('DOC_NUMBER')));
        // Значение поля «Дата» платежного поручения
        xED244.OriginalEpd.EDDate :=
                XMLToDateTime( VarToStr( xXML_DocPP.getAttribute('DOC_DATE')));
        // УИС
        xED244.OriginalEpd.EDAuthor := vExch_2409_UIS;

        // Дата выписки по которой прошло Платежное поручение.
        // соответствует дате формирования документа
        xED244.EdDefineAnswerInfo.AccDocDate :=
                XMLToDateTime( VarToStr( xXML_DocPP.getAttribute('DOC_DATE')));

        // Номер платежного поручения – значение поля «Номер»
        // исправлено 2013/03/01 без задания: если номер ЭД больше 3-х
        // символов - беруться последние три символа.
        // если номер ЭД будет пустым - он не пройдет контроль валидности!
        // нужно 1...3 символа.
        s := TRIM( xED244.OriginalEpd.EDNo);
        if (LENGTH( s) <= 3)
          then xED244.EdDefineAnswerInfo.AccDocNo := s
          else xED244.EdDefineAnswerInfo.AccDocNo := COPY( s, LENGTH(s) - 2, 3);
{
    // отладка
showmessage('отладка: xED244.EdDefineAnswerInfo.AccDocNo="'
+xED244.EdDefineAnswerInfo.AccDocNo+'"');
}

        // Значение поля «Счет» блока «Плательщик»
        xED244.EdDefineAnswerInfo.PayerAcc :=
                                  VarToStr( xXML_DocPP.GetAttribute( 'PAY_ACCOUNT'));

    // отладка
    //showmessage('отладка - замена счета PAY_ACCOUNT');
    //xED244.EdDefineAnswerInfo.PayerAcc := VarToStr( xXML_DocPP.GetAttribute( 'REC_ACCOUNT'));


        // Значение поля «Счет» блока «Получатель»
        xED244.EdDefineAnswerInfo.PayeeAcc :=
                                  VarToStr( xXML_DocPP.GetAttribute( 'REC_ACCOUNT'));

        // Значение поля «ИНН» блока «Плательщик»
        xED244.EdDefineAnswerInfo.PayerInn :=
                                  VarToStr( xXML_DocPP.GetAttribute( 'PAY_TAXCODE'));

        // Значение поля «ИНН» блока «Получатель»
        xED244.EdDefineAnswerInfo.PayeeInn :=
                                  VarToStr( xXML_DocPP.GetAttribute( 'REC_TAXCODE'));

        // Значение поля «Сумма» в шапке Платежного поручения.
        xED244.EdDefineAnswerInfo.Sum :=
                          StrToCurr( VarToStr( xXML_DocPP.GetAttribute( 'AMOUNT')));

        // Значение поля «Организация» блока «Плательщик»
        xED244.EdDefineAnswerInfo.PayerLongName :=
                                  VarToStr( xXML_DocPP.GetAttribute( 'PAY_NAME'));

        // Значение поля «Организация» блока «Получатель»
        xED244.EdDefineAnswerInfo.PayeeLongName :=
                                  VarToStr( xXML_DocPP.GetAttribute( 'REC_NAME'));

        // Значение поля «Назначение платежа» вкладки «Общее» ЭД Платежное поручение
        xED244.EdDefineAnswerInfo.Purpose :=
                                  VarToStr( xXML_DocPP.GetAttribute( 'DESCRIPTION'));

        // Значение поля «Назначение платежа» - не заполнять
        // поскольку в задании указано то же поле для выгрузки что и для
        // xED244.EdDefineAnswerInfo.Purpose. при этом это поле в ED244 короче!
        // поэтому АО утвердило, что это поле не выгружается.
        //xED244.EdDefineAnswerInfo.Address := '';

        // Значение поля «ИНН» блока «Плательщика»
        xED244.EdDefineAnswerInfo.AccDocAddInfo.PayerInn :=
                                  VarToStr( xXML_DocPP.GetAttribute( 'PAY_TAXCODE'));

        // Значение поля «ИНН» блока «Получатель»
        xED244.EdDefineAnswerInfo.AccDocAddInfo.PayeeInn :=
                                  VarToStr( xXML_DocPP.GetAttribute( 'REC_TAXCODE'));

        // Значение поля «Статус лица оформившего документ» идентификатора назначения
        // платежа платежного поручения.
        // это поле должно иметь две цифры, поэтому дополняем цифры нулями слева.
        xED244.EdDefineAnswerInfo.AccDocAddInfo.DrawerStatus :=
            AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'AUTHOR_ID')), 2, '0');

        // Значение поля «КПП» блока «Плательщик»
        xED244.EdDefineAnswerInfo.AccDocAddInfo.PayerKpp :=
                                    VarToStr( xXML_DocPP.GetAttribute( 'PAY_KPP'));


        // Значение поля «КПП» блока получатель
        xED244.EdDefineAnswerInfo.AccDocAddInfo.PayeeKpp :=
                                    VarToStr( xXML_DocPP.GetAttribute( 'REC_KPP'));


        { В зависимости от значения радиобатона «Вид классификации» идентификатора
          платежа платежного поручения:
        1)В случае если классификация «Доходы» значения полей «Админ»+ «КВД» «КОСГУ»;
        2)Если классфикация «Расходы» – «КВСР» + «КФСР» + «КЦСР» + «КВР» + «КОСГУ».
        3)Если классификация «Источники» – «Админ» + «КВИ» + «КОСГУ».}
        xED244.EdDefineAnswerInfo.AccDocAddInfo.CBC :=
                              VarToStr( xXML_DocPP.GetAttribute( 'PI_BUDGET_CODE'));

    (*
        // получаем строку ЭД.
        xXml := QueryXml( 'select p.* from PAYORDERLINE p where p.payorder_id=?',
                          [ aDocPP_ID]);
        // здесь не проверяем отсутствие строки!
        xXml_DocLine := xXml.documentElement.ChildNodes.Item[0];

        case VarToStr( xXML_DocPP.GetAttribute( 'ANAL_KIND')) of
          // доходы
          0: xED244.EdDefineAnswerInfo.AccDocAddInfo.CBC :=
                AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KADMD_CODE')),  3, '0')
               +AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KD_CODE'   )), 14, '0')
               +AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KESD_CODE' )),  3, '0');
          // расходы
          1: xED244.EdDefineAnswerInfo.AccDocAddInfo.CBC :=
                AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KADMR_CODE')),  3, '0')
               +AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KFSR_CODE' )),  4, '0')
               +AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KCSR_CODE' )),  7, '0')
               +AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KVR_CODE'  )),  3, '0')
               +AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KESR_CODE' )),  3, '0');
          // источники
          2: xED244.EdDefineAnswerInfo.AccDocAddInfo.CBC :=
                AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KADMI_CODE')),  3, '0')
               +AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KI_CODE'   )), 14, '0')
               +AppendStringLeft( VarToStr( xXML_DocPP.GetAttribute( 'KESI_CODE' )),  3, '0');
        end;//case//
    *)
        // Значение поля «ОКАТО» идентификатора платежа платежного поручения.
        xED244.EdDefineAnswerInfo.AccDocAddInfo.Okato :=
                                  VarToStr( xXML_DocPP.GetAttribute( 'PAY_OKATO'));

        // Значение поля «Показатель основания платежа» идентификатора платежа
        // платежного поручения.
        s := VarToStr( xXML_DocPP.GetAttribute( 'GROUND_ID'));
        if (LENGTH( s) <= 0)
          then xED244.EdDefineAnswerInfo.AccDocAddInfo.PaytReason := ''
          else xED244.EdDefineAnswerInfo.AccDocAddInfo.PaytReason :=
                            QueryField( 'select NAME from payground where id=?',
                                        [ s],
                                        'NAME');
        (*
        // здесь проверяем, если поле не указано, выгружаем '0'.
        // если указано - выгружаем код из справочника
        s := TRIM( VarToStr( xXML_DocPP.GetAttribute( 'GROUND_ID')));
        if ( LENGTH(s) <= 0)
          then xED244.EdDefineAnswerInfo.AccDocAddInfo.PaytReason := '0'
          else xED244.EdDefineAnswerInfo.AccDocAddInfo.PaytReason := s;
                  QueryField( 'select NAME from payground where id=?', [ s], 'NAME');
                  *)

        // Значение поля «Налоговый период» идентификатора платежа платежного
        // поручения.
        { по закону 106н вот такое указание допустимо:
          1) Первые два знака показателя налогового периода предназначены для
              определения периодичности уплаты налога (сбора), установленной
              законодательством о налогах и сборах, которая указывается следующим
              образом:
          "МС" - месячные платежи;
          "КВ" - квартальные платежи;
          "ПЛ" - полугодовые платежи;
          "ГД" - годовые платежи.
          В 4-м и 5-м знаках показателя налогового периода для месячных платежей
          проставляется номер месяца текущего отчетного года, для квартальных
          платежей - номер квартала, для полугодовых - номер полугодия.
          Номер месяца может принимать значения от 01 до 12,
          номер квартала - от 01 до 04, номер полугодия - 01 или 02.
          В 3-м и 6-м знаках показателя налогового периода в качестве
          разделительных знаков проставляются точки (".").
          В 7 - 10 знаках показателя налогового периода указывается год,
          за который производится уплата налога.
          При уплате налога один раз в год 4-й и 5-й знаки показателя
          налогового периода заполняются нулями.
          Если законодательством о налогах и сборах по годовому платежу
          предусматривается более одного срока уплаты налога (сбора)
          и установлены конкретные даты уплаты налога (сбора) для каждого срока,
          то в показателе налогового периода указываются эти даты.
          Образцы заполнения показателя налогового периода:
          "МС.02.2003"; "КВ.01.2003"; "ПЛ.02.2003"; "ГД.00.2003".
        поэтому дату не проверяем}
        xED244.EdDefineAnswerInfo.AccDocAddInfo.TaxPeriod :=
                               VarToStr( xXML_DocPP.GetAttribute( 'TAXPERIOD'));
        (*
        s := TRIM( VarToStr( xXML_DocPP.GetAttribute( 'TAXPERIOD')));
        if ( LENGTH(s) <= 0)
          then xED244.EdDefineAnswerInfo.AccDocAddInfo.TaxPeriod := ''
          else xED244.EdDefineAnswerInfo.AccDocAddInfo.TaxPeriod :=
                                FormatDateTime( 'yyyy-mm-dd', XmlToDateTime( s));
                                *)

        // Значение поля «Номер документа» идентификатора назначения платежа
        // платежного поручения.
        xED244.EdDefineAnswerInfo.AccDocAddInfo.DocNo :=
                  TRIM( VarToStr( xXML_DocPP.GetAttribute( 'GRND_DOC_NUMBER')));
        // Значение поля «Дата документа» идетификатора назначения платежа
        // платежного поручения.
        // дата передается без преобразования и проверок.
    //showmessage('отладка: дата не указана');
        xED244.EdDefineAnswerInfo.AccDocAddInfo.DocDate :=
                          VarToStr( xXML_DocPP.GetAttribute( 'GRND_DOC_DATE'));
{        s := VarToStr( xXML_DocPP.GetAttribute( 'GRND_DOC_DATE'));
        if (LENGTH( s) > 0)
          then xED244.EdDefineAnswerInfo.AccDocAddInfo.DocDate :=
                                     XMLToDateTime( DDdMMdYYYY_To_YYYYmMMmDD( s));}
        (*
        s := TRIM( VarToStr( xXML_DocPP.GetAttribute( 'GRND_DOC_DATE')));
        if ( LENGTH(s) <= 0)
          then xED244.EdDefineAnswerInfo.AccDocAddInfo.DocDate := ''
          else xED244.EdDefineAnswerInfo.AccDocAddInfo.DocDate :=
                                  FormatDateTime( 'yyyy-mm-dd', XmlToDateTime( s));
                                  *)

        // Значение поля «Тип платежа» идентификатора назначения платежа
        // платежного поручения.
        s := VarToStr( xXML_DocPP.GetAttribute( 'PAYTYPE_ID'));
        if (LENGTH( s) <= 0)
          then xED244.EdDefineAnswerInfo.AccDocAddInfo.TaxPaytKind := ''
          else xED244.EdDefineAnswerInfo.AccDocAddInfo.TaxPaytKind :=
                              QueryField( 'select NAME from paytype where id=?',
                                          [ s],
                                          'NAME');

        // Номер ED 243 на который отправляется Ответ
        xED244.InitialEd.EDNo     := aED243.OriginalEpd.EDNo;
        // Дата ED 243 на который отправляется Ответ
        xED244.InitialEd.EDDate   := aED243.OriginalEpd.EDDate;
        // УИС ED 243  на который отправляется ответ.
        xED244.InitialEd.EDAuthor := aED243.OriginalEpd.EDAuthor;
        except
          ExceptionLog( 'Сбой формирования полей ED244.');
      end;

  //    MakeFileName(Xml);

  //    if xED244.SignType in [stED, stPacket] then
     { if (Ufebs.SignType <> stNone) or (FSigEnvelope and FVerifySigEnvelope) or
        (FEnvelope and Ufebs.EnvelopeCrypted) then}

      xED244.NSType[nstEd     ] := vExch_2409_v1;
      xED244.NSType[nstDsig   ] := vExch_2409_v2;
      xED244.NSType[nstDsigEnv] := vExch_2409_v3;
      xED244.NSType[nstSoap   ] := vExch_2409_v4;
      xED244.NSType[nstMsg    ] := vExch_2409_v5;
  {
      xED244.NSType[nstEd     ] := Trim(aConfig.Values['urn:cbr-ru:ed:v1.1']);
      xED244.NSType[nstDsig   ] := Trim(aConfig.Values['urn:cbr-ru:dsig:v1.1']);
      xED244.NSType[nstDsigEnv] := Trim(aConfig.Values['urn:cbr-ru:dsig:env:v1.1']);
      xED244.NSType[nstSoap   ] := Trim(aConfig.Values['http://www.w3.org/2003/05/soap-envelope']);
      xED244.NSType[nstMsg    ] := Trim(aConfig.Values['urn:cbr-ru:msg:props:v1.1']);
  }
  //    if ((xED244.SignType in [stED, stPacket]) or FSigEnvelope) then begin

      try
        WriteLog( xDocPPInfo + ' - Формирование XML.');
        xED244.MakeXML;

        if vExch_2409_FormatXml then begin
          WriteLog( xDocPPInfo + ' - Форматирование XML.');
          xED244.FormatXML;
        end;

        // формирование ЭЦП в ответе
        if vExch_2409_Sign then begin
          WriteLog('Инициализация "СКАД Сигнатура"', True);
          xED244.VCERTLocalPath := vExch_2409_VCERTLocalPath;
          xED244.VCERTInitialize;
          WriteLog( xDocPPInfo + ' - Установка ЭЦП(ЗК)');
          xED244.SignType := stED;
          xED244.Sign;
        end;
        // проверяется пакет ответа
        if NOT xED244.Validate then begin
          MoveToError( xDocPPInfo
                        + 'не сформирован. Не прошёл контроль валидности.'
                        + #13#10'Причина: ' + xED244.ValidateError.reason
                        + #13#10'XML: ' + xED244.ValidateError.srcText);
          Exit;
        end;

        // каталог результата будет с подкаталогом даты
        if NOT SaveToEx_2409( xED244,
                              xDocPPInfo,
                              FormatDateTime( 'yyyy-mm-dd\', xNOW),
                              xED244_EDNo,
                              s)
          then EXIT
          else xFileED244 := s;
        except
          ExceptionLog( 'Сбой формирования пакета ED244.');
      end;

      // формирование конверта и служебного конверта
      if (vExch_2409_SigEnvelope OR vExch_2409_Envelope) then begin
        try
          // для служебного конферта необходим объект конверта, поэтому независимо
          // от опции формирования конверта, объект конверта будем всегда создавать,
          // но если конверт выгружать не нужно - его не проверяем и не сохраняем.

          WriteLog('<Конверт ЭЦП(КА)> - Создание', True);
          // здесь формируется объект конверта - это объект из которого затем
          // будет формироваться служебный конверт
          xPacked := xED244.makeSigEnvelope;

          WriteLog('<Конверт ЭЦП(КА)> - Формирование', True);
          xPacked.makeXML;

          WriteLog('<Конверт ЭЦП(КА)> - Установка ЭЦП(КА)', True);
          xPacked.Sign;

          if vExch_2409_FormatXml then begin
            WriteLog('<Конверт ЭЦП(КА)> - Форматирование', True);
            xPacked.FormatXML;
          end;

          if vExch_2409_SigEnvelope then
            try
              WriteLog('<Конверт ЭЦП(КА)> - Контроль валидности', True);
              if not xPacked.Validate then begin
                MoveToError( '<Конверт ЭЦП(КА)> - Не прошёл контроль валидности'
                            + #13#10'Причина: ' + xPacked.ValidateError.reason
                            + #13#10'XML: ' + xPacked.ValidateError.srcText);
                EXIT;
              end;
              // каталог результата будет с подкаталогом даты
              if NOT SaveToEx_2409( xPacked,
                                    xDocPPInfo,
                                    FormatDateTime( 'yyyy-mm-dd\Конверт\', xNOW),
                                    xED244_EDNo,
                                    s)
                then EXIT
                else xFileED244_S := s;
              except
                ExceptionLog( 'Сбой проверки и сохранения конверта.');
            end;
          except
            ExceptionLog( 'Сбой формирования конверта.');
        end;

        // формирование служебного конверта
        // он формируется из объекта конверта.
        if vExch_2409_Envelope then
          try
            WriteLog('<Служебный конверт> - Создание', True);
            xPacked := xPacked.makeEnvelope;
            WriteLog('<Служебный конверт> - Формирование', True);
            xPacked.DirSchems                 := vExch_2409_DirSchems;
            xPacked.MessageInfo.SendTo        := vExch_2409_EnvelopeTo;
            xPacked.MessageInfo.SendFrom      := vExch_2409_EnvelopeFrom;
            xPacked.MessageInfo.MessageType   := vExch_2409_EnvelopeMessageType;
            xPacked.MessageInfo.Priority      := vExch_2409_EnvelopePriority;

            //DocInfo
            xPacked.DocInfo.DocFormat := 1; //Всегда 1 - УФЭБС
      {      if not FOneED then
              Ufebs.DocInfo.DocType := 'PacketEPD';}
            xPacked.DocInfo.EDRefID.EDNo := xED244.EDNo;
            xPacked.DocInfo.EDRefID.EDDate := xNOW;
            xPacked.DocInfo.EDRefID.EDAuthor := vExch_2409_UIS;

            if vExch_2409_EnvelopeTransportFileName
              then xPacked.MessageInfo.LegacyTransportFileName := ExtractFileName(FileIn);//ExtractFileName(FileOut);
            if vExch_2409_EnvelopeMessageID
              then xPacked.MessageInfo.MessageID := 'guid:' + xPacked.genGuid;
            if vExch_2409_EnvelopeAppMessageID
              then xPacked.MessageInfo.AppMessageID := 'guid:' + xPacked.genGuid;

            xPacked.makeXML;
            if vExch_2409_FormatXml then begin
              WriteLog('<Служебный конверт> - Форматирование', True);
              xPacked.FormatXML;
            end;
            WriteLog('<Служебный конверт> - Контроль валидности', True);

  //showmessage('отладка - xPacked.Validate');
  //xPacked.SaveToFile('c:\temp\debug_exch2409_file1.xml');
  {SL:= tstringlist.create;
  sl.text:= xPacked.xml;
  sl.savetofile('c:\temp\xPacked.xml');
  sl.free;}

            if not xPacked.Validate then begin
      //        with xPacked.ValidateError do
              MoveToError(
                    '<Служебный конверт> - Не прошёл контроль валидности'
                  + #13#10'Причина: ' + xPacked.ValidateError.reason
                  + #13#10'XML: ' + xPacked.ValidateError.srcText);
              EXIT;
            end;
            // каталог результата будет с подкаталогом даты
            if NOT SaveToEx_2409( xPacked,
                                  xDocPPInfo,
                                  FormatDateTime( 'yyyy-mm-dd\Служебный_Конверт\', xNOW),
                                  xED244_EDNo,
                                  s)
              then EXIT
              else xFileED244_M := s;
            except
              ExceptionLog( 'Сбой формирования служебного конверта.');
          end;
      end;

      // результат создан успешно. конечные файлы удалять не нужно!
      xFileED244   := '';
      xFileED244_S := '';
      xFileED244_M := '';
      finally
        // если выход из процедуры выполнен досрочно (т.е. с ошибками)
        // то все созданные файлы ответов удаляем (те которые создались успешно)
        if ( LENGTH( xFileED244  ) > 0) then DeleteFile( xFileED244);
        if ( LENGTH( xFileED244_S) > 0) then DeleteFile( xFileED244_S);
        if ( LENGTH( xFileED244_M) > 0) then DeleteFile( xFileED244_M);
    end;
    {
    // каталог результата будет с подкаталогом даты
    s := vExch_2409_DirOut + FormatDateTime( 'yyyy-mm-dd\', xNOW);
    ForceDirectories( s);
    if not DirectoryExists( s) then begin
      MoveToError(
                    + 'не сформирован.'
                    + FORMAT( #13#10'Ошибка при создании каталога результата: "%s".',
                              [ s]));
      EXIT;
    end;

    // имя файла будет соответствовать входному файлу
    FileOut := s + ExtractFileName(FileIn);
//showmessage('отладка: FileOut="'+FileOut+'".');
    xED244.SaveToFile( FileOut);
    }
    except
      ExceptionLog( 'Сбой Un_Exch2409.Exch2409_ConvertED243_To_ED244');
  end;
end;


begin
end.
