unit ExcelImport_Bills;

// 导入0号台账/分项清单

interface

uses
  DetailExcelImport, Classes, MCacheTree, sdDB, ProjectData, OExport;

type
  Tdei_CustomBills = class(TDetailExcelImport)
  private
    FCacheTree: TBillsCacheTree;
    FCurRow: Integer;
    FIsFirstPart: Boolean;

    FWithLevelCode: Boolean;
    FWithoutGclBills: Boolean;

    FBaseTree: TBillsCacheTree;
    FFixedIDNodes: TList;

    FCodeCol: Integer;
    FB_CodeCol: Integer;
    FNameCol: Integer;
    FUnitsCol: Integer;
    FPriceCol: Integer;
    FOrgQuantityCol: Integer;
    FMisQuantityCol: Integer;
    FOthQuantityCol: Integer;
    FDgnQuantity1Col: Integer;
    FDgnQuantity2Col: Integer;
    FDrawingCol: Integer;
    FMemoCol: Integer;
    FLevelCol: Integer;
  protected
    procedure BeginImport; override;
    procedure EndImport; override;

    function GetFixedIDNode(AID: Integer): TBillsCacheNode;
    function GetFixedID(ACode, AName: string): Integer;

    procedure CheckFixedIDNodes;

    procedure LoadColumnsFromHead(ASheet: TExportWorkSheet);

    procedure LoadBaseTree(ATree: TBillsCacheTree);

    procedure LoadNode(ARow: TExportRow);
    procedure LoadNodes(ASheet: TExportWorkSheet);

    procedure WriteNode(ADataSet: TsdDataSet; ANode: TBillsCacheNode);
    procedure WriteNodes(ADataSet: TsdDataSet);

    procedure Import; override;
  public
    procedure ImportFile(const AFileName: string; AWithLevelCode, AWithoutGclBills: Boolean); overload;
  end;

implementation

uses
  Forms, Controls, ProgressHintFrm, ConstUnit, UtilMethods, ExcelImport,
  SysUtils, mDataRecord, XLSAdapter;

{ Tdei_CustomBills }

procedure Tdei_CustomBills.BeginImport;
begin
  Screen.Cursor := crHourGlass;
  ShowProgressHint('导入Excel数据', 100, '读取Excel数据', 100);

  FCurRow := 0;
  FIsFirstPart := True;
  FCacheTree := TBillsCacheTree.Create;
  FCacheTree.NewNodeID := 101;
  // 以层次编号为依据，分隔用'.'，以项目节、清单编号为依据，分隔用'-'
  if FWithLevelCode then
    FCacheTree.SeparateChar := '.'
  else
    FCacheTree.SeparateChar := '-';
  FCacheTree.AutoSort := True;

  ProjectData.DisConnectTree;
  ProjectData.BillsData.DisableEvents;

  FBaseTree := TBillsCacheTree.Create;
  FBaseTree.NewNodeID := 101;
  FBaseTree.SeparateChar := '.';
  FFixedIDNodes := TList.Create;
end;

procedure Tdei_CustomBills.CheckFixedIDNodes;

  function GetHintStr(ANode: TBillsCacheNode): string;
  begin
    Result := '';
    if ANode.Code <> '' then
      Result := Result + '编号：' + ANode.Code + '；';
    if ANode.Name <> '' then
      Result := Result + '名称：' + ANode.Name + '；';
  end;

  function GetInvalidModel(ANoPM: Boolean; ACount: Integer): string;
  begin
    if ANoPM then
    begin
      if ACount > 1 then
        Result := '价差功能，部分报表'
      else
        Result := '价差功能'
    end
    else
      Result := '部分报表';
    Result := Result + '将不可使用' + #13#10 + '如有疑问，请联系纵横客服，企业QQ：800003850 电话：(0756)3850888';
  end;

var
  sgs: TStrings;
  iBase: Integer;
  vBase, vImport: TBillsCacheNode;
  sHint: string;
  bNoPM: Boolean;
begin
  bNoPM := False;
  sgs := TStringList.Create;
  try
    sgs.Add('缺少如下固定清单节点：');
    for iBase := 0 to FBaseTree.FixedIDNodes.Count - 1 do
    begin
      vBase := TBillsCacheNode(FBaseTree.FixedIDNodes.Items[iBase]);

      vImport := FCacheTree.FindFixedIDNode(vBase.ID);
      if not Assigned(vImport) then
      begin
        sgs.Add(GetHintStr(vBase));
        if vBase.ID = iPriceMarginID then
          bNoPM := True;
      end;
    end;
  finally
    if sgs.Count > 1 then
    begin
      sgs.Add(GetInvalidModel(bNoPM, sgs.Count - 1));
      WarningMessage(sgs.Text);
    end;
    sgs.Free;
  end;
end;

procedure Tdei_CustomBills.EndImport;
begin
  {if FWithLevelCode then
    CheckFixedIDNodes;}

  FFixedIDNodes.Free;
  FBaseTree.Free;
  ProjectData.BillsData.EnableEvents;
  ProjectData.ReConnectTree;
  FCacheTree.Free;
  ProjectData.BillsCompileData.CalculateAll;
  CloseProgressHint;
  Screen.Cursor := crDefault;
end;

function Tdei_CustomBills.GetFixedID(ACode, AName: string): Integer;
var
  i: Integer;
  Node: TBillsCacheNode;
begin
  Result := -1;
  for i := 0 to FBaseTree.CacheNodes.Count - 1 do
  begin
    Node := TBillsCacheNode(FBaseTree.CacheNodes.Items[i]);
    if (Node.Code = ACode) and (Node.Name = AName) then
    begin
      if Node.ID < 100 then
        Result := Node.ID;
      Break;
    end;
  end;
end;

function Tdei_CustomBills.GetFixedIDNode(AID: Integer): TBillsCacheNode;
var
  i: Integer;
  Node: TBillsCacheNode;
begin
  Result := nil;
  for i := 0 to FFixedIDNodes.Count - 1 do
  begin
    Node := TBillsCacheNode(FFixedIDNodes.Items[i]);
    if (AID = Node.ID) then
    begin
      Result := Node;
      Break;
    end;
  end;
end;

procedure Tdei_CustomBills.Import;
begin
  if FWithLevelCode then
    LoadBaseTree(FBaseTree)
  else
    LoadBaseTree(FCacheTree);
  LoadColumnsFromHead(OExport.OpenWorkSheet);
  LoadNodes(OExport.OpenWorkSheet);
  WriteNodes(ProjectData.BillsData.sddBills);
end;

procedure Tdei_CustomBills.ImportFile(const AFileName: string;
  AWithLevelCode, AWithoutGclBills: Boolean);
begin
  FWithLevelCode := AWithLevelCode;
  FWithoutGclBills := AWithoutGclBills;
  ImportFile(AFileName);
end;

procedure Tdei_CustomBills.LoadBaseTree(ATree: TBillsCacheTree);
var
  BaseImportor: TBillsExcelImport;
begin
  BaseImportor := TBillsExcelImport.Create(nil);
  try
    BaseImportor.ImportToTree(ATree, GetTemplateBillsFileName);
  finally
    BaseImportor.Free;
  end;
end;

procedure Tdei_CustomBills.LoadColumnsFromHead(ASheet: TExportWorkSheet);
var
  vRow: TExportRow;
  iCol: Integer;
  sColName: string;
begin
  FCodeCol := -1;
  FB_CodeCol := -1;
  FNameCol := -1;
  FUnitsCol := -1;
  FPriceCol := -1;
  FOrgQuantityCol := -1;
  FMisQuantityCol := -1;
  FOthQuantityCol := -1;
  FDgnQuantity1Col := -1;
  FDgnQuantity2Col := -1;
  FDrawingCol := -1;
  FMemoCol := -1;
  FLevelCol := -1;

  vRow := ASheet.Rows[FCurRow];
  for iCol := 0 to vRow.Cells.Count - 1 do
  begin
    sColName := GetCellTrimStr(vRow, iCol);

    if Pos('项目节', sColName) > 0 then
      FCodeCol := iCol
    else if (sColName = '清单编号') or (Pos('子目号', sColName) > 0) then
      FB_CodeCol := iCol
    else if sColName = '名称' then
      FNameCol := iCol
    else if sColName = '单位' then
      FUnitsCol := iCol
    else if sColName = '单价' then
      FPriceCol := iCol
    else if (sColName = '清单数量') or (sColName = '工程量') or (sColName = '数量') or (sColName = '施工图数量') then
      FOrgQuantityCol := iCol
    else if (sColName = '设计错漏数量') then
      FMisQuantityCol := iCol
    else if (sColName = '其他错漏数量') then
      FOthQuantityCol := iCol
    else if (sColName = '设计数量1') or (sColName = '数量1') then
      FDgnQuantity1Col := iCol
    else if (sColName = '设计数量2') or (sColName = '数量2') then
      FDgnQuantity2Col := iCol
    else if sColName = '图号' then
      FDrawingCol := iCol
    else if sColName = '备注' then
      FMemoCol := iCol
    else if sColName = '层次编号' then
      FLevelCol := iCol;
  end;
  Inc(FCurRow);
end;

procedure Tdei_CustomBills.LoadNode(ARow: TExportRow);
var
  sLevelCode, sCode, sB_Code, sName: string;
  Node: TBillsCacheNode;
  vValue: Variant;
  iFixedID: Integer;
begin
  sLevelCode := GetCellTrimStr(ARow, FLevelCol);
  sCode := GetCellTrimStr(ARow, FCodeCol);
  sB_Code := GetCellTrimStr(ARow, FB_CodeCol);
  sName := GetCellTrimStr(ARow, FNameCol);

  // 含层次编号时，层次编号为空不导入
  // 不含层次编号时，仅导入第一部分，且项目节编号、清单编号均未空时不导入
  if FWithLevelCode then
  begin
    if sLevelCode = '' then Exit;
  end
  else
  begin
    if ((sCode = '') and (sB_Code = '')) or SameText(sCode, '2') or
        (Pos('第二部分', sName) > 0) then
    begin
      FIsFirstPart := False;
      Exit;
    end;
  end;

  if (sCode = '') and FWithoutGclBills then Exit;

  // 含层次编号时，以层次编号为依据新增节点；反之以项目节编号为依据新增节点
  if not FWithLevelCode then
  begin
    if (sCode <> '') then
      Node := FCacheTree.AddNodeByCode(sCode, -1)
    else
      Node := FCacheTree.AddLeafBillsNode(sB_Code);
  end
  else
  begin
    // 1. 从模板树中查询当前节点是否为固定ID，否则为-1
    iFixedID := GetFixedID(sCode, sName);
    // 2. 从导入树中查询是否添加过该固定ID，防止存在两个固定ID节点主键冲突
    //    如果已添加过固定ID节点，则其他节点未非固定ID节点
    Node := GetFixedIDNode(iFixedID);
    if Assigned(Node) then
      iFixedID := -1;
    // 3. 添加当前节点
    Node := FCacheTree.AddNodeByCode(sLevelCode, iFixedID);
    // 4. 如果当前添加的节点为固定ID节点，则添加到List中便于2快速查找
    if Node.ID < 100 then
      FFixedIDNodes.Add(Node);
  end;

  Node.Code := sCode;
  Node.B_Code := sB_Code;
  Node.Name := sName;
  Node.Units := GetCellTrimStr(ARow, FUnitsCol);
  Node.Price := GetCellFloat(ARow, FPriceCol);
  Node.OrgQuantity := GetCellFloat(ARow, FOrgQuantityCol);
  Node.MisQuantity := GetCellFloat(ARow, FMisQuantityCol);
  Node.OthQuantity := GetCellFloat(ARow, FOthQuantityCol);
  Node.DgnQuantity1 := GetCellFloat(ARow, FDgnQuantity1Col);
  Node.DgnQuantity2 := GetCellFloat(ARow, FDgnQuantity2Col);
  Node.DrawingCode := GetCellTrimStr(ARow, FDrawingCol);
  Node.MemoStr := GetCellTrimStr(ARow, FMemoCol);
end;

procedure Tdei_CustomBills.LoadNodes(ASheet: TExportWorkSheet);
var
  iPos, iSubPos: Integer;
begin
  while (FCurRow < ASheet.Rows.Count){ and FIsFirstPart }do
  begin
    LoadNode(ASheet.Rows[FCurRow]);
    Inc(FCurRow);
    iSubPos := (FCurRow + 1) * 100 div ASheet.Rows.Count;
    iPos := iSubPos div 2;
    UpdateProgressPosition(iPos, iSubPos);
  end;
end;

procedure Tdei_CustomBills.WriteNode(ADataSet: TsdDataSet;
  ANode: TBillsCacheNode);
var
  Rec: TBillsRecord;
begin
  if ANode.Code <> '' then
    UpdateProgressHint('写入读取的Excel数据 ' + ANode.Code)
  else if ANode.B_Code <> '' then
    UpdateProgressHint('写入读取的Excel数据 ' + ANode.B_Code)
  else
    UpdateProgressHint('写入读取的Excel数据 ' + ANode.Name);

  Rec := TBillsRecord(ADataSet.Add);
  Rec.ID.AsInteger := ANode.ID;
  Rec.ParentID.AsInteger := ANode.ParentID;
  Rec.NextSiblingID.AsInteger := ANode.NextSiblingID;
  Rec.Code.AsString := ANode.Code;
  Rec.B_Code.AsString := ANode.B_Code;
  Rec.Name.AsString := ANode.Name;
  Rec.Units.AsString := ANode.Units;
  Rec.Price.AsFloat := PriceRoundTo(ANode.Price);
  Rec.OrgQuantity.AsFloat := QuantityRoundTo(ANode.OrgQuantity);
  Rec.MisQuantity.AsFloat := QuantityRoundTo(ANode.MisQuantity);
  Rec.OthQuantity.AsFloat := QuantityRoundTo(ANode.OthQuantity);
  Rec.DgnQuantity1.AsFloat := QuantityRoundTo(ANode.DgnQuantity1);
  Rec.DgnQuantity2.AsFloat := QuantityRoundTo(ANode.DgnQuantity2);
  Rec.DrawingCode.AsString := ANode.DrawingCode;
  Rec.MemoStr.AsString := ANode.MemoStr;
end;

procedure Tdei_CustomBills.WriteNodes(ADataSet: TsdDataSet);
var
  i, iPos, iSubPos: Integer;
begin
  UpdateProgressHint('写入读取的Excel数据', True);
  UpdateProgressPosition(50, 0);
  ADataSet.DeleteAll;
  for i := 0 to FCacheTree.CacheNodes.Count - 1 do
  begin
    WriteNode(ADataSet, TBillsCacheNode(FCacheTree.CacheNodes[i]));
    iSubPos := i*100 div FCacheTree.CacheNodes.Count;
    iPos := 50 + iSubPos div 2;
    UpdateProgressPosition(iPos, iSubPos);
  end;
  UpdateProgressPosition(100, 100);
end;

end.
