unit ProjectCommands;

interface

uses
  ProjectData, XMLDoc, XMLIntf, ProjectManagerDm, sdIDTree, sdDB,
  Connections;

type
  {1. 生成临时文件夹
   2. 打开项目
   3. 修改项目（上报、批复相应修改）
   4. 保存修改后项目的属性至xml文件
   5. 保存项目至临时文件夹中
   6. 压缩临时文件夹，得到上报、批复文件
   7. 复制粘贴结果文件得用户选择
   8. 删除临时文件夹}
  TReportBase = class
  private
    // 临时文件夹（生成的项目文件、xml属性文件）
    FTempFolder: string;
    FProjectName: string;
    FFileName: string;
    FProjectID: Integer;
    FProjectData: TProjectData;
    FFileType: string;
    // xml项目信息文件
    FXmlInfoFile: string;
    // 结果文件
    FResultFile: string;
    // 接收文件时是否需要升级文件
    FNeedUpdate: Boolean;

    procedure SaveProjectInfo(AXmlNode: IXMLNode);
    procedure SaveTenderProperty(AXmlNode: IXMLNode);
    procedure SaveInfoToXmlDocument(AXmlNode: IXMLNode);
    procedure ExportXmlInfo;
    function GetNewOutputFileName: string;

    function CheckFile(const AFileName: string): Boolean;
  protected
    procedure OpenProject; virtual;
    procedure SaveProject; virtual;
  public
    constructor Create(const AProjectName, AFileName: string; AProjectID: Integer);
    destructor Destroy; override;

    function Execute: Boolean; virtual;
    function ExportTo(AFileName: string): Boolean; virtual;
  end;

  TSubmitProject = class(TReportBase)
  protected
    procedure OpenProject; override;
    procedure SaveProject; override;
  public
    function Execute: Boolean; override;
    function ExportTo(AFileName: string): Boolean; override;
  end;

  TReplyProject = class(TReportBase)
  protected
    procedure OpenProject; override;
    procedure SaveProject; override;
  public
    function Execute: Boolean; override;
    function ExportTo(AFileName: string): Boolean; override;
  end;

  {1. 生成临时文件夹
   2. 解压选中文件至临时文件夹
   3. 读取xml信息文件中的数据
   4. 检查同名项目，存在则备份原项目，不存在则创建新节点
   5. 根据xml文件更新数据项目信息，标段属性
   6. 从临时文件夹中复制粘贴项目文件至新节点的数据文件
   7. 打开该数据文件，根据情况修改
   8. 保存该数据文件
   9. 删除临时文件夹}
  TReceiveProject = class
  private
    FTempFolder: string;
    FFileType: string;
    FXmlDocument: IXMLDocument;
    // 导入后的节点
    FCurNode: TsdIDTreeNode;
    FProjectName: string;
    FFileName: string;
    FSelect: TsdIDTreeNode;
    FProjectData: TProjectData;
    // 初次在线审批人，自上次Exe上报/审批
    FStartAudit: Integer;
    // 末次在线审批人，自上次Exe上报/审批
    FEndAudit: Integer;
    // 文件中的审核状态
    FOrgAuditStatus: Integer;
    // 应升级至的审核状态
    FNewAuditStatus: Integer;

    FStageCount: Integer;
    FReply: Boolean;
    FLostStageCount: Integer;
    // 控制是否需要升级更新文件
    FNeedUpdate: Boolean;
    FLock: Boolean;
    FMessageID: Integer;
    FIsReback: Boolean;
    FWebBidName: string;

    function CheckOpen: Boolean;
    function GetNewTender: TsdIDTreeNode;
    procedure GetCurNode;
    procedure LoadProjectInfo(AXmlNode: IXMLNode);
    procedure LoadTenderProperty(AXmlNode: IXMLNode);
    procedure LoadInfoFromXmlDocument;
    function CheckUpdateStatusValid: Boolean;
    procedure ReadXmlInfo;
    function UpdateData(const AFileName: string): Boolean;
    procedure SetWebBidName(const Value: string);
  public
    constructor Create(ASelect: TsdIDTreeNode);
    destructor Destroy; override;

    function Receive(const AFileName: string): TsdIDTreeNode;
    // 在线审批使用
    // 原报、1审(Online)、2审(Online)、3审(Online)、4审、5审(Online)、6审、7审
    // 则4审在Exe端打开项目时，AStartAudit=1，AEndAudit=3
    //   6审在Exe端打开项目时，AStartAudit=AEndAudit=5
    //   7审在Exe端打开项目时，AStartAudit=AEndAudit=0
    function ReceiveForLost(const AFileName: string; AStartAudit, AEndAudit: Integer; AReply: Boolean = False): TsdIDTreeNode;
    // 供网络版使用
    // 原报已提交，1审已审核并提交后，此时除了2审人以外的参与人接收数据时不可生成二审数据
    property Lock: Boolean read FLock write FLock;
    // 被打回的数据
    property IsReback: Boolean read FIsReback write FIsReback;
    // 信息: 0[接收成功]
    //       1[接收失败，应接收节点打开状态]
    //       2[接收失败，下载文件跟网页状态不一致]
    //       3[接收失败，升级文件失败]
    property MessageID: Integer read FMessageID;
    // ProjectName 是标段名。之前是读自文件包中的XML文件属性，现在要从线上取。
    property WebBidName: string read FWebBidName write SetWebBidName;
  end;

  {1. 生成临时文件夹
   2. 保存标段的属性至xml文件
   3. 复制粘贴标段文件至临时文件夹
   4. 压缩临时文件夹，得到导出的标段文件（结果文件）
   5. 复制粘贴结果文件得用户选择
   6. 删除临时文件夹}
  TTenderExport = class
  private
    // 临时文件夹（生成的项目文件、xml属性文件）
    FTempFolder: string;
    FProjectName: string;
    FFileName: string;
    FProjectID: Integer;
    FFileType: string;
    // xml项目信息文件
    FXmlInfoFile: string;
    // 结果文件
    FResultFile: string;
    FAttachmentPath: string;

    procedure SaveProjectInfo(AXmlNode: IXMLNode);
    procedure SaveTenderProperty(AXmlNode: IXMLNode);
    procedure SaveInfoToXmlDocument(AXmlNode: IXMLNode);
    procedure ExportXmlInfo;
  public
    constructor Create(ARec: TsdDataRecord; const AFileName: string);
    destructor Destroy; override;
    // 参数AExportAttachment：是否导出附件
    procedure Execute(AExportAttachment: Boolean = False);
  end;

  TTenderImport = class
  private
    FTempFolder: string;
    FSelect: TsdIDTreeNode;
    FNewNode: TsdIDTreeNode;
    FProjectName: string;
    FFileName: string;
    FTenderFileName: string;

    procedure GetNewNode;

    procedure LoadProjectInfo(AXmlNode: IXMLNode);
    procedure LoadTenderProperty(AXmlNode: IXMLNode);
    procedure LoadInfoFromXmlDocument(AXmlNode: IXMLNode);
    procedure LoadXmlInfo;
  public
    constructor Create(ASelect: TsdIDTreeNode; const AProjectName, AFileName: string);
    destructor Destroy; override;

    procedure Execute;
    procedure ImportToSelect;
  end;

  {1. 生成临时文件夹
   2. 导出选中节点及其所有子节点信息
   3. 递归所有子节点，分类（Type = 0）则新建同名文件夹，标段（Type = 1）则导出该标段
   4. 压缩临时文件夹，得到导出的分类文件（结果文件）
   5. 复制粘贴结果文件得用户选择
   6. 删除临时文件夹}
  TProjectExport = class
  private
    // 临时文件夹（按层次结构生成所有项目文件）
    FTempFolder: string;
    // xml树结构文件
    FXmlInfoFile: string;
    // 结果文件
    FResultFile: string;
    FExportNode: TsdIDTreeNode;
    FTenderExportor: TTenderExport;
    FExportAttachment: Boolean;

    procedure SaveTreeNodeInfo(AXmlNode: IXMLNode; ANode: TsdIDTreeNode);
    procedure ExportXmlInfo;

    procedure ExportTender(ANode: TsdIDTreeNode; const AExportPath: string);
    procedure ExportNode(ANode: TsdIDTreeNode; const AExportPath: string);
  public
    constructor Create(ANode: TsdIDTreeNode; const AFileName: string; AExportAttachment: Boolean = False);
    destructor Destroy; override;

    procedure Execute;
  end;

  {1. 生成临时文件夹
   2. 解压选中文件至临时文件夹
   3. 根据用户选择新建子分类FNew
   3. 打开Xml树结构信息文件
   4. 根据Xml文件中结构，依次建立分类或导入标段文件
   5. 删除临时文件夹}
  TProjectImport = class
  private
    FTempFolder: string;
    FProjectName: string;
    FFileName: string;
    FSelect: TsdIDTreeNode;
    FNew: TsdIDTreeNode;

    function AddNewNode(AParentID, ANextID: Integer; const ANodeName: string): TsdIDTreeNode;
    procedure LoadTender(AXmlNode: IXMLNode;
      AParent: TsdIDTreeNode; const APath: string);
    procedure LoadChild(AXmlNode: IXMLNode;
      AParent: TsdIDTreeNode; const APath: string);
    procedure LoadChildrenFormXml;
  public
    constructor Create(ASelect: TsdIDTreeNode; const AProjectName, AFileName: string);
    destructor Destroy; override;

    procedure Execute;
  end;

  // 文件检验
  {
    检验分为两部分：
    1. 一致性检验：检验文件中的xml记录数据，与数据库中数据是否一致，以检验文件是否可用
    2. 主动检验：  提供xml记录的部分数据，用户主动检验是否为所需文件

    流程：
    1. 解压文件，得到xml文件，项目文件
    2. 读取xml文件，得到主要数据，与项目文件名
    3. 读取项目文件，并检验基本数据是否与xml文件中描述一致
    PS：用于主动检验的数据，在第2步中同步进行。要求，如果进行主动检验，必须先通过一致性检验。
  }
  TTenderFileChecker = class
  private
    FFileName: string;
    FTempFolder: string;
    FTenderFileName: string;
    FTenderTempFolder: string;

    FValid: Boolean;

    FPhaseCount: Integer;
    FAuditStatus: Integer;
    FProjectName: string;

    procedure LoadProjectInfo(AXmlNode: IXMLNode);
    procedure LoadInfoFromXmlDocument(AXmlNode: IXMLNode);
    procedure LoadInfoFromXml;
    procedure LoadInfo;

    function DecryptFile(const AFileName: string): Boolean;
    function CheckFileMatchXml: Boolean;
  public
    // 检验项目数据库数据与xml文件中主要数据是否吻合
    function CheckFileValid(const AFileName: string): Boolean;

    // xml文件中记录的信息，用于主动检验
    property PhaseCount: Integer read FPhaseCount;
    property AuditStatus: Integer read FAuditStatus;
  end;

  TTenderZipCheckType = (zctAllPhase, zctLastPhase);

  // 文件检验
  TTenderZipCompleteChecker = class
  private
    FFileName: string;
    FTempFolder: string;
    FProjectData: TProjectData;
    FCheckType: TTenderZipCheckType;

    function CheckMain: Boolean;
    function CheckAllPhase: Boolean;
    function CheckLastPhase: Boolean;
  public
    constructor Create;
    destructor Destroy; override;

    function CheckFileValid(const AFileName: string): Boolean;

    property CheckType: TTenderZipCheckType read FCheckType write FCheckType;
  end;

implementation

uses
  UtilMethods, ZhAPI, Globals, SysUtils, SupportUnit, NewProjectFrm,
  PhaseData, ConfigDoc, ConstUnit, PHPWebDm;

{ TSubmitProject }

function TSubmitProject.Execute: Boolean;
begin
  FFileType := '.rmf';
  FNeedUpdate := True;
  //FProjectData.LockedDataForSubmit;
  Result := inherited Execute;
end;

function TSubmitProject.ExportTo(AFileName: string): Boolean;
begin
  FNeedUpdate := True;
  //FProjectData.LockedDataForSubmit;
  Result := inherited ExportTo(AFileName);
end;

procedure TSubmitProject.OpenProject;
begin
  FProjectData.OpenForSubmit(FFileName);
end;

procedure TSubmitProject.SaveProject;
begin
  FProjectData.SaveAsForSubmit(FTempFolder + '\' + ExtractSimpleFileName(FFileName));
end;

{ TReportBase }

constructor TReportBase.Create(const AProjectName, AFileName: string;
  AProjectID: Integer);
begin
  FProjectName := AProjectName;
  FFileName := AFileName;
  FProjectID := AProjectID;
  FProjectData := TProjectData.Create;
  FFileType := '.dat';
  FTempFolder := GenerateTempFolder(GetTempFilePath);
  FXmlInfoFile := FTempFolder + '\Info.xml';
  FResultFile := GetTempFileName;
  FNeedUpdate := False;
  OpenProject;
end;

destructor TReportBase.Destroy;
begin
  DeleteFileOrFolder(FTempFolder);
  DeleteFileOrFolder(FResultFile);
  FProjectData.Free;
  inherited;
end;

function TReportBase.Execute: Boolean;
var
  sFileName: string;
begin
  Result := False;
  ExportXmlInfo;
  SaveProject;
  ZipFolder(FTempFolder, FResultFile);
  // 检验导出文件
  if CheckFile(FResultFile) then
  begin
    Result := True;
    sFileName := GetNewOutputFileName;
    if SaveFile(sFileName, FFileType) then
      CopyFileOrFolder(FResultFile, sFileName);
  end;
end;

function TReportBase.ExportTo(AFileName: string): Boolean;
begin
  Result := False;
  ExportXmlInfo;
  FProjectData.SimpleSaveAs(FTempFolder + '\' + ExtractSimpleFileName(FFileName));
  ZipFolder(FTempFolder, FResultFile);
  if CheckFile(FResultFile) then
  begin
    Result := True;
    CopyFileOrFolder(FResultFile, AFileName);
  end;
end;

procedure TReportBase.ExportXmlInfo;
var
  FXmlDocument: IXMLDocument;
begin
  FXmlDocument := TXMLDocument.Create(nil) as IXMLDocument;
  try
    FXmlDocument.Options := [doNodeAutoCreate,doNodeAutoIndent,doAttrNull,doAutoPrefix,doNamespaceDecl];
    FXmlDocument.Active := True;
    FXmlDocument.Encoding := 'GB2312';
    FXmlDocument.AddChild('ProjectMangerInfo');
    SaveInfoToXmlDocument(FXmlDocument.DocumentElement);
    FXmlDocument.SaveToFile(FXmlInfoFile);
  finally
    FXmlDocument := nil;
  end;
end;

function TReportBase.GetNewOutputFileName: string;
begin
  Result := SupportManager.ConfigInfo.AuditPath + FProjectName
      + Format('-第%d期', [FProjectData.ProjProperties.PhaseCount]);
  if FProjectData.ProjProperties.AuditCompany <> '' then
    Result := Result + '-' + FProjectData.ProjProperties.AuditCompany;
  Result := Result + Format('-%s-', [FProjectData.ProjProperties.AuditStatusStr])
      + FormatDateTime('yyyy-mm-dd', Date) + FFileType;
end;

procedure TReportBase.OpenProject;
begin
  FProjectData.SimpleOpen(FFileName);
end;

procedure TReportBase.SaveProject;
begin
  FProjectData.SimpleSaveAs(FTempFolder + '\' + ExtractSimpleFileName(FFileName));
end;

procedure TReportBase.SaveInfoToXmlDocument(AXmlNode: IXMLNode);
begin
  SaveProjectInfo(AXmlNode);
  SaveTenderProperty(AXmlNode);
end;

procedure TReportBase.SaveProjectInfo(AXmlNode: IXMLNode);
var
  vInfo: IXMLNode;
  InfoRec: TsdDataRecord;
begin
  vInfo := AXmlNode.AddChild('ProjectInfo');
  InfoRec := ProjectManager.sddProjectsInfo.FindKey('idxID', FProjectID);
  vInfo.Attributes['ProjectName'] := FProjectName;
  vInfo.Attributes['NeedUpdate'] := FNeedUpdate;
  vInfo.Attributes['DealTotalPrice'] := InfoRec.ValueByName('DealTotalPrice').AsFloat;
  vInfo.Attributes['PhaseTotalPrice'] := InfoRec.ValueByName('PhaseTotalPrice').AsFloat;
  vInfo.Attributes['EndDealTotalPrice'] := InfoRec.ValueByName('EndDealTotalPrice').AsFloat;
  vInfo.Attributes['EndChangeTotalPrice'] := InfoRec.ValueByName('EndChangeTotalPrice').AsFloat;
  vInfo.Attributes['EndTotalPrice'] := InfoRec.ValueByName('EndTotalPrice').AsFloat;
  vInfo.Attributes['PreTotalPrice'] := InfoRec.ValueByName('PreTotalPrice').AsFloat;
  vInfo.Attributes['PhasePay'] := InfoRec.ValueByName('PhasePay').AsFloat;
  vInfo.Attributes['Deal_BGLTotalPrice'] := InfoRec.ValueByName('Deal_BGLTotalPrice').AsFloat;
  vInfo.Attributes['PhaseCount'] := FProjectData.ProjProperties.PhaseCount;
  vInfo.Attributes['AuditStatus'] := FProjectData.ProjProperties.AuditStatus;
  vInfo.Attributes['FileName'] := ExtractSimpleFileName(FFileName);
  vInfo.Attributes['CommonDigit'] := InfoRec.ValueByName('CommonDigit').AsInteger;
  vInfo.Attributes['DealPayDigit'] := InfoRec.ValueByName('DealPayDigit').AsInteger;

  if G_IsCloud then
  begin
    vInfo.Attributes['WebID'] := InfoRec.ValueByName('WebID').AsInteger;
    vInfo.Attributes['WebOwnerID'] := InfoRec.ValueByName('WebOwnerID').AsInteger;
    vInfo.Attributes['WebAuthorID'] := InfoRec.ValueByName('WebAuthorID').AsInteger;
  end;
end;

procedure TReportBase.SaveTenderProperty(AXmlNode: IXMLNode);
var
  vProp: IXMLNode;
begin
  vProp := AXmlNode.AddChild('TenderProperty');
end;

function TReportBase.CheckFile(const AFileName: string): Boolean;
var
  vChecker: TTenderFileChecker;
begin
  Result := False;
  vChecker := TTenderFileChecker.Create;
  try
    Result := vChecker.CheckFileValid(AFileName);
  finally
    vChecker.Free;
  end;
end;

{ TReplyProject }

function TReplyProject.Execute: Boolean;
begin
  FFileType := '.arf';
  FProjectData.LockedDataForReply;
  Result := inherited Execute;
end;

function TReplyProject.ExportTo(AFileName: string): Boolean;
begin
  FProjectData.LockedDataForReply;
  Result := inherited ExportTo(AFileName);
end;

procedure TReplyProject.OpenProject;
begin
  FProjectData.OpenForReply(FFileName);
end;

procedure TReplyProject.SaveProject;
begin
  FProjectData.SaveForReply(FTempFolder + '\' + ExtractSimpleFileName(FFileName));
end;

{ TReceiveProject }

function TReceiveProject.CheckUpdateStatusValid: Boolean;

  procedure GetCurAuditStatus;
  begin
    {if FStageCount > 0 then
      FLostStageCount := FStageCount - (AXmlNode.Attributes['AuditStatus'] + 1) - 1;

    if FNeedUpdate then
    begin
      if FReply then
        Result := -1
      else if FStageCount > 0 then
        Result := FStageCount - 1
      else
        Result := AXmlNode.Attributes['AuditStatus'] + 1
    end
    else
      Result := AXmlNode.Attributes['AuditStatus'];}
  end;
var
  ChildNodes: IXMLNodeList;
  vProjInfo, vXmlElement: IXMLNode;
begin
  Result := True;
  vXmlElement := FXmlDocument.DocumentElement;
  ChildNodes := vXmlElement.ChildNodes;
  vProjInfo := ChildNodes.FindNode('ProjectInfo');
                                                  
  FFileName := vProjInfo.Attributes['FileName'];
  FNeedUpdate := vProjInfo.Attributes['NeedUpdate'];
  FOrgAuditStatus := vProjInfo.Attributes['AuditStatus'];
  if not FIsReback and FNeedUpdate then
  begin
    if FStartAudit <> 0 then
    begin
      if (FStartAudit <> FOrgAuditStatus + 1) or (FEndAudit < FStartAudit) then
        Result := False
      else
        FLostStageCount := FEndAudit - FStartAudit + 1 + 1;
    end
    else
      FLostStageCount := 1;

    // 锁定的情况下
    if FLock then
      FLostStageCount := FLostStageCount - 1;

    if FReply then
      FNewAuditStatus := -1
    else
      FNewAuditStatus := FOrgAuditStatus + FLostStageCount;
  end
  else
    FNewAuditStatus := FOrgAuditStatus;
end;

function TReceiveProject.CheckOpen: Boolean;
begin
  Result := OpenProjectManager.ProjectIndex(FCurNode.ID) <> -1;
  if Result then
    FMessageID := 1;
end;

constructor TReceiveProject.Create(ASelect: TsdIDTreeNode);
begin
  FSelect := ASelect;
  FTempFolder := GenerateTempFolder(GetTempFilePath);
  FReply := False;
  FLock := False;
  FMessageID := 0;
  FIsReback := False;

  FXmlDocument := TXMLDocument.Create(nil) as IXMLDocument;
end;

destructor TReceiveProject.Destroy;
begin
  FXmlDocument := nil;
  DeleteFileOrFolder(FTempFolder);
  inherited;
end;

procedure TReceiveProject.GetCurNode;
var
  I: Integer;
begin
  if FSelect.Rec.ValueByName('Type').AsInteger = 0 then
    FCurNode := FSelect.FirstChild
  else
    FCurNode := FSelect.Parent.FirstChild;
  while Assigned(FCurNode) and (FCurNode.Rec.ValueByName('Name').AsString <> FProjectName) do
    FCurNode := FCurNode.NextSibling;
  if not Assigned(FCurNode) then
    FCurNode := GetNewTender
  else if CheckOpen then Abort;
end;

function TReceiveProject.GetNewTender: TsdIDTreeNode;
begin
  if FSelect.Rec.ValueByName('Type').AsInteger = 0 then
    Result := ProjectManager.ProjectsTree.Add(FSelect.ID, -1)
  else
    Result := ProjectManager.ProjectsTree.Add(FSelect.ParentID, FSelect.NextSiblingID);
  Result.Rec.ValueByName('Name').AsString := FProjectName;
  Result.Rec.ValueByName('Type').AsInteger := 1;
  Result.Rec.ValueByName('FileName').AsString := ExtractSimpleFileName(GetNewGUIDFileName(GetMyProjectsFilePath));
end;

procedure TReceiveProject.LoadInfoFromXmlDocument;
var
  ChildNodes: IXMLNodeList;
  vProjInfo, vXmlElement: IXMLNode;
begin
  vXmlElement := FXmlDocument.DocumentElement;
  ChildNodes := vXmlElement.ChildNodes;
  vProjInfo := ChildNodes.FindNode('ProjectInfo');
  if (G_IsCloud and (FWebBidName <> '')) then
    FProjectName := FWebBidName
  else
    FProjectName := vProjInfo.Attributes['ProjectName'];
  
  GetCurNode;
  // 备份旧文件
  // 备份须放在这里的原因是，需要打开xml文件后，才能确认是否要备份文件以及应备份哪个文件
  // 但是又不能再导入xml数据以后，导入xml数据后，项目的属性会变掉
  ProjectManager.RestoreTender(FCurNode.ID);
  LoadProjectInfo(ChildNodes.FindNode('ProjectInfo'));
  LoadTenderProperty(ChildNodes.FindNode('TenderProperty'));
end;

procedure TReceiveProject.LoadProjectInfo(AXmlNode: IXMLNode);
begin
  FCurNode.Rec.ValueByName('DealTotalPrice').AsFloat := AXmlNode.Attributes['DealTotalPrice'];
  FCurNode.Rec.ValueByName('PhaseTotalPrice').AsFloat := AXmlNode.Attributes['PhaseTotalPrice'];
  FCurNode.Rec.ValueByName('EndDealTotalPrice').AsFloat := AXmlNode.Attributes['EndDealTotalPrice'];
  FCurNode.Rec.ValueByName('EndChangeTotalPrice').AsFloat := AXmlNode.Attributes['EndChangeTotalPrice'];
  FCurNode.Rec.ValueByName('EndTotalPrice').AsFloat := AXmlNode.Attributes['EndTotalPrice'];
  FCurNode.Rec.ValueByName('PreTotalPrice').AsFloat := AXmlNode.Attributes['PreTotalPrice'];
  FCurNode.Rec.ValueByName('PhasePay').AsFloat := AXmlNode.Attributes['PhasePay'];
  if AXmlNode.HasAttribute('Deal_BGLTotalPrice') then
    FCurNode.Rec.ValueByName('Deal_BGLTotalPrice').AsFloat := AXmlNode.Attributes['Deal_BGLTotalPrice'];
  FCurNode.Rec.ValueByName('PhaseCount').AsInteger := AXmlNode.Attributes['PhaseCount'];
  FCurNode.Rec.ValueByName('AuditStatus').AsInteger := FNewAuditStatus;
  FCurNode.Rec.ValueByName('CreateDate').AsString := FormatDateTime('yyyy-mm-dd', Date);
  FCurNode.Rec.ValueByName('CommonDigit').AsInteger := StrToIntDef(AXmlNode.Attributes['CommonDigit'], 0);
  FCurNode.Rec.ValueByName('DealPayDigit').AsInteger := StrToIntDef(AXmlNode.Attributes['DealPayDigit'], 0);

  if G_IsCloud then
  begin
    FCurNode.Rec.ValueByName('WebID').AsInteger := AXmlNode.Attributes['WebID'];
    FCurNode.Rec.ValueByName('WebOwnerID').AsInteger := AXmlNode.Attributes['WebOwnerID'];
    FCurNode.Rec.ValueByName('WebAuthorID').AsInteger := AXmlNode.Attributes['WebAuthorID'];
  end;

  ProjectManager.CalculateParentInfo(FCurNode.ParentID);
end;

procedure TReceiveProject.LoadTenderProperty(AXmlNode: IXMLNode);
begin
  // ToDo
end;

procedure TReceiveProject.ReadXmlInfo;
begin
  FXmlDocument.LoadFromFile(FTempFolder + '\Info.xml');
  FXmlDocument.Options := [doNodeAutoCreate,doNodeAutoIndent,doAutoPrefix,doNamespaceDecl];
end;

function TReceiveProject.Receive(const AFileName: string): TsdIDTreeNode;
begin
  Result := nil;
  FFileType := ExtractFileExt(AFileName);
  UnZipFile(AFileName, FTempFolder);
  ReadXmlInfo;
  if CheckUpdateStatusValid then
  begin
    if UpdateData(FTempFolder + '\' + FFileName) then
    begin
      LoadInfoFromXmlDocument;
      CopyFileOrFolder(FTempFolder + '\' + FFileName,
        GetMyProjectsFilePath + FCurNode.Rec.ValueByName('FileName').AsString);
      Result := FCurNode;
    end
    else
      FMessageID := 3;
  end
  else
    FMessageID := 2;
end;

function TReceiveProject.ReceiveForLost(const AFileName: string;
  AStartAudit, AEndAudit: Integer; AReply: Boolean = False): TsdIDTreeNode;
begin
  FStartAudit := AStartAudit;
  FEndAudit := AEndAudit;
  FReply := AReply;
  Result := Receive(AFileName);
end;

function TReceiveProject.UpdateData(const AFileName: string): Boolean;
var
  I: Integer;
begin
  if not IsReback and FNeedUpdate then
  begin
    Result := False;
    FProjectData := TProjectData.Create;
    try
      FProjectData.OpenForReceive(AFileName);
      FProjectData.LockedDataForSubmit;
      for I := 0 to FLostStageCount - 1 do
        FProjectData.UpdateDataForReceive;
      if FReply then
        FProjectData.LockedDataForReply;
      FProjectData.SaveForReceive(AFileName);
      Result := True;
    finally
      FProjectData.Free;
    end;
  end
  else
    Result := True;
end;

procedure TReceiveProject.SetWebBidName(const Value: string);
begin
  FWebBidName := Value;
end;

{ TTenderExport }

constructor TTenderExport.Create(ARec: TsdDataRecord;
  const AFileName: string);
begin
  FProjectName := ARec.ValueByName('Name').AsString;
  FFileName := GetMyProjectsFilePath +  ARec.ValueByName('FileName').AsString;
  if not G_IsCloud then
    FAttachmentPath := GetMyProjectsFilePath + 'Attachment\' + ARec.ValueByName('FileName').AsString + '\';
  FProjectID := ARec.ValueByName('ID').AsInteger;
  FTempFolder := GenerateTempFolder(GetTempFilePath);
  FXmlInfoFile := FTempFolder + '\Info.xml';
  FResultFile := AFileName;
end;

destructor TTenderExport.Destroy;
begin
  DeleteFileOrFolder(FTempFolder);
  inherited;
end;

procedure TTenderExport.Execute(AExportAttachment: Boolean);
var
  sFileName: string;
begin
  ExportXmlInfo;
  sFileName := ExtractSimpleFileName(FResultFile);
  CopyFileOrFolder(FFileName, FTempFolder + '\' + sFileName);

//  if not G_IsCloud then   // 云版不导出附件,云版的运作机制是从网上下载
//    if AExportAttachment then
//      CopyFolder(FAttachmentPath, FTempFolder + '\' + sFileName + '[附件包]');

  ZipFolder(FTempFolder, FResultFile);
end;

procedure TTenderExport.ExportXmlInfo;
var
  FXmlDocument: IXMLDocument;
begin
  FXmlDocument := TXMLDocument.Create(nil) as IXMLDocument;
  try
    FXmlDocument.Options := [doNodeAutoCreate,doNodeAutoIndent,doAttrNull,doAutoPrefix,doNamespaceDecl];
    FXmlDocument.Active := True;
    FXmlDocument.Encoding := 'GB2312';
    FXmlDocument.AddChild('ProjectMangerInfo');
    SaveInfoToXmlDocument(FXmlDocument.DocumentElement);
    FXmlDocument.SaveToFile(FXmlInfoFile);
  finally
    FXmlDocument := nil;
  end;
end;

procedure TTenderExport.SaveInfoToXmlDocument(AXmlNode: IXMLNode);
begin
  SaveProjectInfo(AXmlNode);
  SaveTenderProperty(AXmlNode);
end;

procedure TTenderExport.SaveProjectInfo(AXmlNode: IXMLNode);
var
  vInfo: IXMLNode;
  InfoRec: TsdDataRecord;
begin
  vInfo := AXmlNode.AddChild('ProjectInfo');
  InfoRec := ProjectManager.sddProjectsInfo.FindKey('idxID', FProjectID);
  vInfo.Attributes['ProjectName'] := FProjectName;
  vInfo.Attributes['DealTotalPrice'] := InfoRec.ValueByName('DealTotalPrice').AsFloat;
  vInfo.Attributes['PhaseTotalPrice'] := InfoRec.ValueByName('PhaseTotalPrice').AsFloat;
  vInfo.Attributes['EndDealTotalPrice'] := InfoRec.ValueByName('EndDealTotalPrice').AsFloat;
  vInfo.Attributes['EndChangeTotalPrice'] := InfoRec.ValueByName('EndChangeTotalPrice').AsFloat;
  vInfo.Attributes['EndTotalPrice'] := InfoRec.ValueByName('EndTotalPrice').AsFloat;
  vInfo.Attributes['PreTotalPrice'] := InfoRec.ValueByName('PreTotalPrice').AsFloat;
  vInfo.Attributes['PhasePay'] := InfoRec.ValueByName('PhasePay').AsFloat;
  vInfo.Attributes['Deal_BGLTotalPrice'] := InfoRec.ValueByName('Deal_BGLTotalPrice').AsFloat;
  vInfo.Attributes['PhaseCount'] := InfoRec.ValueByName('PhaseCount').AsInteger;
  vInfo.Attributes['AuditStatus'] := InfoRec.ValueByName('AuditStatus').AsInteger;
  vInfo.Attributes['FileName'] := ExtractSimpleFileName(FResultFile);
  vInfo.Attributes['CommonDigit'] := InfoRec.ValueByName('CommonDigit').AsInteger;
  vInfo.Attributes['DealPayDigit'] := InfoRec.ValueByName('DealPayDigit').AsInteger;

  if G_IsCloud then
  begin
    vInfo.Attributes['WebID'] := InfoRec.ValueByName('WebID').AsInteger;
    vInfo.Attributes['WebOwnerID'] := InfoRec.ValueByName('WebOwnerID').AsInteger;
    vInfo.Attributes['WebAuthorID'] := InfoRec.ValueByName('WebAuthorID').AsInteger;
  end;
end;

procedure TTenderExport.SaveTenderProperty(AXmlNode: IXMLNode);
var
  vProp: IXMLNode;
begin
  vProp := AXmlNode.AddChild('TenderProperty');
end;

{ TProjectExport }

constructor TProjectExport.Create(ANode: TsdIDTreeNode; const AFileName: string; AExportAttachment: Boolean);
begin
  FTempFolder := GenerateTempFolder(GetTempFilePath);
  FExportNode := ANode;
  FResultFile := AFileName;
  FExportAttachment := AExportAttachment;
  FXmlInfoFile := FTempFolder + '\TreeInfo.xml';
end;

destructor TProjectExport.Destroy;
begin
  DeleteFileOrFolder(FTempFolder);
  inherited;
end;

procedure TProjectExport.Execute;
begin
  ExportXmlInfo;
  ExportNode(FExportNode.FirstChild, FTempFolder + '\');
  ZipFolder(FTempFolder, FResultFile);
end;

procedure TProjectExport.ExportNode(ANode: TsdIDTreeNode;
  const AExportPath: string);
var
  ACurPath: string;
begin
  if not Assigned(ANode) then Exit;
  if ANode.HasChildren or (ANode.Rec.ValueByName('Type').AsInteger = 0) then
  begin
    ACurPath := AExportPath + ANode.Rec.ValueByName('Name').AsString + '\';
    CreateDirectoryInDeep(ACurPath);
    ExportNode(ANode.FirstChild, ACurPath);
  end
  else
    ExportTender(ANode, AExportPath);
  ExportNode(ANode.NextSibling, AExportPath);
end;

procedure TProjectExport.ExportTender(ANode: TsdIDTreeNode;
  const AExportPath: string);
begin
  FTenderExportor := TTenderExport.Create(ANode.Rec,
    AExportPath + ANode.Rec.ValueByName('Name').AsString);
  try
    FTenderExportor.Execute(FExportAttachment);
  finally
    FTenderExportor.Free;
  end;
end;

procedure TProjectExport.ExportXmlInfo;
var
  FXmlDocument: IXMLDocument;
begin
  FXmlDocument := TXMLDocument.Create(nil) as IXMLDocument;
  try
    FXmlDocument.Options := [doNodeAutoCreate,doNodeAutoIndent,doAttrNull,doAutoPrefix,doNamespaceDecl];
    FXmlDocument.Active := True;
    FXmlDocument.Encoding := 'GB2312';
    FXmlDocument.AddChild('ProjectTreeInfo');
    SaveTreeNodeInfo(FXmlDocument.DocumentElement, FExportNode.FirstChild);
    FXmlDocument.SaveToFile(FXmlInfoFile);
  finally
    FXmlDocument := nil;
  end;
end;

procedure TProjectExport.SaveTreeNodeInfo(AXmlNode: IXMLNode;
  ANode: TsdIDTreeNode);
var
  CurNode: IXMLNode;
begin
  if not Assigned(ANode) then Exit;
  CurNode := AXmlNode.AddChild('TreeNode');
  CurNode.Attributes['Name'] := ANode.Rec.ValueByName('Name').AsString;
  CurNode.Attributes['Type'] := ANode.Rec.ValueByName('Type').AsInteger;
  if ANode.HasChildren then
    SaveTreeNodeInfo(CurNode, ANode.FirstChild);
  SaveTreeNodeInfo(AXmlNode, ANode.NextSibling);
end;

{ TProjectImport }

function TProjectImport.AddNewNode(AParentID, ANextID: Integer;
  const ANodeName: string): TsdIDTreeNode;
begin
  with ProjectManager.ProjectsTree do
    Result := Add(AParentID, ANextID);
  Result.Rec.ValueByName('Name').AsString := ANodeName;
  Result.Rec.ValueByName('Type').AsInteger := 0;
end;

constructor TProjectImport.Create(ASelect: TsdIDTreeNode;
  const AProjectName, AFileName: string);
begin
  FSelect := ASelect;
  FProjectName := AProjectName;
  FFileName := AFileName;
  FTempFolder := GenerateTempFolder(GetTempFilePath);
end;

destructor TProjectImport.Destroy;
begin
  DeleteFileOrFolder(FTempFolder);
  inherited;
end;

procedure TProjectImport.Execute;
begin
  UnZipFile(FFileName, FTempFolder);
  // ID 188 需求变更，一直作为后兄弟节点导入
  {if FSelect.Rec.ValueByName('Type').AsInteger = 1 then
    FNew := AddNewNode(FSelect.ParentID, FSelect.NextSiblingID, FProjectName)
  else
    FNew := AddNewNode(FSelect.ID, -1, FProjectName);}
  if Assigned(FSelect) then
    FNew := AddNewNode(FSelect.ParentID, FSelect.NextSiblingID, FProjectName)
  else
    FNew := AddNewNode(-1, -1, FProjectName);
  LoadChildrenFormXml;
end;

procedure TProjectImport.LoadChild(AXmlNode: IXMLNode;
  AParent: TsdIDTreeNode; const APath: string);
var
  I, iType: Integer;
  XmlNode: IXMLNode;
  stnNewNode: TsdIDTreeNode;
begin
  for I := 0 to AXmlNode.ChildNodes.Count - 1 do
  begin
    XmlNode := AXmlNode.ChildNodes.Get(I);
    iType := XmlNode.Attributes['Type'];
    if iType = 0 then
    begin
      stnNewNode := AddNewNode(AParent.ID, -1, XmlNode.Attributes['Name']);
      if XmlNode.HasChildNodes then
        LoadChild(XmlNode, stnNewNode, APath + XmlNode.Attributes['Name'] + '\');
    end
    else
      LoadTender(XmlNode, AParent, APath);
  end;
end;

procedure TProjectImport.LoadChildrenFormXml;
var
  FXmlDocument: IXMLDocument;
  XmlNode: IXMLNode;
  FRootList: IXMLNodeList;
begin
  FXmlDocument := TXMLDocument.Create(nil) as IXMLDocument;
  try
    FXmlDocument.LoadFromFile(FTempFolder + '\TreeInfo.xml');
    FXmlDocument.Options := [doNodeAutoCreate,doNodeAutoIndent,doAutoPrefix,doNamespaceDecl];
    XmlNode := FXmlDocument.DocumentElement;
    LoadChild(XmlNode, FNew, FTempFolder + '\');
  finally
    FRootList := nil;
    FXmlDocument := nil;
  end;
end;

procedure TProjectImport.LoadTender(AXmlNode: IXMLNode;
  AParent: TsdIDTreeNode; const APath: string);
var
  TenderImportor: TTenderImport;
begin
  TenderImportor := TTenderImport.Create(AParent,
    AXmlNode.Attributes['Name'], APath + AXmlNode.Attributes['Name']);
  try
    TenderImportor.Execute;
  finally
    TenderImportor.Free;
  end;
end;

{ TTenderImport }

constructor TTenderImport.Create(ASelect: TsdIDTreeNode;
  const AProjectName, AFileName: string);
begin
  FSelect := ASelect;
  FProjectName := AProjectName;
  FFileName := AFileName;
  FTempFolder := GenerateTempFolder(GetTempFilePath);
end;

destructor TTenderImport.Destroy;
begin
  DeleteFileOrFolder(FTempFolder);
  inherited;
end;

procedure TTenderImport.Execute;
var
  sFile, sFJ: string;
begin
  UnZipFile(FFileName, FTempFolder);
  GetNewNode;
  LoadXmlInfo;
  sFile := FTempFolder + '\' + FTenderFileName;
//  sFJ := sFile + '[附件包]';
  CopyFileOrFolder(sFile, GetMyProjectsFilePath + FNewNode.Rec.ValueByName('FileName').AsString);
//  if not G_IsCloud then          // 云版附件从服务器获取，不导入。
//    if DirectoryExists(sFJ) then
//      CopyFolder(sFJ, GetMyProjectsFilePath + 'Attachment\' + FNewNode.Rec.ValueByName('FileName').AsString);
end;

procedure TTenderImport.GetNewNode;
begin
  if FSelect.Rec.ValueByName('Type').AsInteger = 1 then
    FNewNode := ProjectManager.ProjectsTree.Add(FSelect.ParentID, FSelect.NextSiblingID)
  else
    FNewNode := ProjectManager.ProjectsTree.Add(FSelect.ID, -1);
  FNewNode.Rec.ValueByName('Name').AsString := FProjectName;
  FNewNode.Rec.ValueByName('Type').AsInteger := 1;
  FNewNode.Rec.ValueByName('FileName').AsString := ExtractSimpleFileName(GetNewGUIDFileName(GetMyProjectsFilePath));
end;

procedure TTenderImport.ImportToSelect;
begin
  UnZipFile(FFileName, FTempFolder);
  FNewNode := FSelect;
  LoadXmlInfo;
  CopyFileOrFolder(FTempFolder + '\' + FTenderFileName,
    GetMyProjectsFilePath + FNewNode.Rec.ValueByName('FileName').AsString);
end;

procedure TTenderImport.LoadInfoFromXmlDocument(AXmlNode: IXMLNode);
var
  ChildNodes: IXMLNodeList;
begin
  ChildNodes := AXmlNode.ChildNodes;
  LoadProjectInfo(ChildNodes.FindNode('ProjectInfo'));
  LoadTenderProperty(ChildNodes.FindNode('TenderProperty'));
end;

procedure TTenderImport.LoadProjectInfo(AXmlNode: IXMLNode);
begin
  FTenderFileName := AXmlNode.Attributes['FileName'];
  FNewNode.Rec.ValueByName('DealTotalPrice').AsFloat := AXmlNode.Attributes['DealTotalPrice'];
  FNewNode.Rec.ValueByName('PhaseTotalPrice').AsFloat := AXmlNode.Attributes['PhaseTotalPrice'];
  FNewNode.Rec.ValueByName('EndDealTotalPrice').AsFloat := AXmlNode.Attributes['EndDealTotalPrice'];
  FNewNode.Rec.ValueByName('EndChangeTotalPrice').AsFloat := AXmlNode.Attributes['EndChangeTotalPrice'];
  FNewNode.Rec.ValueByName('EndTotalPrice').AsFloat := AXmlNode.Attributes['EndTotalPrice'];
  FNewNode.Rec.ValueByName('PreTotalPrice').AsFloat := AXmlNode.Attributes['PreTotalPrice'];
  FNewNode.Rec.ValueByName('PhasePay').AsFloat := AXmlNode.Attributes['PhasePay'];
  if AXmlNode.HasAttribute('Deal_BGLTotalPrice') then
    FNewNode.Rec.ValueByName('Deal_BGLTotalPrice').AsFloat := AXmlNode.Attributes['Deal_BGLTotalPrice'];
  FNewNode.Rec.ValueByName('PhaseCount').AsInteger := AXmlNode.Attributes['PhaseCount'];
  FNewNode.Rec.ValueByName('AuditStatus').AsInteger := AXmlNode.Attributes['AuditStatus'];
  FNewNode.Rec.ValueByName('CreateDate').AsString := FormatDateTime('yyyy-mm-dd', Date);
  FNewNode.Rec.ValueByName('CommonDigit').AsInteger := StrToIntDef(AXmlNode.Attributes['CommonDigit'], 0);
  FNewNode.Rec.ValueByName('DealPayDigit').AsInteger := StrToIntDef(AXmlNode.Attributes['DealPayDigit'], 0);

  if G_IsCloud then
  begin
    FNewNode.Rec.ValueByName('WebID').AsInteger := AXmlNode.Attributes['WebID'];
    FNewNode.Rec.ValueByName('WebOwnerID').AsInteger := AXmlNode.Attributes['WebOwnerID'];
    FNewNode.Rec.ValueByName('WebAuthorID').AsInteger := AXmlNode.Attributes['WebAuthorID'];
  end;

  ProjectManager.CalculateParentInfo(FNewNode.ParentID);
end;

procedure TTenderImport.LoadTenderProperty(AXmlNode: IXMLNode);
begin
  // TODO
end;

procedure TTenderImport.LoadXmlInfo;
var
  FXmlDocument: IXMLDocument;
  XmlNode: IXMLNode;
  FRootList: IXMLNodeList;
begin
  FXmlDocument := TXMLDocument.Create(nil) as IXMLDocument;
  try
    FXmlDocument.LoadFromFile(FTempFolder + '\Info.xml');
    FXmlDocument.Options := [doNodeAutoCreate,doNodeAutoIndent,doAutoPrefix,doNamespaceDecl];
    XmlNode := FXmlDocument.DocumentElement;
    LoadInfoFromXmlDocument(XmlNode);
  finally
    FRootList := nil;
    FXmlDocument := nil;
  end;
end;

{ TTenderFileChecker }

function TTenderFileChecker.CheckFileMatchXml: Boolean;
var
  FProjectData: TProjectData;
begin
  FProjectData := TProjectData.Create;
  try
    try
      FProjectData.SimpleOpen(FTenderFileName);
      Result := FProjectData.CheckDataBaseInfo(FPhaseCount, FAuditStatus);
    except
      Result := False;
    end;
  finally
    FProjectData.Free;
  end;
end;

function TTenderFileChecker.CheckFileValid(const AFileName: string): Boolean;
begin
  FTempFolder := GenerateTempFolder(GetTempFilePath);
  FTenderTempFolder := FTempFolder + '\Tender';
  CreateDirectoryInDeep(FTenderTempFolder);
  try
    Result := DecryptFile(AFileName) and CheckFileMatchXml;
  finally
    DeleteFileOrFolder(FTempFolder);
  end;
end;

function TTenderFileChecker.DecryptFile(const AFileName: string): Boolean;
begin
  Result := UnZipFile(AFileName, FTempFolder);
  if not Result then Exit;
  LoadInfo;
  Result := UnZipFile(FTenderFileName, FTenderTempFolder);
end;

procedure TTenderFileChecker.LoadInfo;
begin
  LoadInfoFromXml;
end;

procedure TTenderFileChecker.LoadInfoFromXml;
var
  FXmlDocument: IXMLDocument;
  XmlNode: IXMLNode;
  FRootList: IXMLNodeList;
begin
  FXmlDocument := TXMLDocument.Create(nil) as IXMLDocument;
  try
    FXmlDocument.LoadFromFile(FTempFolder + '\Info.xml');
    FXmlDocument.Options := [doNodeAutoCreate,doNodeAutoIndent,doAutoPrefix,doNamespaceDecl];
    XmlNode := FXmlDocument.DocumentElement;
    LoadInfoFromXmlDocument(XmlNode);
  finally
    FRootList := nil;
    FXmlDocument := nil;
  end;
end;

procedure TTenderFileChecker.LoadInfoFromXmlDocument(AXmlNode: IXMLNode);
var
  ChildNodes: IXMLNodeList;
begin
  ChildNodes := AXmlNode.ChildNodes;
  LoadProjectInfo(ChildNodes.FindNode('ProjectInfo'));
end;

procedure TTenderFileChecker.LoadProjectInfo(AXmlNode: IXMLNode);
begin
  FProjectName := AXmlNode.Attributes['ProjectName'];
  FPhaseCount := AXmlNode.Attributes['PhaseCount'];
  FAuditStatus := AXmlNode.Attributes['AuditStatus'];
  FTenderFileName := FTempFolder + '\' + AXmlNode.Attributes['FileName'];
end;

{ TTenderZipCompleteChecker }

function TTenderZipCompleteChecker.CheckAllPhase: Boolean;
begin
  Result := Assigned(FProjectData) and FProjectData.CheckPhaseFileComplete;
end;

function TTenderZipCompleteChecker.CheckFileValid(
  const AFileName: string): Boolean;
begin
  FFileName := AFileName;
  FTempFolder := GenerateTempFolder(GetTempFilePath);
  try
    case CheckType of
      zctAllPhase: Result := UnZipFile(AFileName, FTempFolder) and CheckMain and CheckAllPhase;
      zctLastPhase: Result := UnZipFile(AFileName, FTempFolder) and CheckMain and CheckLastPhase;
    end;
  finally
    if Assigned(FProjectData) then
      FProjectData.Free;
    DeleteFileOrFolder(FTempFolder);
  end;
end;

function TTenderZipCompleteChecker.CheckLastPhase: Boolean;
begin
  Result := Assigned(FProjectData) and FProjectData.CheckLastPhaseFileExist;
end;

function TTenderZipCompleteChecker.CheckMain: Boolean;
begin
  Result := False;
  if FileExists(FTempFolder + '\main.dat') then
  begin
    try
      FProjectData := TProjectData.Create;
      FProjectData.OpenForSubmit(FFileName);
      Result := True;
    except
      Result := False;
    end;
  end;
end;

constructor TTenderZipCompleteChecker.Create;
begin
  FCheckType := zctAllPhase;
end;

destructor TTenderZipCompleteChecker.Destroy;
begin
  inherited;
end;

end.
