unit ReportManagerDM;

interface

uses
  SysUtils, Classes, DB, DBClient, Contnrs, UtilMethods, Archiver;

const
  P6 = '4鲐;d煬0痤踿8镊<x伡垁Zx鷹?	';    // 报表模板文件密码
  PA6: array [0..31] of Byte =
    ($34, $F6, $D8, $0C, $3B, $64, $9F, $AC, $30, $08, $F0, $EE, $DB, $80, $38, $C4,
     $F7, $3C, $78, $81, $BC, $88, $80, $5A, $78, $FA, $97, $8D, $1E, $09, $FA, $FD);

type
  TChangeType = (ctAdd, ctEdit);

  TReport = class(TObject)
  private
    FID: Integer;
    FLocalVer: string;
    FName: string;
    FFileName: string;
    FCategory: string;
    FNewestVer: string;
    FHistoryVer1: string;
    FHistoryVer2: string;
//    FHistoryURL2: string;
//    FHistoryURL1: string;
//    FNewestURL: string;
    FHistoryMemo2: string;
    FHistoryMemo1: string;
    FNewestMemo: string;
    FKey: string;
    FArchiver: TArchiver;
    FOwner: TObject;
    FArea: string;
    procedure SetID(const Value: Integer);
    procedure SetLocalVer(const Value: string);
    procedure SetFileName(const Value: string);
    procedure SetName(const Value: string);
    procedure SetCategory(const Value: string);
    procedure SetNewestVer(const Value: string);
    procedure SetHistoryVer1(const Value: string);
    procedure SetHistoryVer2(const Value: string);
//    procedure SetHistoryURL1(const Value: string);
//    procedure SetHistoryURL2(const Value: string);
//    procedure SetNewestURL(const Value: string);
    procedure SetHistoryMemo1(const Value: string);
    procedure SetHistoryMemo2(const Value: string);
    procedure SetNewestMemo(const Value: string);
    procedure ArchiverEnterCryptKey(Sender: TObject; var Key: String);
    procedure ArchiverRequestCryptKey(Sender: TObject; var Key: String);
    procedure SetOwner(const Value: TObject);
    function GetPath: string;
    procedure SetArea(const Value: string);
  public
    property ID: Integer read FID write SetID;
    property LocalVer: string read FLocalVer write SetLocalVer;
    property Path: string read GetPath;

    property NewestVer: string read FNewestVer write SetNewestVer;        // 线上最新版本
//    property NewestURL: string read FNewestURL write SetNewestURL;
    property NewestMemo: string read FNewestMemo write SetNewestMemo;

    property HistoryVer1: string read FHistoryVer1 write SetHistoryVer1;  // 线上历史版本次新
//    property HistoryURL1: string read FHistoryURL1 write SetHistoryURL1;
    property HistoryMemo1: string read FHistoryMemo1 write SetHistoryMemo1;

    property HistoryVer2: string read FHistoryVer2 write SetHistoryVer2;  // 线上历史版本次次新
//    property HistoryURL2: string read FHistoryURL2 write SetHistoryURL2;
    property HistoryMemo2: string read FHistoryMemo2 write SetHistoryMemo2;
//    property Name: string read FName write SetName;         // 文件头中的文件名，仅用于大报表界面显示
    property Area: string read FArea write SetArea;         // 全国1，广东2，通用3
    property Category: string read FCategory write SetCategory;
    property FileName: string read FFileName write SetFileName;     // 硬盘上的物理文件名

    property Owner: TObject read FOwner write SetOwner;

    constructor Create;
    destructor Destroy; override;
    // AVer报表的版本号
    function Update(AVer: string): Boolean;
    function EncryptReport: Boolean;
    procedure DecryptReport;
    procedure GetHistoryVer;
  end;

  TReports = class(TDataModule)
    cdsOnline: TClientDataSet;
    cdsOnlineCategory: TStringField;
    cdsOnlineName: TStringField;
    cdsOnlineVer: TStringField;
    cdsOnlineState: TStringField;
    cdsOnlineHistory: TStringField;
    cdsOnlineNewest: TStringField;
    cdsOnlineID: TIntegerField;
    cdsLocal: TClientDataSet;
    cdsLocalID: TIntegerField;
    cdsLocalCategory: TStringField;
    cdsLocalName: TStringField;
    cdsLocalVer: TStringField;
    cdsLocalState: TStringField;
    cdsLocalHistory: TStringField;
    cdsLocalNewest: TStringField;
    cdsCustom: TClientDataSet;
    cdsCustomID: TIntegerField;
    cdsCustomName: TStringField;
    cdsCustomCategory: TStringField;
    cdsCustomLocalVer: TStringField;
    cdsCustomState: TStringField;
    cdsCustomHistory: TStringField;
    cdsCustomNewestVer: TStringField;
    cdsTemp: TClientDataSet;
    cdsCode: TClientDataSet;
    cdsCodeProject: TStringField;
    cdsCodeCode: TStringField;
    procedure DataModuleCreate(Sender: TObject);
    procedure DataModuleDestroy(Sender: TObject);
  private
    { Private declarations }
    FList: TObjectList;
    FPath: string;
    // 线上报表分类列表。线下通过文件读出来的跟线上的不一致，导致分类不统一。现以线上的为主。
    FOnlineCategories: TStringList;

    // AChangeType: 1增加 2修改
    procedure ChangeRec(ACDS: TClientDataSet; AReport: TReport; AChangeType: TChangeType);
  public
    { Public declarations }
    procedure Load;
    function LoadCustom(ACode: string): Boolean;
    procedure Refresh(AReport: TReport; ANeedAddLocal: Boolean);
    procedure CategoryList(AList: TStrings);
    procedure CancelFilterCategory;
    procedure FilterCategory(ACategory: string);
    function FindReprot(AID: Integer): TReport;
    procedure FilterNeedUpdates(AFilter: Boolean);
    function HasNeedUpdateReport: Boolean;
    function HasReport: Boolean;
    procedure LoadCodes;
    procedure saveCodes(AProjectName, ACode: string);

    property Path: string read FPath;
  end;



implementation

  uses Variants, ScFileArchiverConsts, ArchiverRoot, PHPWebDm, CslJson,
    XMLDoc, XMLIntf, Windows, Math, Forms, iniFiles;

const G_ReportDNS = 'http://vc.6jlzf.cn';   // 最后不能带"/"


var
  Reports: TReports;

{$R *.dfm}

function ByteArrayToStr(AByteArray: array of Byte; ALength: Integer): string;
begin
  SetString(Result, PChar(@AByteArray[0]), ALength);
end;

{ TReport }

constructor TReport.Create;
begin
  FArchiver := TArchiver.Create(nil);
  FArchiver.Options := FArchiver.Options - [oOpenSingleSegment] + [oMaintainFileDirectory, oCrypt];
  FArchiver.OnEnterCryptKey := ArchiverEnterCryptKey;
  FArchiver.OnRequestCryptKey := ArchiverRequestCryptKey;
end;

destructor TReport.Destroy;
begin
  FArchiver.Free;
  inherited;
end;

procedure TReport.SetCategory(const Value: string);
begin
  FCategory := Value;
end;

procedure TReport.SetFileName(const Value: string);
begin
  FFileName := Value;
end;

procedure TReport.SetID(const Value: Integer);
begin
  FID := Value;
end;

procedure TReport.SetName(const Value: string);
begin
  FName := Value;
end;

procedure TReport.SetLocalVer(const Value: string);
begin
  FLocalVer := Value;
end;

procedure TReport.SetNewestVer(const Value: string);
begin
  FNewestVer := Value;
end;

procedure TReports.DataModuleCreate(Sender: TObject);
begin
  FPath := ExtractFilePath(ParamStr(0)) + 'ReportTemplates\';
  FList := TObjectList.Create;
  FOnlineCategories := TStringList.Create;
  
  Load;

  cdsTemp.CloneCursor(cdsLocal, True);

  LoadCodes;
end;

procedure TReports.DataModuleDestroy(Sender: TObject);
begin
  FList.Free;
  FOnlineCategories.Free;
end;

procedure TReports.Load;
var
  vReport: TReport;
  SRec: TSearchRec;
  iRetval, i, iID: Integer;
  FOnlineAry, VersArr: TOVArr;
  vHead7: TScFile7Head;
  vSL: TStringList;
  sProp, sURL, sIDs, sArea, sFile: string;
  vFS: TFileStream;
begin
  if FList.Count > 0 then
    FList.Clear;

  // 先读本地的
  vSL := TStringList.Create;
  iRetval := FindFirst(FPath + '*.srt', faAnyFile, sRec);
  try
    while iRetval = 0 do
    begin
      if (SRec.Attr and faDirectory) = 0 then
      begin
        sFile := FPath + SRec.Name;
        vFS := TFileStream.Create(sFile, fmOpenRead);
        try
          vFS.Read(vHead7, SizeOf(vHead7));
        finally
          vFS.Free;
        end;

        SetString(sProp, vHead7.ReportProperties, 256);

        vSL.Text := sProp;
        vReport := TReport.Create;
        vReport.Owner := Self;
        vReport.ID := StrToInt(vSL.Values['报表ID']);
//        vReport.Category := vSL.Values['分类名称'];   // 分类不取本地文件，取线上的。
        vReport.FileName := SRec.Name;
        vReport.LocalVer := vSL.Values['版本'];
        FList.Add(vReport);

      end;
      iRetval := FindNext(SRec);
    end;

  finally
    SysUtils.FindClose(SRec);
    vSL.Free;
  end;

  // 对比线上最新版本号，看是否需更新
  if FList.Count > 0 then
  begin
    cdsLocal.DisableControls;
    cdsLocal.First;
    while cdsLocal.RecordCount > 0 do
      cdsLocal.Delete;

    try
      for i := 0 to FList.Count - 1 do
      sIDs := sIDs + ',' + IntToStr(TRePort(FList[i]).ID);

      Delete(sIDs, 1, 1);
      sURL := G_ReportDNS + '/api/report/update/get';

      case PHPWeb.Search(sURL, ['idlist'], [sIDs], VersArr) of
        1:
        begin
          for i := 0 to Length(VersArr) - 1 do
          begin
            iID := StrToInt(VersArr[i, 0]);
            vReport := FindReprot(iID);
            if vReport <> nil then
            begin
              vReport.NewestVer := VersArr[i, 1];
              vReport.NewestMemo := ReplaceChars(VersArr[i, 3]);
              vReport.Category := VersArr[i, 2];
              ChangeRec(cdsLocal, vReport, ctAdd);
            end;
          end;
        end
        else
        begin
          Application.MessageBox(PChar('报表更新检测失败，请检查网络！') , '提示', MB_OK + MB_ICONWARNING);
          Exit;
        end;
      end;
    finally
      cdsLocal.First;
      cdsLocal.EnableControls;
    end;
  end;

  cdsLocal.AddIndex('idxCN','Category;NewestVer',[],'NewestVer');
  cdsLocal.IndexName := 'idxCN';
  // 这种不能指定降序。
//  cdsLocal.IndexFieldNames := 'Category;NewestVer';

  // 线上报表按地区获取；线下广东和全国分开装，报表不会混合，所以本地无需作区分
  {$IFDEF _mGuangDong}
     sArea := '2';   // 广东
  {$ELSE}
     sArea := '1';   // 全国
  {$ENDIF}
  PHPWeb.Search(G_ReportDNS + '/api/getreportmsg', ['areacate'], [sArea], FOnlineAry);
  if Length(FOnlineAry) > 0 then
  begin
    cdsOnline.DisableControls;
    cdsOnline.First;
    while cdsOnline.RecordCount > 0 do
      cdsOnline.Delete;

    try
      for i := Low(FOnlineAry) to High(FOnlineAry) do
      begin
        FOnlineCategories.Add(FOnlineAry[i, 5]);
        iID := StrToInt(FOnlineAry[i, 0]);
        vReport := FindReprot(iID);

        // 线上有，本地无
        if vReport = nil then
        begin
          vReport := TReport.Create;
          vReport.Owner := Self;
          vReport.ID := iID;
          vReport.FileName := CheckExt(FOnlineAry[i, 1], '.srt');
          vReport.Area := FOnlineAry[i, 2];
          vReport.NewestVer := FOnlineAry[i, 3];
          vReport.Category := FOnlineAry[i, 5];
          vReport.NewestMemo := ReplaceChars(FOnlineAry[i, 6]);
          vReport.LocalVer := '';

          FList.Add(vReport);
        end;

        ChangeRec(cdsOnline, vReport, ctAdd);
      end;
    finally
      cdsOnline.First;
      cdsOnline.EnableControls;
    end;
  end;
end;

procedure TReports.CategoryList(AList: TStrings);
var sCgy: string;
  i: Integer;
begin
  AList.Clear;
  AList.Add('按类别筛选');
  FOnlineCategories.Sort;
  for i := 0 to FOnlineCategories.Count - 1 do
  begin
    sCgy := FOnlineCategories[i];
    if AList.IndexOf(sCgy) < 0 then
      AList.Add(sCgy);
  end;
end;

procedure TReports.CancelFilterCategory;
begin
  cdsOnline.Filtered := False;
end;

procedure TReports.FilterCategory(ACategory: string);
begin
  cdsOnline.Filter := Format('Category=''%s''', [ACategory]);
  cdsOnline.Filtered := True;
end;

procedure TReport.SetHistoryVer1(const Value: string);
begin
  FHistoryVer1 := Value;
end;

procedure TReport.SetHistoryVer2(const Value: string);
begin
  FHistoryVer2 := Value;
end;

//procedure TReport.SetHistoryURL1(const Value: string);
//begin
//  FHistoryURL1 := Value;
//end;
//
//procedure TReport.SetHistoryURL2(const Value: string);
//begin
//  FHistoryURL2 := Value;
//end;
//
//procedure TReport.SetNewestURL(const Value: string);
//begin
//  FNewestURL := Value;
//end;

function TReports.FindReprot(AID: Integer): TReport;
var i: Integer;
  vReport: TReport;
begin
  Result := nil;
  for i := 0 to FList.Count - 1 do
  begin
    vReport := TReport(FList[i]);
    if vReport.ID = AID then
    begin
      Result := vReport;
      Break;
    end;
  end;
end;

procedure TReport.SetHistoryMemo1(const Value: string);
begin
  FHistoryMemo1 := Value;
end;

procedure TReport.SetHistoryMemo2(const Value: string);
begin
  FHistoryMemo2 := Value;
end;

procedure TReport.SetNewestMemo(const Value: string);
begin
  FNewestMemo := Value;
end;

function TReport.Update(AVer: string): Boolean;
var vArr: array of string;
  sURL, sLocalFile: string;
  ANeedAddLocal: Boolean;
begin
  Result := False;
  vArr := VarArrayOf(['downloadurl']);
  sURL := Format(G_ReportDNS + '/api/downloadurl/%d/%s', [FID, AVer]);
  PHPWeb.Search(sURL, vArr);
  sLocalFile := Path + CheckExt(FFileName, '.xml');

  if PHPweb.DownFile(vArr[0], sLocalFile) then
  begin
    if EncryptReport then
    begin
      ANeedAddLocal := FLocalVer = '';
      FLocalVer := AVer;
      TReports(FOwner).Refresh(Self, ANeedAddLocal);
      Result := True;
    end;
  end
  else
    Application.MessageBox(PChar('报表文件下载失败，请重试！') , '提示', MB_OK + MB_ICONWARNING);
end;


function TReport.EncryptReport: Boolean;

  function GetReportPropertyFromXML(AFile: string): string;
  var
    I: Integer;
    xmlDoc: IXMLDocument;
    xmlNode: IXMLNode;
    slstProperties: TStringList;
  begin
    slstProperties := TStringList.Create;
    xmlDoc := TXMLDocument.Create(nil) as IXMLDocument;
    try
      xmlDoc.LoadFromFile(AFile);
      xmlNode := xmlDoc.DocumentElement;
      if xmlNode <> nil then
      begin
        for I := 0 to xmlNode.AttributeNodes.Count - 1 do
        begin
          slstProperties.Values[xmlNode.AttributeNodes[I].NodeName] := xmlNode.AttributeNodes[I].NodeValue;
        end;
      end;
      Result := slstProperties.Text;
    finally
      slstProperties.Free;
    end;
  end;

  procedure WriteReportProp(AFile: string; AProp: string);
  var vHead7: TScFile7Head;
    sTemp: string;
    I: Integer;
    vFS, vTempFS: TFileStream;
  begin
    ZeroMemory(@vHead7, Sizeof(TScFile7Head));
    vHead7.ProductName := 'SmartCost';
    vHead7.ProductVersion := '8.0';
    vHead7.FileType := 6;
    vHead7.ProjectFileType := 0;
    vHead7.IsSysFile := True;
    vHead7.ReadOnly := False;
    vHead7.HasPassword := False;
    vHead7.Password := '';
    vHead7.FileVersion := '1.0.0.1';
    if AProp <> '' then
      StrPLCopy(@(vHead7.ReportProperties[0]), AProp, Length(AProp));

    ZeroMemory(@(vHead7.Reserve[0]), Length(vHead7.Reserve));
    for I := 0 to Length(vHead7.RandomData) - 1 do
      vHead7.RandomData[I] := Char(RandomRange(0, 255));
    vFS := TFileStream.Create(AFile, fmOpenReadWrite);
    sTemp := ExtractFilePath(AFile) + TempName(16);
    vTempFS := TFileStream.Create(sTemp, fmCreate);
    try
      vTempFS.Seek($00, soFromBeginning);
      vTempFS.Write(vHead7, Sizeof(TScFile7Head));
      vTempFS.CopyFrom(vFS, vFS.Size);
    finally
      vTempFS.Free;
      vFS.Free;
      CopyFile(PChar(sTemp), PCHar(AFile), False);
      DeleteFile(PChar(sTemp));
    end;
  end;

var sSource, sXMLProp, sTargetFile, sTempFile: string;
begin
  Result := False;
  sSource := Path + CheckExt(FileName, '.xml');
  if not FileExists(sSource) then
    Exit;

  try
    try
      FKey := ByteArrayToStr(PA6, Length(PA6));
      sXMLProp := GetReportPropertyFromXML(sSource);

      sTargetFile := CheckExt(sSource, '.srt');
      if FileExists(sTargetFile) then
        DeleteFile(PChar(sTargetFile));

      FileSetAttr(sSource, 0);
      sTempFile := sSource + '.~temp~';
      CopyFile(PChar(sSource), PChar(sTempFile), False);

      FArchiver.FileName := sTargetFile;
      FArchiver.OpenNew;
      FArchiver.AddFile(sTempFile);
      FArchiver.Close;

      WriteReportProp(sTargetFile, sXMLProp);
      Result := True;
    except
      Application.MessageBox(PChar('报表文件损坏，加密失败无法使用，请重试！') , '提示', MB_OK + MB_ICONWARNING);
    end;
  finally
    DeleteFile(PChar(sTempFile));
    DeleteFile(PChar(sSource));
  end;
end;

procedure TReport.ArchiverEnterCryptKey(Sender: TObject; var Key: String);
begin
  Key := FKey;
end;

procedure TReport.ArchiverRequestCryptKey(Sender: TObject;
  var Key: String);
begin
  Key := FKey;
end;

procedure TReport.DecryptReport;
var
  sSource, sTemp, sDcyt: string;
  TF, TS: TFileStream;
  idx: Integer;
  IsEmpty: Boolean;
begin
  sSource := Path + FileName;
  if not FileExists(sSource) then
    Exit;
  FKey := ByteArrayToStr(PA6, Length(PA6));
  sDcyt := ChangeFileExt(sSource, '.xml');
  sTemp := Path + 'TempReport.~temp~';

  FileSetAttr(sSource, 0);

  TS := TFileStream.Create(sSource, fmOpenRead);
  TF := TFileStream.Create(sTemp, fmCreate);
  try
    TS.Position := SizeOf(TScFile7Head);
    TF.CopyFrom(TS, TS.Size - SizeOf(TScFile7Head));
  finally
    TS.Free;
    TF.Free;
  end;

  try
    FArchiver.FileName := sTemp;
    FArchiver.Open;
    FArchiver.EnumerateFiles;
    if FArchiver.FileCount <=0 then
      raise Exception.Create('读取文件出错！');
    idx := FArchiver.FileCount - 1;

    with FArchiver.Header do
      IsEmpty := (ArchiveInfo.FileCount + SegmentInfo.FileCount) = 0;

    if IsEmpty or (idx <= -1) then
      raise Exception.Create('文件中没有数据！');

    try
      with FArchiver.Files[idx].FileEntry do
        FArchiver.ExtractFileTo(Segment, Offset, ArchiveInfo.CompressedSize, sDcyt);
    except
      raise;
    end;
  finally
    FArchiver.Close;
    DeleteFile(PChar(sTemp));
    DeleteFile(PChar(sSource));
  end;
end;

procedure TReports.Refresh(AReport: TReport; ANeedAddLocal: Boolean);
begin
  if ANeedAddLocal then
    ChangeRec(cdsLocal, AReport, ctAdd)
  else
    ChangeRec(cdsLocal, AReport, ctEdit);

  ChangeRec(cdsOnline, AReport, ctEdit);
  ChangeRec(cdsCustom, AReport, ctEdit);
end;

procedure TReport.SetOwner(const Value: TObject);
begin
  FOwner := Value;
end;

function TReport.GetPath: string;
begin
  Result := TReports(FOwner).Path;
end;

procedure TReports.ChangeRec(ACDS: TClientDataSet; AReport: TReport; AChangeType: TChangeType);
begin
  if AChangeType = ctAdd then
    ACDS.Append
  else if AChangeType = ctEdit then
  begin
    if ACDS.Locate('ID', AReport.ID, []) then
      ACDS.Edit
    else
      Exit;
  end;

  ACDS.FieldByName('ID').AsInteger := AReport.ID;
  ACDS.FieldByName('Category').AsString := AReport.Category;
  ACDS.FieldByName('Name').AsString := AReport.FileName;
  ACDS.FieldByName('LocalVer').AsString := AReport.LocalVer;
  ACDS.FieldByName('NewestVer').AsString := AReport.NewestVer;

  if AReport.LocalVer = '' then
    ACDS.FieldByName('State').AsString := '下载'
  else
  begin
    if SameText(AReport.LocalVer, AReport.NewestVer) then
      ACDS.FieldByName('State').AsString := '已下载'
    else
      ACDS.FieldByName('State').AsString := '更新';
  end;

  ACDS.FieldByName('History').AsString := '查看';
  ACDS.Post;
end;

procedure TReport.GetHistoryVer;
var vArr: TOVArr;
  sURL, sLocalFile: string;
begin
  sURL := Format('%s/api/getversionmsg/%d', [G_ReportDNS, FID]);
  PHPWeb.Search(sURL, vArr);

  try
    if Length(vArr) > 0 then
    begin
      FNewestVer := vArr[0, 0];
      FNewestMemo := ReplaceChars(vArr[0, 1]);
//      FNewestURL := vArr[0, 2];
    end;

    if Length(vArr) > 1 then
    begin
      FHistoryVer1 := vArr[1, 0];
      FHistoryMemo1 := ReplaceChars(vArr[1, 1]);
//      FHistoryURL1 := vArr[1, 2];
    end;

    if Length(vArr) > 2 then
    begin
      FHistoryVer2 := vArr[2, 0];
      FHistoryMemo2 := ReplaceChars(vArr[2, 1]);
//      FHistoryURL2 := vArr[2, 2];
    end;
  except
    Application.MessageBox('报表文件有错误！', '提示', MB_OK + MB_ICONWARNING);
  end;
end;

function TReports.LoadCustom(ACode: string): Boolean;
var FOnlineAry: TOVArr;
  i, iID: Integer;
  vReport: TReport;
  sProjName: string;
begin
  Result := False;

  PHPWeb.Search(G_ReportDNS + '/api/getreportmsg', ['itemcode'], [ACode], FOnlineAry);
  if Length(FOnlineAry) > 0 then
  begin
    cdsCustom.DisableControls;
    cdsCustom.First;
    while cdsCustom.RecordCount > 0 do
      cdsCustom.Delete;

    try
      for i := Low(FOnlineAry) to High(FOnlineAry) do
      begin
        iID := StrToInt(FOnlineAry[i, 0]);
        vReport := FindReprot(iID);

        // 线上有，本地无
        if vReport = nil then
        begin
          vReport := TReport.Create;
          vReport.Owner := Self;
          vReport.ID := iID;
          vReport.FileName := CheckExt(FOnlineAry[i, 1], '.srt');
          vReport.Area := FOnlineAry[i, 2];
          vReport.NewestVer := FOnlineAry[i, 3];
          vReport.Category := FOnlineAry[i, 5];
          vReport.NewestMemo := ReplaceChars(FOnlineAry[i, 6]);
          vReport.LocalVer := '';

          FList.Add(vReport);
        end;

        ChangeRec(cdsCustom, vReport, ctAdd);
      end;
      sProjName := FOnlineAry[0, 7];
      Result := True;
      saveCodes(sProjName, ACode);
    finally
      cdsCustom.First;
      cdsCustom.EnableControls;
    end;
  end
  else
    Application.MessageBox('没有找到该领取码的定制报表！', '提示', MB_OK + MB_ICONINFORMATION);
end;

procedure TReport.SetArea(const Value: string);
begin
  FArea := Value;
end;

procedure TReports.FilterNeedUpdates(AFilter: Boolean);
begin
  if not cdsLocal.Active then Exit;

  if AFilter then
  begin
    cdsLocal.Filter := 'State = ''更新''';
    cdsLocal.Filtered := True;
  end
  else
    cdsLocal.Filtered := False;
end;

function TReports.HasNeedUpdateReport: Boolean;
//var cdsTemp: TClientDataSet;
begin
//  Result := False;
//  cdsTemp := TClientDataSet.Create(nil);
//  try
//    cdsTemp.CloneCursor(cdsLocal, True);
//    cdsTemp.Filter := 'State = ''更新''';
//    cdsTemp.Filtered := True;
//    Result := cdsTemp.RecordCount > 0;
//  finally
//    cdsTemp.Free;
//  end;

  cdsTemp.Filter := 'State = ''更新''';
  cdsTemp.Filtered := True;
  Result := cdsTemp.RecordCount > 0;
end;

function TReports.HasReport: Boolean;
begin
  cdsTemp.Filtered := False;
  Result := cdsTemp.RecordCount > 0;
end;

procedure TReports.LoadCodes;
var ini: TIniFile;
  vSL: TStringList;
  i: Integer;
  sCode: String;
begin
  cdsCode.DisableControls;
  vSL := TStringList.Create;
  ini := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'Cloud.ini');
  try
    cdsCode.First;
    while cdsCode.RecordCount > 0 do
      cdsCode.Delete;

    Ini.ReadSection('Codes', vSL);

    for i := vSL.Count - 1 downto 0 do
    begin
      sCode := Ini.ReadString('Codes', vSL[i], '');
      cdsCode.Append;
      cdsCodeProject.AsString :=  vSL[i];
      cdsCodeCode.AsString := sCode;
      cdsCode.Post;
    end;

    cdsCode.First;
  finally
    vSL.Free;
    ini.Free;
    cdsCode.EnableControls;
  end;
end;

procedure TReports.saveCodes(AProjectName, ACode: string);
var ini: TIniFile;
  vSL: TStringList;
  i: Integer;
  sCode: String;
  bExist: Boolean;
begin
  bExist := False;
  vSL := TStringList.Create;
  ini := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'Cloud.ini');
  try
    Ini.ReadSection('Codes', vSL);

    for i := 0 to vSL.Count - 1 do
    begin
      sCode := Ini.ReadString('Codes', vSL[i], '');
      if SameText(sCode, ACode) then
        Exit;
    end;

    ini.WriteString('Codes', AProjectName, ACode);

  finally
    vSL.Free;
    ini.Free;
  end;
end;

end.
