diff --git a/packages/fcl-report/src/fpreportdata.pp b/packages/fcl-report/src/fpreportdata.pp index 140a667964..655a15c9a3 100644 --- a/packages/fcl-report/src/fpreportdata.pp +++ b/packages/fcl-report/src/fpreportdata.pp @@ -20,6 +20,10 @@ Type // Override this to return a dataset which is owned by AOwner, and configured by AConfig. // The dataset must not be opened. Function CreateDataset(AOwner : TComponent; AConfig : TJSONObject) : TDataset; virtual; abstract; + // By default, master-detail is not possible. Let this return 'True' indicate that a dataset allows master-detail. + Class Function AllowMasterDetail : Boolean; virtual; + // This is called to set the master dataset on Detail + Class Procedure SetMasterDataset(ADetail,AMaster : TDataset); virtual; // Check if the configuration is valid. Return a string that describes the error(s) // If the return is an empty string, the data designer will not close. Class Function CheckConfig(AConfig : TJSONObject) : String; virtual; @@ -36,12 +40,14 @@ Type private FConfig: TJSONObject; FDataType: String; + FMaster: String; FName: String; FReportData: TFPReportDatasetData; FRunReportDataItem: TFPReportDataItem; function GetJSONConfig: TJSONStringType; procedure SetConfig(AValue: TJSONObject); procedure SetJSONConfig(AValue: TJSONStringType); + procedure SetMaster(AValue: String); procedure SetName(AValue: String); Protected // To hold temporary references @@ -63,6 +69,7 @@ Type Published property Name : String Read FName Write SetName; Property DataType : String Read FDataType Write FDataType; + property Master : String Read FMaster Write SetMaster; Property JSONConfig : TJSONStringType Read GetJSONConfig Write SetJSONConfig; end; @@ -75,6 +82,7 @@ Type Public Function IndexOfRunData(aData : TFPReportDatasetData) : integer; Function IndexOfName(const aName : String): Integer; + Procedure CheckCircularReference(aMasterName : String; aItem : TFPReportDataDefinitionItem); Function FindDataByName(const aName : String): TFPReportDataDefinitionItem; Function AddData(const aName : String) : TFPReportDataDefinitionItem; Procedure SaveToJSON(O : TJSONObject); @@ -148,7 +156,10 @@ Resourcestring SErrInvalidDataType = 'Invalid data type: "%s"'; SErrInvalidJSONConfig = '%s: Invalid JSON Configuration'; SErrUnknownDataType = 'Unknown report data type: %s'; - + SErrNoMasterDetailSupport = 'No master-detail support for class "%s"'; + SErrOpeningDataset = 'Error opening data "%s" : Exception %s with message %s'; + SErrInvalidDatasourceName = 'Invalid data source name : "%s"'; + SErrCircularReference = 'Invalid master data source "%s", circular references not allowed.'; implementation @@ -333,6 +344,7 @@ Var I : Integer; begin + aReport.SaveDataToNames; For I:=0 to DataDefinitions.Count-1 do begin DD:=DataDefinitions[i]; @@ -364,16 +376,17 @@ procedure TFPCustomReportDataManager.ApplyToReport(aReport : TFPReport; Errors: Var I : Integer; - DesignD : TFPReportDataDefinitionItem; + MasterD,DesignD : TFPReportDataDefinitionItem; DatasetD : TFPReportDatasetData; + H : TFPReportDataHandler; L : TFPList; P : TComponent; + DDS,MDS : TDataset; begin - RemoveFromReport(aReport); P:=GetDatasetParent; - aReport.SaveDataToNames; aReport.ReportData.Clear; + // Create all datasets For I:=0 to DataDefinitions.Count-1 do begin DesignD:=DataDefinitions[i]; @@ -386,7 +399,7 @@ begin except On E : Exception do If Assigned(Errors) then - Errors.Add(Format('Error opening data "%s" : Exception %s with message %s',[DesignD.Name,E.ClassName,E.Message])) + Errors.Add(Format(SErrOpeningDataset, [DesignD.Name, E.ClassName, E.Message])) else Raise; end; @@ -395,6 +408,19 @@ begin DatasetD.StartDesigning; // set designing flag, or OI will not show reference to it. DesignD.RunReportDataItem:=aReport.ReportData.AddReportData(DatasetD); end; + // Set master-detail relations + For I:=0 to DataDefinitions.Count-1 do + begin + DesignD:=DataDefinitions[i]; + if (DesignD.Master<>'') then + begin + H:=TFPCustomReportDataManager.GetTypeHandler(DesignD.DataType); + MasterD:=DataDefinitions.FindDataByName(DesignD.Master); + DDS:=(DesignD.RunReportDataItem.Data as TFPReportDatasetData).DataSet; + MDS:=(MasterD.RunReportDataItem.Data as TFPReportDatasetData).DataSet; + H.SetMasterDataset(DDS,MDS); + end; + end; end; class function TFPCustomReportDataManager.GetRegisteredTypes(AList: Tstrings): Integer; @@ -486,6 +512,24 @@ begin Dec(Result); end; +procedure TFPReportDataDefinitions.CheckCircularReference(aMasterName: String; aItem: TFPReportDataDefinitionItem); + +Var + DD : TFPReportDataDefinitionItem; + +begin + While (aMasterName<>'') do + begin + DD:=FindDataByName(aMasterName); + if (DD=Nil) then + raise EReportDataError.CreateFmt(SErrInvalidDatasourceName, [aMasterName]); + If (DD=aItem) then + raise EReportDataError.CreateFmt(SErrCircularReference, [aMasterName]); + aMasterName:=DD.Master; + end; + +end; + function TFPReportDataDefinitions.FindDataByName(const aName: String): TFPReportDataDefinitionItem; var @@ -503,7 +547,7 @@ function TFPReportDataDefinitions.AddData(const aName: String): TFPReportDataDef begin if (IndexOfName(aName)<>-1) then - raise EReportError.CreateFmt(SErrDuplicateData, [aName]); + raise EReportDataError.CreateFmt(SErrDuplicateData, [aName]); Result:=add as TFPReportDataDefinitionItem; Result.Name:=aName; end; @@ -562,6 +606,15 @@ begin TFPCustomReportDataManager.RegisterConfigFrameClass(DataType,aClass); end; +class function TFPReportDataHandler.AllowMasterDetail: Boolean; +begin + Result:=False; +end; + +Class procedure TFPReportDataHandler.SetMasterDataset(ADetail, AMaster: TDataset); +begin + Raise EReportDataError.CreateFmt(SErrNoMasterDetailSupport,[ADetail.ClassName]); +end; class function TFPReportDataHandler.CheckConfig(AConfig: TJSONObject): String; @@ -612,6 +665,12 @@ begin end; end; +procedure TFPReportDataDefinitionItem.SetMaster(AValue: String); +begin + if FMaster=AValue then Exit; + FMaster:=AValue; +end; + procedure TFPReportDataDefinitionItem.SetName(AValue: String); begin if FName=AValue then Exit; @@ -638,6 +697,7 @@ begin Config:=D.Config; Name:=D.Name; DataType:=D.DataType; + Master:=D.Master; end else inherited Assign(Source); @@ -648,6 +708,7 @@ begin O.Add('name',Name); O.Add('type',DataType); O.Add('config',Config.Clone); + O.Add('master',Master); end; procedure TFPReportDataDefinitionItem.LoadFromJSON(O: TJSONObject); @@ -661,6 +722,7 @@ begin C:=O.Get('config',TJSONObject(Nil)); if Assigned(C) then Config:=C; + Master:=O.Get('master',''); end; function TFPReportDataDefinitionItem.Clone(aNewName: String): TFPReportDataDefinitionItem; diff --git a/packages/fcl-report/src/fpreportdatasqldb.pp b/packages/fcl-report/src/fpreportdatasqldb.pp index 0c9d879d3f..4affcec7b7 100644 --- a/packages/fcl-report/src/fpreportdatasqldb.pp +++ b/packages/fcl-report/src/fpreportdatasqldb.pp @@ -68,11 +68,15 @@ Type end; + { TSQLDBReportDataHandler } + TSQLDBReportDataHandler = Class(TFPReportDataHandler) Function CreateDataset(AOwner : TComponent; AConfig : TJSONObject) : TDataset; override; Class Function CheckConfig(AConfig: TJSONObject): String; override; Class Function DataType : String; override; Class Function DataTypeDescription : String; override; + Class Function AllowMasterDetail: Boolean; override; + Class Procedure SetMasterDataset(ADetail, AMaster: TDataset); override; end; @@ -262,6 +266,28 @@ begin Result:='SQL Database server'; end; +class function TSQLDBReportDataHandler.AllowMasterDetail: Boolean; +begin + Result:=True; +end; + +class procedure TSQLDBReportDataHandler.SetMasterDataset(ADetail, AMaster: TDataset); + +Var + Q : TSQLQuery; + DS : TDatasource; + +begin + Q:=(ADetail as TSQLQuery); + DS:=Q.DataSource; + if DS=Nil then + begin + DS:=TDatasource.Create(Q); + Q.Datasource:=DS; + end; + DS.Dataset:=AMaster; +end; + initialization TSQLDBReportDataHandler.RegisterHandler; TFPReportConnector.Init;