unit BillsTree;

interface

uses
  sdIDTree, sdDB, mDataRecord;

type
  TBillsIDTreeNode = class(TsdIDTreeNode)
  private     
    FStageRec: TStageRecord;

    FDealQuantity: Double;
    FDealTotalPrice: Double;
    FQcQuantity: Double;
    FQcTotalPrice: Double;
    FPcQuantity: Double;
    FPcTotalPrice: Double;
    FGatherTotalPrice: Double;
    FGatherQuantity: Double;

    function HasCountPrice: Boolean;
    function HasTotalPrice: Boolean;

    function GetRec: TBillsRecord;
  public
    function CanUpLevel: Boolean; override;
    function CanDownLevel: Boolean; override;
    function CanUpMove: Boolean; override;
    function CanDownMove: Boolean; override;

    function UpLevel: Boolean; override;
    function DownLevel: Boolean; override;

    function HasMeasure: Boolean;
    function HasLedger: Boolean;

    function CountPriceEnable: Boolean;
    function TotalPriceEnable: Boolean;

    property Rec: TBillsRecord read GetRec;

    property DealQuantity: Double read FDealQuantity write FDealQuantity;
    property DealTotalPrice: Double read FDealTotalPrice write FDealTotalPrice;
    property QcQuantity: Double read FQcQuantity write FQcQuantity;
    property QcTotalPrice: Double read FQcTotalPrice write FQcTotalPrice;
    property PcQuantity: Double read FPcQuantity write FPcQuantity;
    property PcTotalPrice: Double read FPcTotalPrice write FPcTotalPrice;
    property GatherQuantity: Double read FGatherQuantity write FGatherQuantity;
    property GatherTotalPrice: Double read FGatherTotalPrice write FGatherTotalPrice;

    // Cache Data
    property StageRec: TStageRecord read FStageRec write FStageRec;
  end;

  TReCalculateNode = procedure(AID: Integer) of object;

  TBillsIDTree = class(TsdIDTree)
  protected
    function CreateItem: TsdIDTreeNode; override;
  public
    function CanDelete(ANode: TsdIDTreeNode): Boolean; override;
    function DeleteNode(ANode: TsdIDTreeNode): Boolean; override;

    function Add(AParentID, ANextSiblingID: TsdTreeNodeID): TsdIDTreeNode; override;

    procedure DoOnReCalcNode(AID: Integer); overload; virtual; abstract;
    procedure DoOnReCalcNode(ANode: TsdIDTreeNode); overload; virtual; abstract;
  end;

  TCompileBillsIDTree = class(TBillsIDTree)
  private
    FOnReCalcNode: TReCalculateNode;
  public
    procedure DoOnReCalcNode(AID: Integer); overload; override;
    procedure DoOnReCalcNode(ANode: TsdIDTreeNode); overload; override;

    property OnReCalcNode: TReCalculateNode read FOnReCalcNode write FOnReCalcNode;
  end;

  TMeasureBillsIDTreeNode = class(TBillsIDTreeNode)
  private
    FStageRec: TStageRecord;
  public
    // Cache Data
    property StageRec: TStageRecord read FStageRec write FStageRec;
  end;

  TMeasureBillsIDTree = class(TBillsIDTree)
  private
    FCompileTree: TCompileBillsIDTree;
  protected
    function CreateItem: TsdIDTreeNode; override;
  public
    procedure DoOnReCalcNode(AID: Integer); overload; override;
    procedure DoOnReCalcNode(ANode: TsdIDTreeNode); overload; override;

    property CompileTree: TCompileBillsIDTree read FCompileTree write FCompileTree;
  end;

  TEstimateIDTreeNode = class(TsdIDTreeNode)
  public
    function CanExpand: Boolean; override;
  end;

  TEstimateIDTree = class(TsdIDTree)
  public
    function CreateItem: TsdIDTreeNode; override;
  end;

implementation

{ TBillsIDTree }

function TBillsIDTree.Add(AParentID,
  ANextSiblingID: TsdTreeNodeID): TsdIDTreeNode;
begin
  // 不允许插入首层节点
  if (Selected <> nil) and (Selected.Level = 0) then
    Result := inherited Add(Selected.ID, -1)
  else
    Result := inherited Add(AParentID, ANextSiblingID);
end;

function TBillsIDTree.CanDelete(ANode: TsdIDTreeNode): Boolean;
begin
  Result := Inherited CanDelete(ANode)
            and (ANode.ID >= 100)
            and (not ANode.Rec.ValueByName('LockedLevel').AsBoolean)
            and (ANode.Rec.ValueByName('AddDealQuantity').AsFloat = 0)
            and (ANode.Rec.ValueByName('AddDealTotalPrice').AsFloat = 0)
            and (ANode.Rec.ValueByName('AddQcQuantity').AsFloat = 0)
            and (ANode.Rec.ValueByName('AddQcTotalPrice').AsFloat = 0)
            and (ANode.Rec.ValueByName('AddPcQuantity').AsFloat = 0)
            and (ANode.Rec.ValueByName('AddPcTotalPrice').AsFloat = 0);
end;

function TBillsIDTree.CreateItem: TsdIDTreeNode;
begin
  Result := TBillsIDTreeNode.Create(Self);
end;

function TBillsIDTree.DeleteNode(ANode: TsdIDTreeNode): Boolean;
var
  vParent: TsdIDTreeNode;
begin
  vParent := ANode.Parent;
  Result := inherited DeleteNode(ANode);
  DoOnReCalcNode(vParent);
end;

{ TBillsIDTreeNode }

function TBillsIDTreeNode.CanDownLevel: Boolean;
begin
  Result := Inherited CanDownLevel
            and (Level > 0)
            and (not Rec.ValueByName('LockedLevel').AsBoolean)
            and not HasMeasure;

  if Assigned(PrevSibling) then
  begin
    Result := Result
              and (PrevSibling.HasChildren or not TBillsIDTreeNode(PrevSibling).HasMeasure);
  end;
end;

function TBillsIDTreeNode.CanDownMove: Boolean;
begin
  Result := Inherited CanDownMove
            and (not Rec.ValueByName('LockedLevel').AsBoolean);
end;

function TBillsIDTreeNode.CanUpLevel: Boolean;
var
  vNextSibling: TsdIDTreeNode;
begin
  Result := Inherited CanUpLevel
            and (Level > 1)
            and (not Rec.ValueByName('LockedLevel').AsBoolean)
            and not HasMeasure;

  vNextSibling := NextSibling;
  while Assigned(vNextSibling) and Result do
  begin
    Result := Result
              and not TBillsIDTreeNode(NextSibling).HasMeasure;
    vNextSibling := vNextSibling.NextSibling;
  end;
end;

function TBillsIDTreeNode.CanUpMove: Boolean;
begin
  Result := Inherited CanUpMove
            and (not Rec.ValueByName('LockedLevel').AsBoolean);
end;

function TBillsIDTreeNode.CountPriceEnable: Boolean;
begin
  Result := HasCountPrice or (not HasTotalPrice);
end;

function TBillsIDTreeNode.DownLevel: Boolean;
var
  iOrgParentID: Integer;
begin
  iOrgParentID := ParentID;
  Result := inherited DownLevel;

  if not Result then Exit;
  // 如升级后变为父项，则清空数量、单价
  if Assigned(Parent) then
  begin
    Parent.Rec.ValueByName('OrgQuantity').AsFloat := 0;
    Parent.Rec.ValueByName('MisQuantity').AsFloat := 0;
    Parent.Rec.ValueByName('OthQuantity').AsFloat := 0;
    Parent.Rec.ValueByName('Quantity').AsFloat := 0;
    Parent.Rec.ValueByName('Price').AsFloat := 0;
  end;

  TBillsIDTree(Owner).DoOnReCalcNode(ParentID);
  TBillsIDTree(Owner).DoOnReCalcNode(iOrgParentID);
end;

function TBillsIDTreeNode.GetRec: TBillsRecord;
begin
  Result := TBillsRecord(TsdIDTreeNode(Self).Rec);
end;

function TBillsIDTreeNode.HasCountPrice: Boolean;
begin
  Result := False;
  if not Assigned(Rec) then Exit;
  Result := (Rec.Price.AsFloat <> 0)
         or (Rec.OrgQuantity.AsFloat <> 0)
         or (Rec.MisQuantity.AsFloat <> 0)
         or (Rec.OthQuantity.AsFloat <> 0)
         or (Rec.AddDealQuantity.AsFloat <> 0)
         or (Rec.AddQcQuantity.AsFloat <> 0)
         or (Rec.AddPcQuantity.AsFloat <> 0);
end;

function TBillsIDTreeNode.HasLedger: Boolean;
begin
  Result := False;
  if not Assigned(Rec) then Exit;
  Result := (Rec.Price.AsFloat <> 0)
            or (Rec.Quantity.AsFloat <> 0);
end;

function TBillsIDTreeNode.HasMeasure: Boolean;
begin
  Result := False;
  if not Assigned(Rec) then Exit;
  Result := (Rec.AddDealQuantity.AsFloat <> 0)
            or (Rec.AddDealTotalPrice.AsFloat <> 0)
            or (Rec.AddQcQuantity.AsFloat <> 0)
            or (Rec.AddQcTotalPrice.AsFloat <> 0)
            or (Rec.AddPcQuantity.AsFloat <> 0)
            or (Rec.AddPcTotalPrice.AsFloat <> 0);
end;

function TBillsIDTreeNode.HasTotalPrice: Boolean;
begin
  Result := False;
  if not Assigned(Rec) then Exit;
  Result := (Rec.OrgTotalPrice.AsFloat <> 0)
         or (Rec.MisTotalPrice.AsFloat <> 0)
         or (Rec.OthTotalPrice.AsFloat <> 0)
         or (Rec.AddDealTotalPrice.AsFloat <> 0)
         or (Rec.AddQcTotalPrice.AsFloat <> 0)
         or (Rec.AddPcTotalPrice.AsFloat <> 0);
end;

function TBillsIDTreeNode.TotalPriceEnable: Boolean;
begin
  Result := not HasCountPrice;
end;

function TBillsIDTreeNode.UpLevel: Boolean;
var
  iOrgParentID: Integer;
begin
  iOrgParentID := ParentID;
  Result := inherited UpLevel;

  if not Result then Exit;
  // 如升级后变为父项，则清空数量、单价
  if HasChildren then
  begin
    Rec.ValueByName('OrgQuantity').AsFloat := 0;
    Rec.ValueByName('MisQuantity').AsFloat := 0;
    Rec.ValueByName('OthQuantity').AsFloat := 0;
    Rec.ValueByName('Quantity').AsFloat := 0;
    Rec.ValueByName('Price').AsFloat := 0;
  end;

  TBillsIDTree(Owner).DoOnReCalcNode(iOrgParentID);
  TBillsIDTree(Owner).DoOnReCalcNode(ParentID);
end;

{ TEstimateIDTreeNode }

function TEstimateIDTreeNode.CanExpand: Boolean;
var
  iChild: Integer;
  vChild: TsdIDTreeNode;
begin
  Result := True;
  if HasChildren then
    for iChild := 0 to ChildCount - 1 do
    begin
      vChild := ChildNodes[iChild];
      if vChild.Rec.ValueByName('B_Code').AsString <> '' then
      begin
        Result := False;
        Break;
      end;
    end;
end;

{ TEstimateIDTree }

function TEstimateIDTree.CreateItem: TsdIDTreeNode;
begin
  Result := TEstimateIDTreeNode.Create(Self);
end;

{ TCompileBillsIDTree }

procedure TCompileBillsIDTree.DoOnReCalcNode(AID: Integer);
begin
  if (AID <> -1) and Assigned(FOnReCalcNode) then
    FOnReCalcNode(AID);
end;

procedure TCompileBillsIDTree.DoOnReCalcNode(ANode: TsdIDTreeNode);
begin
  if Assigned(ANode) then
    DoOnReCalcNode(ANode.ID);
end;

{ TMeasureBillsIDTree }

procedure TMeasureBillsIDTree.DoOnReCalcNode(AID: Integer);
begin
  if Assigned(FCompileTree) then
    FCompileTree.DoOnReCalcNode(AID);
end;

function TMeasureBillsIDTree.CreateItem: TsdIDTreeNode;
begin
  Result := TMeasureBillsIDTreeNode.Create(Self);  
end;

procedure TMeasureBillsIDTree.DoOnReCalcNode(ANode: TsdIDTreeNode);
begin
  if Assigned(FCompileTree) then
    FCompileTree.DoOnReCalcNode(ANode);
end;

end.
