unit ZJJLDm;

interface

uses
  ZjGrid, MeasureGatherZJJL,
  SysUtils, Classes, DB, DBClient, ADODB, sdIDTree, sdDB, Provider,
  Windows, BillsTree, sdProvider, Variants;

type
  TZJJLType = (ztFx, ztGcl, ztGclGather); 
  TRefreshDetailGridEvent = procedure of Object;

  TZJJLNode = class
  private
    FBillsID: Integer;
    FCode: string;
    FCertificateCode: string;
    FBillsCode: string;
    FFormulaMemo: string;
    FRelaFile: string;
    FType: Integer;
  end;

  TZJJLInfoRec = Record
    FBFXName: string;
    PegName: string;
    BeginPeg: string;
    EndPeg: string;
    BGLCode: string;
    DrawingCode: string;
    UnitName: string;
  end;

  TZJJLData = class(TDataModule)
    sdpZJJL: TsdADOProvider;
    sddZJJL: TsdDataSet;
    sdvZJJL: TsdDataView;
    sdpHistory: TsdADOProvider;
    sddHistory: TsdDataSet;
    sdpZJJLDetail: TsdADOProvider;
    sddZJJLDetail: TsdDataSet;
    procedure sdvZJJLBeforeValueChange(AValue: TsdValue;
      const NewValue: Variant; var Allow: Boolean);
    procedure sdvZJJLAfterDeleteRecord(ARecord: TsdDataRecord);
    procedure sdvZJJLCurrentChanged(ARecord: TsdDataRecord);
  private
    FPhaseData: TObject;
    FCanModified: Boolean;
    FNewID: Integer;
    FOrgDataList: TList;
    FRefreshDetailGrid: TRefreshDetailGridEvent;

    function GetNewCode(ANewID: Integer): string;
    procedure GenerateZJJLNode(ANode: TsdIDTreeNode; AType: Integer);

    function CheckLastXmj(AID: Integer): Boolean;
    function CheckBGExist(ANode: TsdIDTreeNode): Boolean;
    function CheckMeasureExist(ANode: TsdIDTreeNode): Boolean;
    procedure GenerateLastXmj(ANode: TsdIDTreeNode);
    procedure GenerateNode(ANode: TsdIDTreeNode);

    function CheckLeafNodeMeasureExist(AID: Integer): Boolean;
    procedure GenerateLeafNode(ANode: TsdIDTreeNode);
    procedure GenerateNodeByB_Code(ANode: TsdIDTreeNode);

    procedure DeleteAll;
    procedure RestoreOrgData;

    procedure DeleteHistory(ABillsID, AType: Integer);
    procedure DeleteGatherHistory(ARecord: TsdDataRecord);

    procedure DetailGridCellCanEdit(Sender: TObject; const ACoord: TPoint;
      var Allow: Boolean);
    procedure DetailGridCellTextChanged(Sender: TObject; Col, Row: Integer);

    function GetFieldStrDef(ANode: TsdIDTreeNode; const AFieldName, ADef: string): string;
    function GetPegNode(ANode: TsdIDTreeNode): TsdIDTreeNode;
    function GetBGLCode(ANode: TsdIDTreeNode): string;
    // 取树结构的第ALevel层节点的名称（level从0开始）
    function GetNameByLevel(ANode: TsdIDTreeNode; ALevel: Integer): string;
    function GetFBFXName(ANode, APegNode: TsdIDTreeNode): string;
    function GetPegName(ANode: TsdIDTreeNode): string;

    function GetInfoRecByCode_2(ABillsID: Integer): TZJJLInfoRec;
    function GetInfoRecByB_Code_2(ABillsID: Integer): TZJJLInfoRec;
    function GetInfoRec_2(ABillsID, AType: Integer): TZJJLInfoRec;

    function GetNewHistoryID: Integer;
    procedure SetHistory(const AFieldName, AValue: string; AZJJL_Rec: TsdDataRecord);

    // 向父项检测，直至提取到图册号为止
    function GetDrawingCode(ANode: TsdIDTreeNode): string;

    procedure CheckZjjlVerison;

    function GetMainBillsTree: TsdIDTree;
  public
    constructor Create(APhaseData: TObject);
    destructor Destroy; override;

    procedure Open(AConnection: TADOConnection);
    procedure Save;

    procedure GenerateAll;
    procedure GenerateAllByB_Code;
    procedure GenerateAllByB_CodeGather;
    procedure AssignedCurData(ARec: TsdDataRecord; ADetailGrid: TZJGrid);

    function GetInfoRecByCode(ABillsID: Integer): TZJJLInfoRec;
    function GetInfoRecByB_Code(ABillsID: Integer): TZJJLInfoRec;
    function GetInfoRec(ABillsID, AType: Integer): TZJJLInfoRec;

    function GetZJJLCalcData(ARec: TsdDataRecord; const AFileName: string): Double;

    procedure LocateBills;
    function FindZJJLRecord(ABillsID: Integer): TsdDataRecord;

    property PhaseData: TObject read FPhaseData;
    property MainBillsTree: TsdIDTree read GetMainBillsTree;
    property CanModified: Boolean read FCanModified write FCanModified;

    property RefreshDetailGrid: TRefreshDetailGridEvent read FRefreshDetailGrid write FRefreshDetailGrid;
  end;

implementation

uses
  PhaseData, ProjectData, BillsDm, UtilMethods, ProjectProperty, ZhAPI,
  BillsCompileDm, BillsMeasureDm, mPegFilter, Math;

{$R *.dfm}

{ TZJJLData }

constructor TZJJLData.Create(APhaseData: TObject);
begin
  inherited Create(nil);
  FPhaseData := APhaseData;
  FOrgDataList := TList.Create;
end;

destructor TZJJLData.Destroy;
begin
  ClearObjects(FOrgDataList);
  FOrgDataList.Free;
  inherited;
end;

function TZJJLData.GetMainBillsTree: TsdIDTree;
begin
  with TProjectData(TPhaseData(FPhaseData).ProjectData) do
    Result := BillsMeasureData.BillsMeasureTree;
end;

procedure TZJJLData.Open(AConnection: TADOConnection);
begin
  sdpZJJL.Connection := AConnection;
  sddZJJL.Open;
  sddZJJL.AddIndex('idxID', 'ID');
  sddZJJL.AddIndex('idxBillsID', 'BillsID');
  sdvZJJL.Open;

  sdpHistory.Connection := AConnection;
  sddHistory.Open;
  sddHistory.AddIndex('idxID', 'ID');
  sddHistory.AddIndex('idxHistory', 'BillsID;Type;FieldName');
  sddHistory.AddIndex('idxGatherHistory', 'BillsID;Type;B_Code;Name;Units;Price;FieldName');

  sdpZJJLDetail.Connection := AConnection;
  sddZJJLDetail.Open;
  sddZJJLDetail.AddIndex('idxID', 'ID');

  CheckZjjlVerison;
end;

procedure TZJJLData.Save;
begin
  sddZJJL.Save;
  sddHistory.Save;
  sddZJJLDetail.Save;
end;

function TZJJLData.CheckLastXmj(AID: Integer): Boolean;
var
  stnNode, stnChild: TsdIDTreeNode;
begin
  stnNode := MainBillsTree.FindNode(AID);
  Result := stnNode.Rec.ValueByName('Code').AsString <> '';
  if not stnNode.HasChildren or not Result then Exit;
  stnChild := stnNode.FirstChild;
  Result := stnChild.Rec.ValueByName('B_Code').AsString <> '';
end;

procedure TZJJLData.AssignedCurData(ARec: TsdDataRecord; ADetailGrid: TZJGrid);
var
  iRowIndex: Integer;
begin
  ADetailGrid.OnCellTextChanged := nil;
  ADetailGrid.FixedRowCount := 0;
  ADetailGrid.FixedColCount := 0;
  ADetailGrid.ColCount := 1;
  ADetailGrid.RowCount := 11;
  ADetailGrid.DefaultColWidth := 300;
  for iRowIndex := 0 to ADetailGrid.RowCount - 1 do
    ADetailGrid[0, iRowIndex].Align := gaTopLeft;
  if not Assigned(ARec) then Exit;
  ADetailGrid.Cells[0, 0].Text := '变更令号：' + ARec.ValueByName('BGLCode').AsString;
  ADetailGrid.Cells[0, 1].Text := '部位：' + ARec.ValueByName('PegName').AsString;
  ADetailGrid.Cells[0, 2].Text := '起始桩号：' + ARec.ValueByName('BeginPeg').AsString;
  ADetailGrid.Cells[0, 3].Text := '终止桩号：' + ARec.ValueByName('EndPeg').AsString;
  ADetailGrid.Cells[0, 4].Text := '分部分项工程：' + ARec.ValueByName('FBFXName').AsString;
  ADetailGrid.Cells[0, 5].Text := '计量单元：' + ARec.ValueByName('UnitName').AsString;
  ADetailGrid.Cells[0, 6].Text := '图号：' + ARec.ValueByName('DrawingCode').AsString;
  ADetailGrid.Cells[0, 7].Text := '计算式说明：';
  ADetailGrid.Cells[0, 8].Text := ARec.ValueByName('FormulaMemo').AsString;
  ADetailGrid.Cells[0, 8].Align := gaTopLeft;
  ADetailGrid.RowHeights[8] := 57;
  ADetailGrid.Cells[0, 9].Text := '计算草图几何尺寸：';
  ADetailGrid.Cells[0, 10].Text := ARec.ValueByName('RelaFile').AsString;
  ADetailGrid.Cells[0, 10].Align := gaTopLeft;
  ADetailGrid.RowHeights[10] := 57;
  ADetailGrid.OnCellTextChanged := DetailGridCellTextChanged;
end;

procedure TZJJLData.GenerateAll;
begin
  RestoreOrgData;
  FNewID := 1;
  DeleteAll;
  GenerateNode(MainBillsTree.FirstNode);
  TPhaseData(FPhaseData).PhaseProperty.ZjjlVersion := 1;
end;

procedure TZJJLData.GenerateNode(ANode: TsdIDTreeNode);
begin
  if not Assigned(ANode) then Exit;
  if CheckLastXmj(ANode.ID) then
    GenerateLastXmj(ANode)
  else
    GenerateNode(ANode.FirstChild);
  GenerateNode(ANode.NextSibling);
end;

procedure TZJJLData.GenerateLastXmj(ANode: TsdIDTreeNode);
begin
  if CheckMeasureExist(ANode) then
    GenerateZJJLNode(ANode, 0);
end;

function TZJJLData.CheckBGExist(ANode: TsdIDTreeNode): Boolean;

  function CheckBGLExist(AID: Integer): Boolean;
  var
    StageRec: TsdDataRecord;
  begin
    StageRec := TPhaseData(FPhaseData).StageData.StageRecord(AID);
    Result := Assigned(StageRec) and
           ((StageRec.ValueByName('QcBGLCode').AsString <> '')
           or (StageRec.ValueByName('PcBGLCode').AsString <> ''));
  end;

var
  iChild: Integer;
begin
  Result := False;
  if not Assigned(ANode) then Exit;
  if ANode.HasChildren then
  begin
    for iChild := 0 to ANode.ChildCount - 1 do
    begin
      Result := Result or CheckBGExist(ANode.ChildNodes[iChild]);
      if Result then Break;
    end;
  end
  else
    Result := CheckBGLExist(ANode.ID);
end;

procedure TZJJLData.DeleteAll;
begin
  sddZJJL.DeleteAll;
  sddZJJLDetail.DeleteAll;
end;

procedure TZJJLData.RestoreOrgData;

  function CreateNodeData(ARec: TsdDataRecord): TZJJLNode;
  begin
    Result := TZJJLNode.Create;

    Result.FBillsID := ARec.ValueByName('BillsID').AsInteger;
    Result.FCode := ARec.ValueByName('Code').AsString;
    Result.FCertificateCode := ARec.ValueByName('CertificateCode').AsString;
    Result.FBillsCode := ARec.ValueByName('BillsCode').AsString;
    Result.FFormulaMemo := ARec.ValueByName('FormulaMemo').AsString;
    Result.FRelaFile := ARec.ValueByName('RelaFile').AsString;
    Result.FType := ARec.ValueByName('Type').AsInteger;
  end;

var
  i: Integer;
  vRec: TsdDataRecord;
begin
  FOrgDataList.Clear;
  for i := 0 to sddZJJL.RecordCount - 1 do
  begin
    vRec := sddZJJL.Records[i];
    FOrgDataList.Add(CreateNodeData(vRec));
  end;
end;

function TZJJLData.CheckMeasureExist(ANode: TsdIDTreeNode): Boolean;

  function CheckStageCompleteData(AID: Integer): Boolean;
  var
    StageRec: TsdDataRecord;
  begin
    StageRec := TPhaseData(FPhaseData).StageData.StageRecord(AID);
    Result := Assigned(StageRec) and
           ((StageRec.ValueByName('GatherQuantity').AsFloat <> 0)
           or (StageRec.ValueByName('GatherTotalPrice').AsFloat <> 0));
  end;

var
  iChild: Integer;
begin
  Result := False;
  if not Assigned(ANode) then Exit;
  if ANode.HasChildren then
  begin
    for iChild := 0 to ANode.ChildCount - 1 do
    begin
      Result := Result or CheckMeasureExist(ANode.ChildNodes[iChild]);
      if Result then Break;
    end;
  end
  else
    Result := CheckStageCompleteData(ANode.ID);
end;

procedure TZJJLData.DetailGridCellCanEdit(Sender: TObject;
  const ACoord: TPoint; var Allow: Boolean);
begin
  Allow := ((ACoord.Y = 6) or (ACoord.Y = 8)) and
      not TPhaseData(FPhaseData).StageDataReadOnly;
end;

function TZJJLData.GetInfoRecByCode(ABillsID: Integer): TZJJLInfoRec;
var
  vPeg, vNode: TsdIDTreeNode;
begin
  vNode := MainBillsTree.FindNode(ABillsID);
  vPeg := GetPegNode(vNode);
  Result.BGLCode := GetBGLCode(vNode);
  Result.PegName := GetFieldStrDef(vPeg, 'Name', '');
  Result.FBFXName := GetFBFXName(vNode, vPeg);
  Result.UnitName := GetFieldStrDef(vNode, 'Name', '');
  Result.DrawingCode := GetDrawingCode(vNode);
end;

procedure TZJJLData.DetailGridCellTextChanged(Sender: TObject; Col,
  Row: Integer);
var
  Rec: TsdDataRecord;
begin
  Rec := sdvZJJL.Current;
  if not Assigned(Rec) then Exit;

  if (Row = 8) then
    Rec.ValueByName('FormulaMemo').AsString := TZJGrid(Sender).Cells[Col, Row].Text
  else if (Row = 10) then
    Rec.ValueByName('RelaFile').AsString := TZJGrid(Sender).Cells[Col, Row].Text;
end;

procedure TZJJLData.GenerateAllByB_Code;
begin
  RestoreOrgData;
  FNewID := 1;
  DeleteAll;
  GenerateNodeByB_Code(MainBillsTree.FirstNode);
  TPhaseData(FPhaseData).PhaseProperty.ZjjlVersion := 1;
end;

procedure TZJJLData.GenerateNodeByB_Code(ANode: TsdIDTreeNode);
begin
  if not Assigned(ANode) then Exit;
  if ANode.HasChildren then
    GenerateNodeByB_Code(ANode.FirstChild)
  else
    GenerateLeafNode(ANode);
  GenerateNodeByB_Code(ANode.NextSibling);
end;

procedure TZJJLData.GenerateLeafNode(ANode: TsdIDTreeNode);
begin
  if (ANode.Rec.ValueByName('B_Code').AsString <> '') and
      CheckLeafNodeMeasureExist(ANode.ID) then
    GenerateZJJLNode(ANode, 1);
end;

procedure TZJJLData.GenerateZJJLNode(ANode: TsdIDTreeNode;
  AType: Integer);

  function GetOrgZJJLNode(ABillsID: Integer): TZJJLNode;
  var
    iIndex: Integer;
  begin
    Result := nil;
    for iIndex := 0 to FOrgDataList.Count - 1 do
      // 7/30新增了一个“总量控制”模式，Type字段控制。真的是要疯掉了。
      if (TZJJLNode(FOrgDataList.Items[iIndex]).FBillsID = ABillsID)
          and (TZJJLNode(FOrgDataList.Items[iIndex]).FType = AType) then
      begin
        Result := TZJJLNode(FOrgDataList.Items[iIndex]);
        Break;
      end;
  end;

  function GetFieldValue(const AFieldName, ADefaultValue: string): string;
  var
    HistoryRec: TsdDataRecord;
  begin
    HistoryRec := sddHistory.FindKey('idxHistory', VarArrayOf([ANode.ID, AType, AFieldName]));
    if Assigned(HistoryRec) then
      Result := HistoryRec.ValueByName('FieldValue').AsString
    else
      Result := ADefaultValue;
  end;

var
  ZJJLNode: TZJJLNode;
  vInfoRec: TZJJLInfoRec;
  Rec: TsdDataRecord;
begin
  Rec := sddZJJL.Add;
  Rec.ValueByName('ID').AsInteger := FNewID;
  Rec.ValueByName('BillsID').AsInteger := ANode.ID;
  Rec.ValueByName('Type').AsInteger := AType;
  if AType = Ord(ztFx) then
    Rec.ValueByName('BillsCode').AsString := ANode.Rec.ValueByName('Code').AsString
  else if AType = Ord(ztGcl) then
    Rec.ValueByName('BillsCode').AsString := ANode.Rec.ValueByName('B_Code').AsString;

  Rec.ValueByName('Code').AsString := GetFieldValue('Code', GetNewCode(FNewID));
  Rec.ValueByName('CertificateCode').AsString := GetFieldValue('CertificateCode', '');
  Rec.ValueByName('FormulaMemo').AsString := GetFieldValue('FormulaMemo', '');
  Rec.ValueByName('RelaFile').AsString := GetFieldValue('RelaFile', '');

  vInfoRec := GetInfoRec_2(ANode.ID, AType);
  Rec.ValueByName('BGLCode').AsString := GetFieldValue('BGLCode', vInfoRec.BGLCode);
  Rec.ValueByName('PegName').AsString := GetFieldValue('PegName', vInfoRec.PegName);
  Rec.ValueByName('BeginPeg').AsString := GetFieldValue('BeginPeg', vInfoRec.BeginPeg);
  Rec.ValueByName('EndPeg').AsString := GetFieldValue('EndPeg', vInfoRec.EndPeg);
  Rec.ValueByName('FBFXName').AsString := GetFieldValue('FBFXName', vInfoRec.FBFXName);
  Rec.ValueByName('UnitName').AsString := GetFieldValue('UnitName', vInfoRec.UnitName);
  Rec.ValueByName('DrawingCode').AsString := GetFieldValue('DrawingCode', vInfoRec.DrawingCode);

  Rec := sddZJJLDetail.Add;
  Rec.ValueByName('ID').AsInteger := FNewID;
  Rec.ValueByName('BillsID').AsInteger := ANode.ID;

  Inc(FNewID);
end;

function TZJJLData.GetInfoRecByB_Code(ABillsID: Integer): TZJJLInfoRec;
var
  vNode: TsdIDTreeNode;
begin
  vNode := MainBillsTree.FindNode(ABillsID);
  Result.BGLCode := GetBGLCode(vNode);
  if Assigned(vNode.Parent) then
    Result.PegName := vNode.Parent.Rec.ValueByName('Name').AsString;
  Result.FBFXName := '';
  Result.UnitName := vNode.Rec.ValueByName('Name').AsString;
  Result.DrawingCode := GetDrawingCode(vNode);
end;

function TZJJLData.GetInfoRec(ABillsID, AType: Integer): TZJJLInfoRec;
begin
  if AType = 0 then
    Result := GetInfoRecByCode(ABillsID)
  else if AType = 1 then
    Result := GetInfoRecByB_Code(ABillsID);
end;

procedure TZJJLData.LocateBills;
var
  Rec: TsdDataRecord;
begin
  with TProjectData(TPhaseData(FPhaseData).ProjectData) do
  begin
    Rec := BillsData.sddBills.FindKey('idxID', sdvZJJL.Current.ValueByName('BillsID').AsInteger);
    BillsMeasureData.sdvBillsMeasure.LocateInControl(Rec);
  end;
end;

function TZJJLData.GetDrawingCode(ANode: TsdIDTreeNode): string;
var
  vNode: TBillsIDTreeNode;
begin
  Result := '';
  vNode := TBillsIDTreeNode(ANode);
  while (Result = '') and Assigned(vNode) do
  begin
    if Assigned(vNode.Rec) then
      Result := vNode.Rec.DrawingCode.AsString;
    vNode := TBillsIDTreeNode(vNode.Parent);
  end;
end;

procedure TZJJLData.CheckZjjlVerison;

  procedure LoadVersion0Info;
  var
    i: Integer;
    Rec: TsdDataRecord;
    ZJJLInfoRec: TZJJLInfoRec;
  begin
    for i := 0 to sddZJJL.RecordCount - 1 do
    begin
      Rec := sddZJJL.Records[i];
      ZJJLInfoRec := GetInfoRec(Rec.ValueByName('BillsID').AsInteger, Rec.ValueByName('Type').AsInteger);
      Rec.ValueByName('BGLCode').AsString := ZJJLInfoRec.BGLCode;
      Rec.ValueByName('PegName').AsString := ZJJLInfoRec.PegName;
      Rec.ValueByName('FBFXName').AsString := ZJJLInfoRec.FBFXName;
      Rec.ValueByName('UnitName').AsString := ZJJLInfoRec.UnitName;
      Rec.ValueByName('DrawingCode').AsString := ZJJLInfoRec.DrawingCode;
    end;
  end;

  procedure LoadVersion0History;
  var
    i, j, iNewID: Integer;
    Rec: TsdDataRecord;
  begin
    for i := 0 to sddZJJL.RecordCount - 1 do
    begin
      Rec := sddZJJL.Records[i];
      SetHistory('CertificateCode', Rec.ValueByName('CertificateCode').AsString, Rec);
      SetHistory('FormulaMemo', Rec.ValueByName('FormulaMemo').AsString, Rec);
      SetHistory('RelaFile', Rec.ValueByName('RelaFile').AsString, Rec);
    end;
  end;

begin
  if TPhaseData(PhaseData).PhaseProperty.ZjjlVersion = 0 then
  begin
    TPhaseData(PhaseData).PhaseProperty.ZJJLPreText := TProjectData(TPhaseData(PhaseData).ProjectData).ProjProperties.ZJJLPreText;
    LoadVersion0Info;
    LoadVersion0History;
  end;
end;

function TZJJLData.GetInfoRec_2(ABillsID, AType: Integer): TZJJLInfoRec;
begin
  if AType = 0 then
    Result := GetInfoRecByCode_2(ABillsID)
  else if AType = 1 then
    Result := GetInfoRecByB_Code_2(ABillsID);
end;

function TZJJLData.GetInfoRecByB_Code_2(ABillsID: Integer): TZJJLInfoRec;
var
  vNode: TsdIDTreeNode;
  sPeg: string;    
  vPegFilter: TPegStrFilter;
begin
  vNode := MainBillsTree.FindNode(ABillsID);
  sPeg := GetPegName(vNode);
  Result.BGLCode := GetBGLCode(vNode);
  if Assigned(vNode.Parent) then
    Result.PegName := vNode.Parent.Rec.ValueByName('Name').AsString;
  if sPeg <> '' then
  begin
    vPegFilter := TPegStrFilter.Create;
    vPegFilter.PegStr := sPeg;
    Result.BeginPeg := vPegFilter.BeginPeg;
    Result.EndPeg := vPegFilter.EndPeg;
    vPegFilter.Free;
  end;
  Result.UnitName := vNode.Rec.ValueByName('Name').AsString;
  Result.DrawingCode := GetDrawingCode(vNode);
end;

function TZJJLData.GetInfoRecByCode_2(ABillsID: Integer): TZJJLInfoRec;
var
  vPeg, vNode: TsdIDTreeNode;
  vPegFilter: TPegStrFilter;
begin
  vNode := MainBillsTree.FindNode(ABillsID);
  vPeg := GetPegNode(vNode);
  Result.BGLCode := GetBGLCode(vNode);
  Result.PegName := GetFieldStrDef(vPeg, 'Name', '');
  if Result.PegName <> '' then
  begin
    vPegFilter := TPegStrFilter.Create;
    vPegFilter.PegStr := Result.PegName;
    Result.BeginPeg := vPegFilter.BeginPeg;
    Result.EndPeg := vPegFilter.EndPeg;
    vPegFilter.Free;
  end;
  Result.FBFXName := GetFBFXName(vNode, vPeg);
  Result.UnitName := GetFieldStrDef(vNode, 'Name', '');
  Result.DrawingCode := GetDrawingCode(vNode);
end;

function TZJJLData.GetBGLCode(ANode: TsdIDTreeNode): string;
begin
  with TProjectData(TPhaseData(FPhaseData).ProjectData) do
    Result := BillsMeasureData.GatherRelaBGL(ANode);
end;

function TZJJLData.GetPegName(ANode: TsdIDTreeNode): string;
var
  vPeg: TsdIDTreeNode;
begin
  vPeg := GetPegNode(ANode);
  Result := GetFieldStrDef(vPeg, 'Name', '');
end;

function TZJJLData.GetFieldStrDef(ANode: TsdIDTreeNode; const AFieldName,
  ADef: string): string;
begin
  if Assigned(ANode) then
    Result := ANode.Rec.ValueByName(AFieldName).AsString
  else
    Result := '';
end;

function TZJJLData.GetPegNode(ANode: TsdIDTreeNode): TsdIDTreeNode;
begin
  Result := nil;
  if not Assigned(ANode) then Exit;
  if CheckPeg(ANode.Rec.ValueByName('Name').AsString) then
    Result := ANode
  else
    Result := GetPegNode(ANode.Parent);
end;

function TZJJLData.GetFBFXName(ANode, APegNode: TsdIDTreeNode): string;
var
  vCurNode: TsdIDTreeNode;
begin
  Result := '';
  if not Assigned(ANode) then Exit;
  // 如果计量单元节点的名称为桩号（转化为判断计量单元节点与桩号节点为同一个）
  if not Assigned(APegNode) or (ANode.ID = APegNode.ID) then
  begin
    // 取树结构的第三、四层节点的名称
    Result := GetNameByLevel(ANode, 2);
    if (Result <> '') and (GetNameByLevel(ANode, 3) <> '') then
      Result := Result + ',';
    Result := Result + GetNameByLevel(ANode, 3);
  end
  // 否则，合并[桩号节点的子节点]至[计量单元节点的父节点]的名称
  else
  begin
    vCurNode := ANode.Parent;
    // 转化为判断层次，层次大于[桩号节点的子节点]的层次
    while vCurNode.Level > APegNode.Level do
    begin
      Result := vCurNode.Rec.ValueByName('Name').AsString + ',' + Result;
      vCurNode := vCurNode.Parent;
    end;
  end;
end;

function TZJJLData.GetNameByLevel(ANode: TsdIDTreeNode;
  ALevel: Integer): string;
begin
  Result := '';
  if not Assigned(ANode) then Exit;
  if ANode.Level = ALevel then
    Result := ANode.Rec.ValueByName('Name').AsString
  else if ANode.Level > ALevel then
    Result := GetNameByLevel(ANode.Parent, ALevel);
end;

procedure TZJJLData.SetHistory(const AFieldName, AValue: string;
  AZJJL_Rec: TsdDataRecord);
var
  HistoryRec: TsdDataRecord;
begin
  if AZJJL_Rec.ValueByName('Type').AsInteger <> Integer(ztGclGather) then
    HistoryRec := sddHistory.FindKey('idxHistory', VarArrayOf([AZJJL_Rec.ValueByName('BillsID').AsInteger,
      AZJJL_Rec.ValueByName('Type').AsInteger, AFieldName]))
  else
    HistoryRec := sddHistory.FindKey('idxGatherHistory', VarArrayOf([AZJJL_Rec.ValueByName('GatherBillsID').AsInteger,
      AZJJL_Rec.ValueByName('Type').AsInteger, AZJJL_Rec.ValueByName('B_Code').AsString,
      AZJJL_Rec.ValueByName('Name').AsString, AZJJL_Rec.ValueByName('Units').AsString,
      AZJJL_Rec.ValueByName('Price').AsFloat, AFieldName]));

  if not Assigned(HistoryRec) then
  begin
    HistoryRec := sddHistory.Add;
    HistoryRec.ValueByName('ID').AsInteger := GetNewHistoryID;
    if AZJJL_Rec.ValueByName('Type').AsInteger <> Integer(ztGclGather) then
      HistoryRec.ValueByName('BillsID').AsInteger := AZJJL_Rec.ValueByName('BillsID').AsInteger
    else
      HistoryRec.ValueByName('BillsID').AsInteger := AZJJL_Rec.ValueByName('GatherBillsID').AsInteger;
    HistoryRec.ValueByName('Type').AsInteger := AZJJL_Rec.ValueByName('Type').AsInteger;
    HistoryRec.ValueByName('FieldName').AsString := AFieldName;

    HistoryRec.ValueByName('B_Code').AsString := AZJJL_Rec.ValueByName('B_Code').AsString;
    HistoryRec.ValueByName('Name').AsString := AZJJL_Rec.ValueByName('Name').AsString;
    HistoryRec.ValueByName('Units').AsString := AZJJL_Rec.ValueByName('Units').AsString;
    HistoryRec.ValueByName('Price').AsFloat := AZJJL_Rec.ValueByName('Price').AsFloat;
  end;
  HistoryRec.ValueByName('FieldValue').AsString := AValue;
end;

function TZJJLData.GetNewHistoryID: Integer;
var
  idx: TsdIndex;
begin
  idx := sddHistory.FindIndex('idxID');
  if idx.RecordCount = 0 then
    Result := 1
  else
    Result := idx.Records[idx.RecordCount - 1].ValueByName('ID').AsInteger + 1;
end;

procedure TZJJLData.sdvZJJLBeforeValueChange(AValue: TsdValue;
  const NewValue: Variant; var Allow: Boolean);

  function CheckFieldNeedHistory(const AFieldName: string): Boolean;
  begin
    Result := SameText(AFieldName, 'Code') or
              SameText(AFieldName, 'CertificateCode') or
              SameText(AFieldName, 'BGLCode') or
              SameText(AFieldName, 'PegName') or
              SameText(AFieldName, 'BeginPeg') or
              SameText(AFieldName, 'EndPeg') or
              SameText(AFieldName, 'FBFXName') or
              SameText(AFieldName, 'UnitName') or
              SameText(AFieldName, 'DrawingCode') or
              SameText(AFieldName, 'FormulaMemo') or
              SameText(AFieldName, 'RelaFile');
  end;

var
  sNewValue: string;
begin
  if CheckFieldNeedHistory(AValue.FieldName) then
  begin
    sNewValue := VarToStrDef(NewValue, '');
    if sNewValue <> AValue.AsString then
      SetHistory(AValue.FieldName, sNewValue, AValue.Owner);
  end;
end;

procedure TZJJLData.sdvZJJLAfterDeleteRecord(ARecord: TsdDataRecord);
begin
  if ARecord.ValueByName('Type').AsInteger <> Integer(ztGclGather) then
    DeleteHistory(ARecord.ValueByName('BillsID').AsInteger, ARecord.ValueByName('Type').AsInteger)
  else
    DeleteGatherHistory(ARecord);
end;

procedure TZJJLData.DeleteHistory(ABillsID, AType: Integer);
var
  i: Integer;
  Rec: TsdDataRecord;
begin
  i := 0;
  while (i < sddHistory.RecordCount) do
  begin
    Rec := sddHistory.Records[i];

    if (ABillsID = Rec.ValueByName('BillsID').AsInteger) and
       (AType = Rec.ValueByName('Type').AsInteger) then
      sddHistory.Remove(Rec)
    else
      Inc(i);
  end;
end;

procedure TZJJLData.GenerateAllByB_CodeGather;

  procedure GenerateZJJLGather(AManager: TmgZJJLManager; ANode: TBillsIDTreeNode);
  var
    iChild: Integer;
    vChild: TBillsIDTreeNode;
  begin
    if ANode.HasChildren then
    begin
      iChild := 0;
      vChild := TBillsIDTreeNode(ANode.NextNode);
      while iChild < ANode.PosterityCount do
      begin
        if not vChild.HasChildren and CheckLeafNodeMeasureExist(vChild.ID) then
          AManager.AddZJJLAndDetail(vChild, ANode);
        vChild := TBillsIDTreeNode(vChild.NextNode);
        Inc(iChild);
      end;
    end
    else if CheckLeafNodeMeasureExist(ANode.ID) then
      AManager.AddZJJLAndDetail(ANode, ANode);
  end;

  procedure GenerateZJJLGatherByNode(AManager: TmgZJJLManager; ANode: TsdIDTreeNode);
  begin
    if not Assigned(ANode) then Exit;
    if ANode.HasChildren and not TBillsIDTreeNode(ANode).Rec.IsGatherZJJL.AsBoolean then
      GenerateZJJLGatherByNode(AManager, ANode.FirstChild)
    else
      GenerateZJJLGather(AManager, TBillsIDTreeNode(ANode));
    GenerateZJJLGatherByNode(AManager, ANode.NextSibling);
  end;

  function GetFieldValue(AZJJL: TmgZJJL; const AFieldName, ADefaultValue: string): string;
  var
    HistoryRec: TsdDataRecord;
  begin
    HistoryRec := sddHistory.FindKey('idxGatherHistory', VarArrayOf([AZJJL.GatherNode.ID, Integer(ztGclGather), AZJJL.B_Code, AZJJL.Name, AZJJL.Units, AZJJL.Price, AFieldName]));
    if Assigned(HistoryRec) then
      Result := HistoryRec.ValueByName('FieldValue').AsString
    else
      Result := ADefaultValue;
  end;

  procedure SaveGatherData(AManager: TmgZJJLManager);
  var
    i, j: Integer;
    vZJJL: TmgZJJL;
    Rec: TsdDataRecord;
  begin
    for i := 0 to AManager.ZJJLCount - 1 do
    begin
      vZJJL := AManager.ZJJL[i];
      Rec := sddZJJL.Add;
      Rec.ValueByName('ID').AsInteger := i + 1;
      Rec.ValueByName('BillsID').AsInteger := vZJJL.Detail[0].RelaNode.ID;
      Rec.ValueByName('Type').AsInteger := Integer(ztGclGather);
      Rec.ValueByName('GatherBillsID').AsInteger := vZJJL.GatherNode.ID;
      Rec.ValueByName('BillsCode').AsString := vZJJL.B_Code;
      Rec.ValueByName('B_Code').AsString := vZJJL.B_Code;
      Rec.ValueByName('Name').AsString := vZJJL.Name;
      Rec.ValueByName('Units').AsString := vZJJL.Units;
      Rec.ValueByName('Price').AsFloat := vZJJL.Price;

      Rec.ValueByName('Code').AsString := GetFieldValue(vZJJL, 'Code', GetNewCode(i+1));
      Rec.ValueByName('CertificateCode').AsString := GetFieldValue(vZJJL, 'CertificateCode', '');
      Rec.ValueByName('FormulaMemo').AsString := GetFieldValue(vZJJL, 'FormulaMemo', '');
      Rec.ValueByName('RelaFile').AsString := GetFieldValue(vZJJL, 'RelaFile', '');

      Rec.ValueByName('BGLCode').AsString := GetFieldValue(vZJJL, 'BGLCode', vZJJL.BGLCode);
      Rec.ValueByName('PegName').AsString := GetFieldValue(vZJJL, 'PegName', '');
      Rec.ValueByName('BeginPeg').AsString := GetFieldValue(vZJJL, 'BeginPeg', vZJJL.BeginPeg);
      Rec.ValueByName('EndPeg').AsString := GetFieldValue(vZJJL, 'EndPeg', vZJJL.EndPeg);
      Rec.ValueByName('FBFXName').AsString := GetFieldValue(vZJJL, 'FBFXName', vZJJL.GatherNode.Rec.Name.AsString);
      Rec.ValueByName('UnitName').AsString := GetFieldValue(vZJJL, 'UnitName', '');
      Rec.ValueByName('DrawingCode').AsString := GetFieldValue(vZJJL, 'DrawingCode', vZJJL.DrawingCode);
      for j := 0 to vZJJL.DetailCount - 1 do
      begin
        Rec := sddZJJLDetail.Add;
        Rec.ValueByName('ID').AsInteger := i + 1;
        Rec.ValueByName('BillsID').AsInteger := vZJJL.Detail[j].RelaNode.ID;
      end;
    end;
  end;

var
  vManager: TmgZJJLManager;
begin
  vManager := TmgZJJLManager.Create;
  try
    DeleteAll;
    GenerateZJJLGatherByNode(vManager, MainBillsTree.FirstNode);
    SaveGatherData(vManager);
    TPhaseData(FPhaseData).PhaseProperty.ZjjlVersion := Integer(ztGclGather);
  finally
    vManager.Free;
  end;
end;

function TZJJLData.CheckLeafNodeMeasureExist(AID: Integer): Boolean;
var
  StageRec: TsdDataRecord;
begin
  StageRec := TPhaseData(FPhaseData).StageData.StageRecord(AID);
  Result := Assigned(StageRec) and
         ((StageRec.ValueByName('GatherQuantity').AsFloat <> 0)
         or (StageRec.ValueByName('GatherTotalPrice').AsFloat <> 0));
end;

function TZJJLData.GetNewCode(ANewID: Integer): string;
begin
  with TProjectData(TPhaseData(FPhaseData).ProjectData) do
  begin
    Result := ProjProperties.ZJJLPreText;
    if (Result <> '') and (Result[Length(Result)] <> '-') then
      Result := Result + '-';
    Result := Result + Format('%d-%d', [PhaseIndex, ANewID]);
  end;
end;

function TZJJLData.FindZJJLRecord(ABillsID: Integer): TsdDataRecord;
var
  ADetailRec: TsdDataRecord;
begin
  if sddZJJLDetail.RecordCount > 0 then
  begin
    ADetailRec := sddZJJLDetail.Locate('BillsID', ABillsID);
    if Assigned(ADetailRec) then
      Result := sddZJJL.FindKey('idxID', ADetailRec.ValueByName('ID').AsInteger)
    else
      Result := nil;
  end
  else
    Result := sddZJJL.Locate('BillsID', ABillsID);
end;

procedure TZJJLData.DeleteGatherHistory(ARecord: TsdDataRecord);
var
  i: Integer;
  Rec: TsdDataRecord;
begin
  i := 0;
  while (i < sddHistory.RecordCount) do
  begin
    Rec := sddHistory.Records[i];

    if (ARecord.ValueByName('GatherBillsID').AsInteger = Rec.ValueByName('BillsID').AsInteger) and
       (ARecord.ValueByName('Type').AsInteger = Rec.ValueByName('Type').AsInteger) and
       (ARecord.ValueByName('B_Code').AsString = Rec.ValueByName('B_Code').AsString) and
       (ARecord.ValueByName('Name').AsString = Rec.ValueByName('Name').AsString) and
       (ARecord.ValueByName('Units').AsString = Rec.ValueByName('Units').AsString) and
       (Abs(ARecord.ValueByName('Price').AsFloat - Rec.ValueByName('Price').AsFloat) < 0.00001) then
      sddHistory.Remove(Rec)
    else
      Inc(i);
  end;
end;

procedure TZJJLData.sdvZJJLCurrentChanged(ARecord: TsdDataRecord);
begin
  if Assigned(FRefreshDetailGrid) then
    FRefreshDetailGrid;
end;

function TZJJLData.GetZJJLCalcData(ARec: TsdDataRecord;
  const AFileName: string): Double;
var
  vIdx: TsdIndex;
  iBegin, iEnd, i: Integer;
  vRec, vStageRec: TsdDataRecord;
begin
  Result := 0;
  vIdx := sddZJJLDetail.FindIndex('idxID');
  iBegin := vIdx.FindKeyIndex(ARec.ValueByName('ID').AsInteger);
  iEnd := vIdx.FindKeyLastIndex(ARec.ValueByName('ID').AsInteger);
  if iBegin <> -1 then
  begin
    for i := iBegin to iEnd do
    begin
      vRec := vIdx.Records[i];
      vStageRec := TPhaseData(FPhaseData).StageData.StageRecord(vRec.ValueByName('BillsID').AsInteger);
      if Assigned(vStageRec) then
        Result := QuantityRoundTo(Result + vStageRec.ValueByName(AFileName).AsFloat);
    end;
  end;
end;

end.
