unit OtherMeasurePhaseDm;

interface

uses
  CalcDecimal,
  SysUtils, Classes, sdDB, sdProvider, ADODB, OMPhaseRecord;

type
  TOtherMeasurePhaseData = class(TDataModule)
    sdpPhase: TsdADOProvider;
    sddPhase: TsdDataSet;
    sdvPhase: TsdDataView;
    procedure sdvPhaseBeforeAddRecord(ARecord: TsdDataRecord;
      var Allow: Boolean);
    procedure sdvPhaseAfterValueChanged(AValue: TsdValue);
    procedure sddPhaseGetRecordClass(var ARecordClass: TsdRecordClass);
    procedure sddPhaseAfterValueChanged(AValue: TsdValue);
    procedure sdvPhaseSetText(var Text: String; ARecord: TsdDataRecord;
      AValue: TsdValue; AColumn: TsdViewColumn; var Allow: Boolean);
    procedure sdvPhaseBeforeDeleteRecord(ARecord: TsdDataRecord;
      var Allow: Boolean);
    procedure sdvPhaseFilterRecord(ARecord: TsdDataRecord;
      var Allow: Boolean);
    procedure sdvPhaseGetText(var Text: String; ARecord: TsdDataRecord;
      AValue: TsdValue; AColumn: TsdViewColumn; DisplayText: Boolean);
  private
    FProjectData: TObject;
    FFilterDeletedRec: Boolean;

    function GetNewID: Integer;
    function GetNewSerialNo: Integer;

    procedure SaveDetailData;

    procedure BeforeBatchOperation;
    procedure AfterBatchOperation;
    procedure SetFilterDeletedRec(const Value: Boolean);
    function GetDecimal: TCalcDecimal;
  public
    constructor Create(AProjectData: TObject);
    destructor Destroy; override;

    procedure Open(AConnection: TADOConnection);
    procedure Save;

    procedure LoadCurStageData;

    procedure CalculateAll;
    procedure CalculateRecord(ARec: TOMPhaseRecord);

    property Decimal: TCalcDecimal read GetDecimal;
    property FilterDeletedRec: Boolean read FFilterDeletedRec write SetFilterDeletedRec;
  end;

implementation

uses
  ProjectData, UtilMethods, Math, DB;

{$R *.dfm}

{ TOtherMeasurePhaseData }

procedure TOtherMeasurePhaseData.AfterBatchOperation;
begin
  sddPhase.EndUpdate;
  sddPhase.EnableControls;
  sddPhase.AfterValueChanged := sddPhaseAfterValueChanged;
end;

procedure TOtherMeasurePhaseData.BeforeBatchOperation;
begin
  sddPhase.AfterValueChanged := nil;
  sddPhase.DisableControls;
  sddPhase.BeginUpdate;
end;

constructor TOtherMeasurePhaseData.Create(AProjectData: TObject);
begin
  inherited Create(nil);
  FProjectData := AProjectData;
  FFilterDeletedRec := True;
end;

destructor TOtherMeasurePhaseData.Destroy;
begin
  inherited;
end;

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

function TOtherMeasurePhaseData.GetNewSerialNo: Integer;
var
  idx: TsdIndex;
begin
  idx := sddPhase.FindIndex('idxSerial');
  if idx.RecordCount > 0 then
    Result := idx.Records[idx.RecordCount - 1].ValueByName('SerialNo').AsInteger + 1
  else
    Result := 1;
end;

procedure TOtherMeasurePhaseData.LoadCurStageData;

  procedure LoadData;
  const
    sSelectSql = 'Select * From OMPhaseDetail Where (PhaseID = %d) and (StageID = %d)';
  var
    vQuery: TADOQuery;
    i: Integer;
    Rec: TOMPhaseRecord;
  begin
    try
      with TProjectData(FProjectData) do
        vQuery := QueryData(sdpPhase.Connection, Format(sSelectSql, [PhaseIndex, StageIndex]));
      for i := 0 to sddPhase.RecordCount - 1 do
      begin
        Rec := TOMPhaseRecord(sddPhase.Records[i]);
        if vQuery.Locate('ID', Rec.ValueByName('ID').AsInteger, []) then
        begin
          Rec.PreTotalPrice.AsFloat := vQuery.FieldByName('PreTotalPrice').AsFloat;
          Rec.CurTotalPrice.AsFloat := vQuery.FieldByName('CurTotalPrice').AsFloat;
          Rec.EndTotalPrice.AsFloat := vQuery.FieldByName('EndTotalPrice').AsFloat;
        end
        else
        begin
          Rec.PreTotalPrice.AsFloat := 0;
          Rec.CurTotalPrice.AsFloat := 0;
          Rec.EndTotalPrice.AsFloat := 0;
        end;
      end;
    finally
      vQuery.Free;
    end;
  end;

begin
  BeforeBatchOperation;
  try
    LoadData;
  finally
    AfterBatchOperation;
  end;
end;

procedure TOtherMeasurePhaseData.Open(AConnection: TADOConnection);
begin
  sdpPhase.Connection := AConnection;
  sddPhase.Open;
  if not Assigned(sddPhase.FindIndex('idxID')) then
    sddPhase.AddIndex('idxID', 'ID');
  if not Assigned(sddPhase.FindIndex('idxSerial')) then
    sddPhase.AddIndex('idxSerial', 'SerialNo');
  sdvPhase.Open;
  sdvPhase.IndexName := 'idxSerial';
  sdvPhase.Filtered := True;

  LoadCurStageData;
end;

procedure TOtherMeasurePhaseData.Save;
begin
  sddPhase.Save;
  if not TProjectData(FProjectData).StageDataReadOnly then
    SaveDetailData;
end;

procedure TOtherMeasurePhaseData.SaveDetailData;
const
  sClearSql = 'Delete From OMPhaseDetail where ID not in (Select distinct ID From OtherMeasurePhase)';
  sDeleteSql = 'Delete From OMPhaseDetail where (PhaseID = %d) and (StageID = %d)';
  sInsertSql = 'Insert Into OMPhaseDetail' +
               '  Select ID, %d As PhaseID, %d As StageID, PreTotalPrice, CurTotalPrice, EndTotalPrice' +
               '    From OtherMeasurePhase Where not Deleted';
var
  iPhase, iStage: Integer;
  vQuery: TADOQuery;
begin
  if TProjectData(FProjectData).StageDataReadOnly then Exit;

  // 被删除的数据，也不应保留明细
  ExecuteSql(sdpPhase.Connection, sClearSql);

  iPhase := TProjectData(FProjectData).PhaseIndex;
  iStage := TProjectData(FProjectData).StageIndex;

  ExecuteSql(sdpPhase.Connection, Format(sDeleteSql, [iPhase, iStage]));
  ExecuteSql(sdpPhase.Connection, Format(sInsertSql, [iPhase, iStage]));

  ExecuteSql(sdpPhase.Connection, Format(sDeleteSql, [iPhase, -1]));
  ExecuteSql(sdpPhase.Connection, Format(sInsertSql, [iPhase, -1]));
end;

procedure TOtherMeasurePhaseData.sdvPhaseBeforeAddRecord(
  ARecord: TsdDataRecord; var Allow: Boolean);
begin
  ARecord.ValueByName('ID').AsInteger := GetNewID;
  ARecord.ValueByName('SerialNo').AsInteger := GetNewSerialNo;
  ARecord.ValueByName('CreatePhaseID').AsInteger := TProjectData(FProjectData).PhaseIndex;
  ARecord.ValueByName('CreateStageID').AsInteger := TProjectData(FProjectData).StageIndex;
end;

procedure TOtherMeasurePhaseData.sdvPhaseAfterValueChanged(
  AValue: TsdValue);
var
  Rec: TOMPhaseRecord;
begin
  if SameText(AValue.FieldName, 'CurTotalPrice') then
  begin
    Rec := TOMPhaseRecord(AValue.Owner);
    Rec.SetFloatValue(Rec.EndTotalPrice, TotalPriceRoundTo(Rec.CurTotalPrice.AsFloat + Rec.PreTotalPrice.AsFloat));
  end;
end;

procedure TOtherMeasurePhaseData.sddPhaseGetRecordClass(
  var ARecordClass: TsdRecordClass);
begin
  ARecordClass := TOMPhaseRecord;
end;

procedure TOtherMeasurePhaseData.sddPhaseAfterValueChanged(
  AValue: TsdValue);
var
  Rec: TOMPhaseRecord;
begin
  if SameText(AValue.FieldName, 'CurTotalPrice') then
  begin
    Rec := TOMPhaseRecord(AValue.Owner);
    Rec.SetFloatValue(Rec.EndTotalPrice, TotalPriceRoundTo(Rec.CurTotalPrice.AsFloat + Rec.PreTotalPrice.AsFloat));
    Rec.SetFloatValue(Rec.AddTotalPrice, Rec.EndTotalPrice.AsFloat);
  end;
end;

procedure TOtherMeasurePhaseData.sdvPhaseSetText(var Text: String;
  ARecord: TsdDataRecord; AValue: TsdValue; AColumn: TsdViewColumn;
  var Allow: Boolean);
begin
  if SameText(AColumn.FieldName, 'Code') then
  begin
    if (Text = '') then
    begin
      if (ARecord.ValueByName('Name').AsString = '') then
        DataSetErrorMessage(Allow, '编号和名称不可同时为空。');
      if (ARecord.ValueByName('Code').AsString = '') and (ARecord.ValueByName('Name').AsString = '') then
        sddPhase.Remove(ARecord);
    end;
  end
  else if SameText(AColumn.FieldName, 'Name') then
  begin
    if (Text = '') then
    begin
      if (ARecord.ValueByName('Code').AsString = '') then
        DataSetErrorMessage(Allow, '编号和名称不可同时为空。');
      if(ARecord.ValueByName('Code').AsString = '') and (ARecord.ValueByName('Name').AsString = '') then
        sddPhase.Remove(ARecord);
    end;
  end
  else
  begin
    if (ARecord.ValueByName('Code').AsString = '') and (ARecord.ValueByName('Name').AsString = '') then
    begin
      DataSetErrorMessage(Allow, '编号、名称不可同时为空，请先填写编号或名称，再填写其他信息。');
      sddPhase.Remove(AValue.Owner);
    end
    else if Pos('TotalPrice', AColumn.FieldName) > 0 then
      Text := FloatToStr(Decimal.TotalPrice.RoundTo(StrToFloatDef(Text, 0)));
  end;
end;

procedure TOtherMeasurePhaseData.sdvPhaseBeforeDeleteRecord(
  ARecord: TsdDataRecord; var Allow: Boolean);

  function CanDelete(ARec: TsdDataRecord): Boolean;
  begin
    Result := (ARec.ValueByName('CreatePhaseID').AsInteger = TProjectData(FProjectData).PhaseIndex) and
        (ARec.ValueByName('CreateStageID').AsInteger = TProjectData(FProjectData).StageIndex);
  end;

begin
  Allow := CanDelete(ARecord);
  if not Allow then
  begin
    ARecord.ValueByName('Deleted').AsBoolean := True;
    ARecord.ValueByName('DeletePhaseID').AsInteger := TProjectData(FProjectData).PhaseIndex;
    ARecord.ValueByName('DeleteStageID').AsInteger := TProjectData(FProjectData).StageIndex;
    sdvPhase.RefreshFilter;
  end;
end;

procedure TOtherMeasurePhaseData.sdvPhaseFilterRecord(
  ARecord: TsdDataRecord; var Allow: Boolean);
begin
  if Assigned(ARecord) then
    Allow := not ARecord.ValueByName('Deleted').AsBoolean;
end;

procedure TOtherMeasurePhaseData.SetFilterDeletedRec(const Value: Boolean);
begin
  FFilterDeletedRec := Value;
  sdvPhase.Filtered := FFilterDeletedRec;
end;

procedure TOtherMeasurePhaseData.sdvPhaseGetText(var Text: String;
  ARecord: TsdDataRecord; AValue: TsdValue; AColumn: TsdViewColumn;
  DisplayText: Boolean);

  procedure GetDisplayText;
  begin
    if AValue.DataType = ftFloat then
      if AValue.AsFloat = 0 then
        Text := '';
  end;

begin
  if DisplayText then
    GetDisplayText;
end;

function TOtherMeasurePhaseData.GetDecimal: TCalcDecimal;
begin
  Result := TProjectData(FProjectData).ProjProperties.DecimalManager.Common;
end;

procedure TOtherMeasurePhaseData.CalculateAll;
var
  i: Integer;
  Rec: TOMPhaseRecord;
begin
  for i := 0 to sddPhase.RecordCount - 1 do
  begin
    Rec := TOMPhaseRecord(sddPhase.Records[i]);
    CalculateRecord(Rec);
  end;
end;

procedure TOtherMeasurePhaseData.CalculateRecord(ARec: TOMPhaseRecord);
begin
  ARec.SetFloatValue(ARec.EndTotalPrice, Decimal.TotalPrice.RoundTo(ARec.CurTotalPrice.AsFloat + ARec.PreTotalPrice.AsFloat));
  ARec.SetFloatValue(ARec.AddTotalPrice, ARec.EndTotalPrice.AsFloat);
end;

end.
