unit StatusUpdater;

interface

uses
  Forms, SysUtils, ComObj, Dialogs, Classes, DB, ADODB, ComCtrls, StdCtrls,
  Buttons, UtilUnit, System.UITypes, System.AnsiStrings, Winapi.ActiveX;

type
  EAuthException = class(Exception);

  EConnectException = class(Exception);

type
  TConfig = record
    ProxyIp: string;
    ProxySocket: string;
    ProxyLogin: string;
    ProxyPassword: string;
  end;

  TThreadStatus = class(TThread)
  private
    Progress, Count, Error: integer;
    Name, Status, StatusMessage, InvName, ErrorMessageText: string;
    EVatService, InvVatStatus: OleVariant;
    Data: TADOQuery;
    ProgressBar: TProgressBar;
    Memo: TMemo;
    Tab: TTabSheet;
    Config: TConfig;
    procedure SetProgress;
    procedure WriteStatus;
    procedure MessageSend;
    procedure ErrorMessage;
    procedure CreateQuery;
    procedure WriteMessageToMemo(name, status, status_message: string);
  protected
    procedure Execute; override;
  public
    constructor Create(CreateSuspended: Boolean; Data: TADOQuery; Progress:
      TProgressBar; Tab: TTabSheet; Memo: TMemo; Config: TConfig); overload;
  end;

implementation

var
  UpdateQuery: TADOQuery;

constructor TThreadStatus.Create(CreateSuspended: Boolean; Data: TADOQuery;
  Progress: TProgressBar; Tab: TTabSheet; Memo: TMemo; Config: TConfig);
begin
  inherited Create(CreateSuspended);

  Self.Data := Data;
  Self.ProgressBar := Progress;
  Self.Memo := Memo;
  Self.Tab := Tab;
  Self.Config := Config;

  CreateQuery;
end;

procedure TThreadStatus.CreateQuery;
begin
  //Создание Query для обновления статусов
  UpdateQuery := TADOQuery.Create(Application);
  UpdateQuery.Connection := Self.Data.Connection;
  UpdateQuery.Parameters.CreateParameter('invsend', ftSmallint, pdInput, 2, 0);
  UpdateQuery.Parameters.CreateParameter('invyear', ftSmallint, pdInput, 2, 0);
  UpdateQuery.Parameters.CreateParameter('invnumb', ftLargeint, pdInput, 8, 0);
  UpdateQuery.Parameters.CreateParameter('invstat', ftWord, pdInput, 1, 0);
  UpdateQuery.Parameters.CreateParameter('invstatdate', ftDateTime, pdInput, -1, 0);
  UpdateQuery.SQL.Add('UPDATE history SET invStatus=:invstat, StatusDate=:invstatdate');
  UpdateQuery.SQL.Add('WHERE (idSender=:invsend) and (compilingYear=:invyear) and (number=:invnumb)')
end;

function GetStatusId(Status: string): Integer; //Получение статуса по номеру
begin
  Result := IndexStr(Status, ['IN_PROGRESS', 'IN_PROGRESS_ERROR', 'COMPLETED',
    'ON_AGREEMENT', 'COMPLETED_SIGNED', 'CANCELLED', 'ON_AGREEMENT_CANCEL', 'NOT_FOUND', 'ERROR']) + 1;
end;

procedure TThreadStatus.SetProgress;
begin
  ProgressBar.Position := Progress;
end;

procedure TThreadStatus.WriteStatus;
begin
  Memo.Lines.Add(Name + ' - ' + Status + ' - ' + StatusMessage);
end;

procedure TThreadStatus.WriteMessageToMemo(name, status, status_message: string);
begin
  if Self.Memo <> nil then
  begin
    Self.Name := name;
    Self.Status := status;
    Self.StatusMessage := status_message;
    Synchronize(WriteStatus);
  end;
end;

procedure TThreadStatus.ErrorMessage;
begin
  MessageDlg(ErrorMessageText, mtInformation, [mbOK], 0);
end;

procedure TThreadStatus.MessageSend;
begin
  MessageDlg('Операция завершена успешно. Статусов получено: ' + IntToStr(Count
    - Error) + ' из ' + IntToStr(Count), mtInformation, [mbOK], 0)
end;

procedure TThreadStatus.Execute; //Получение статусов с портала через EInvVatService
var
  res, err: Integer;
begin
  err := 0;
  Tab.Enabled := False;

  try
    EVatService := CreateOleObject('EInvVatService.Connector');
    if EVatService.Login['', 0] <> 0 then
      raise EAuthException.Create('Не удалось авторизироваться на портале.');

    if (Length(Config.ProxyIp) > 0) and (Length(Config.ProxySocket) > 0) then
      EVatService.SetProxy[Config.ProxyIp, Config.ProxySocket, Config.ProxyLogin, Config.ProxyPassword, '1'];

    if EVatService.Connect['https://ws.vat.gov.by/InvoicesWS/services/InvoicesPort'] <> 0 then
      raise EConnectException.Create('Не удалось подключится к порталу.');

    Data.DisableControls;
    Data.First;

    while not Data.Eof do
    begin
      Progress := 100 * Data.RecNo div Data.RecordCount;
      Synchronize(Setprogress);

      if (Data.FieldByName('invStatus').AsInteger in [6, 7]) then
      begin
        Data.Next;
        Continue;
      end;

      InvName := Data.FieldByName('senderUNP').AsString + '-' + Data.FieldByName
        ('compilingYear').AsString + '-' + IntToStrWithZeros(Data.FieldByName('number').AsVariant, 10);

      InvVatStatus := EVatService.GetStatus[InvName];

      try
        res := InvVatStatus.Verify;

        if res <> 0 then
        begin
          ErrorMessageText := EVatService.LastError;
          Synchronize(ErrorMessage);
          Break;
        end
        else
        begin
          WriteMessageToMemo(InvName, InvVatStatus.status, InvVatStatus.Message);

          with UpdateQuery do
          begin
            Parameters.ParamByName('invsend').Value := Data.FieldByName('idSender').AsInteger;
            Parameters.ParamByName('invyear').Value := Data.FieldByName('compilingYear').AsInteger;
            Parameters.ParamByName('invnumb').Value := Data.FieldByName('number').AsLargeInt;
            Parameters.ParamByName('invstat').Value := GetStatusId(InvVatStatus.status);
            Parameters.ParamByName('invstatdate').Value := Now();
            ExecSQL;
          end;
        end;
      except
        on EAccessViolation do
        begin
          Inc(err);
          WriteMessageToMemo(InvName, Data.FieldByName('idSender').AsString, 'Ошибка получения статуса');
        end;
      end;

      Data.Next;
    end;

    Error := err;
    Count := Data.RecordCount;
    Synchronize(MessageSend);
  except
    on e: Exception do
    begin
      ErrorMessageText := e.Message;
      Synchronize(ErrorMessage);
    end;
  end;

  Data.EnableControls;
  Tab.Enabled := True;
  UpdateQuery.Free;
end;

end.

